1. 程式人生 > >Mybatis最入門---Mapper檔案配置詳解(下)

Mybatis最入門---Mapper檔案配置詳解(下)

[一步是咫尺,一步即天涯]

上文我們詳細解釋了HelloWorld工程中的配置項,本文,我們再來介紹一些更加基礎,靈活的配置項。本文,我們先不演示具體的工程,後續的文章中會按照實際應用來配置相關的引數。敬請期待!

--------------------------------------------------------------------------------------------------------------------------------------------------------

1.Setting配置:這是Mybatis中極為重要的配置項,他們的屬性值能夠改變Mybatis的執行狀態,請小心配置!具體內容如下:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

解釋如下:

引數名稱 功能描述 有效配置 預設值
cacheEnabled 該配置影響的所有對映器中配置的快取的全域性開關。 true | false true
lazyLoadingEnabled 延遲載入的全域性開關。當開啟時,所有關聯物件都會延遲載入。 特定關聯關係中可通過設定fetchType屬性來覆蓋該項的開關狀態。 true | false true
multipleResultSetsEnabled 是否允許單一語句返回多結果集(需要相容驅動)。 true | false true
useColumnLabel 使用列標籤代替列名。不同的驅動在這方面會有不同的表現, 具體可參考相關驅動文件或通過測試這兩種不同的模式來觀察所用驅動的結果。
true | false true
useGeneratedKeys 允許 JDBC 支援自動生成主鍵,需要驅動相容。 如果設定為 true 則這個設定強制使用自動生成主鍵,儘管一些驅動不能相容但仍可正常工作(比如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 應如何自動對映列到欄位或屬性。 NONE 表示取消自動對映;PARTIAL 只會自動對映沒有定義巢狀結果集對映的結果集。 FULL 會自動對映任意複雜的結果集(無論是否巢狀)。 NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target.
  • NONE: Do nothing
  • WARNING: Output warning log (The log level of'org.apache.ibatis.session.AutoMappingUnknownColumnBehavior'must be set to WARN)
  • FAILING: Fail mapping (Throw SqlSessionException)
NONE, WARNING, FAILING NONE
defaultExecutorType 配置預設的執行器。SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理語句(prepared statements); BATCH 執行器將重用語句並執行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 設定超時時間,它決定驅動等待資料庫響應的秒數。 Any positive integer Not Set (null)
defaultFetchSize Sets the driver a hint as to control fetching size for return results. This parameter value can be override by a query setting. Any positive integer Not Set (null)
safeRowBoundsEnabled 允許在巢狀語句中使用分頁(RowBounds)。 true | false False
mapUnderscoreToCamelCase 是否開啟自動駝峰命名規則(camel case)對映,即從經典資料庫列名 A_COLUMN 到經典 Java 屬性名 aColumn 的類似對映。 true | false False
localCacheScope MyBatis 利用本地快取機制(Local Cache)防止迴圈引用(circular references)和加速重複巢狀查詢。 預設值為 SESSION,這種情況下會快取一個會話中執行的所有查詢。 若設定值為 STATEMENT,本地會話僅用在語句執行上,對相同 SqlSession 的不同調用將不會共享資料。 JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER OTHER
jdbcTypeForNull 當沒有為引數提供特定的 JDBC 型別時,為空值指定 JDBC 型別。 某些驅動需要指定列的 JDBC 型別,多數情況直接用一般型別即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER OTHER
lazyLoadTriggerMethods 指定哪個物件的方法觸發一次延遲載入。 A method name list separated by commas equals,clone,hashCode,toString

2.typeHandlers配置

無論是 MyBatis 在預處理語句(PreparedStatement)中設定一個引數時,還是從結果集中取出一個值時, 都會用型別處理器將獲取的值以合適的方式轉換成 Java 型別。下表描述了一些預設的型別處理器。

型別處理器 Java 型別 JDBC 型別
BooleanTypeHandler java.lang.Booleanboolean 資料庫相容的 BOOLEAN
ByteTypeHandler java.lang.Bytebyte 資料庫相容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Shortshort 資料庫相容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandler java.lang.Integerint 資料庫相容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Longlong 資料庫相容的 NUMERIC 或 LONG INTEGER
FloatTypeHandler java.lang.Floatfloat 資料庫相容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Doubledouble 資料庫相容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 資料庫相容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHARVARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOBLONGVARCHAR
NStringTypeHandler java.lang.String NVARCHARNCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 資料庫相容的位元組流型別
BlobTypeHandler byte[] BLOBLONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
ObjectTypeHandler Any OTHER 或未指定型別
EnumTypeHandler Enumeration Type VARCHAR-任何相容的字串型別,儲存列舉的名稱(而不是索引)
EnumOrdinalTypeHandler Enumeration Type 任何相容的 NUMERIC 或 DOUBLE 型別,儲存列舉的索引(而不是名稱)。
我們可以重寫型別處理器或建立自己的型別處理器來處理不支援的或非標準的型別。 具體做法為:實現 org.apache.ibatis.type.TypeHandler 介面, 或繼承一個很便利的類 org.apache.ibatis.type.BaseTypeHandler, 然後可以選擇性地將它對映到一個 JDBC 型別。比如:
// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return cs.getString(columnIndex);
  }
}
<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>

使用這個的型別處理器將會覆蓋已經存在的處理 Java 的 String 型別屬性和 VARCHAR 引數及結果的型別處理器。 要注意 MyBatis 不會窺探資料庫元資訊來決定使用哪種型別,所以你必須在引數和結果對映中指明那是 VARCHAR 型別的欄位, 以使其能夠繫結到正確的型別處理器上。 這是因為:MyBatis 直到語句被執行才清楚資料型別。

通過型別處理器的泛型,MyBatis 可以得知該型別處理器處理的 Java 型別,不過這種行為可以通過兩種方法改變:

  • 在型別處理器的配置元素(typeHandler element)上增加一個 javaType 屬性(比如:javaType="String");
  • 在型別處理器的類上(TypeHandler class)增加一個 @MappedTypes 註解來指定與其關聯的 Java 型別列表。 如果在 javaType 屬性中也同時指定,則註解方式將被忽略。

可以通過兩種方式來指定被關聯的 JDBC 型別:

  • 在型別處理器的配置元素上增加一個 jdbcType 屬性(比如:jdbcType="VARCHAR");
  • 在型別處理器的類上(TypeHandler class)增加一個 @MappedJdbcTypes 註解來指定與其關聯的 JDBC 型別列表。 如果在 jdbcType 屬性中也同時指定,則註解方式將被忽略。
最後,可以讓 MyBatis 為你查詢型別處理器:
<typeHandlers>
  <package name="org.mybatis.example"/>
</typeHandlers>

注意在使用自動檢索(autodiscovery)功能的時候,只能通過註解方式來指定 JDBC 的型別。

你能建立一個泛型型別處理器,它可以處理多於一個類。為達到此目的, 需要增加一個接收該類作為引數的構造器,這樣在構造一個型別處理器的時候 MyBatis 就會傳入一個具體的類。

//GenericTypeHandler.java
public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {

  private Class<E> type;

  public GenericTypeHandler(Class<E> type) {
    if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
    this.type = type;
  }
  ...
EnumTypeHandler 和 EnumOrdinalTypeHandler 都是泛型型別處理器(generic TypeHandlers), 我們將會在接下來的部分詳細探討。

3.列舉型別處理

若想對映列舉型別 Enum,則需要從 EnumTypeHandler 或者 EnumOrdinalTypeHandler 中選一個來使用。

比如說我們想儲存取近似值時用到的舍入模式。預設情況下,MyBatis 會利用 EnumTypeHandler 來把 Enum 值轉換成對應的名字。

注意 EnumTypeHandler 在某種意義上來說是比較特別的,其他的處理器只針對某個特定的類,而它不同,它會處理任意繼承了 Enum 的類。

不過,我們可能不想儲存名字,相反我們的 DBA 會堅持使用整形值程式碼。那也一樣輕而易舉: 在配置檔案中把 EnumOrdinalTypeHandler 加到 typeHandlers 中即可, 這樣每個 RoundingMode 將通過他們的序數值來對映成對應的整形。

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>

但是怎樣能將同樣的 Enum 既對映成字串又對映成整形呢?

自動對映器(auto-mapper)會自動地選用 EnumOrdinalTypeHandler 來處理, 所以如果我們想用普通的 EnumTypeHandler,就非要為那些 SQL 語句顯式地設定要用到的型別處理器不可。

這裡我們先展示一份mapper檔案,後面我們在進行詳細的解釋。
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.apache.ibatis.submitted.rounding.Mapper">
	<resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
		<result column="funkyNumber" property="funkyNumber"/>
		<result column="roundingMode" property="roundingMode"/>
	</resultMap>

	<select id="getUser" resultMap="usermap">
		select * from users
	</select>
	<insert id="insert">
	    insert into users (id, name, funkyNumber, roundingMode) values (
	    	#{id}, #{name}, #{funkyNumber}, #{roundingMode}
	    )
	</insert>
	
	<resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap2">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
		<result column="funkyNumber" property="funkyNumber"/>
		<result column="roundingMode" property="roundingMode" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
	</resultMap>
	<select id="getUser2" resultMap="usermap2">
		select * from users2
	</select>
	<insert id="insert2">
	    insert into users2 (id, name, funkyNumber, roundingMode) values (
	    	#{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler}
	    )
	</insert>

</mapper>

4.物件工廠

MyBatis 每次建立結果物件的新例項時,它都會使用一個物件工廠(ObjectFactory)例項來完成。 預設的物件工廠需要做的僅僅是例項化目標類,要麼通過預設構造方法,要麼在引數對映存在的時候通過引數構造方法來例項化。 如果想覆蓋物件工廠的預設行為,則可以通過建立自己的物件工廠來實現。比如:

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
  public Object create(Class type) {
    return super.create(type);
  }
  public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }}

<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>

ObjectFactory 介面很簡單,它包含兩個建立用的方法,一個是處理預設構造方法的,另外一個是處理帶引數的構造方法的。 最後,setProperties 方法可以被用來配置 ObjectFactory,在初始化你的 ObjectFactory 例項後, objectFactory 元素體中定義的屬性會被傳遞給 setProperties 方法。

5.外掛配置

MyBatis 允許你在已對映語句執行過程中的某一點進行攔截呼叫。預設情況下,MyBatis 允許使用外掛來攔截的方法呼叫包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

這些類中方法的細節可以通過檢視每個方法的簽名來發現,或者直接檢視 MyBatis 的發行包中的原始碼。 假設你想做的不僅僅是監控方法的呼叫,那麼你應該很好的瞭解正在重寫的方法的行為。 因為如果在試圖修改或重寫已有方法的行為的時候,你很可能在破壞 MyBatis 的核心模組。 這些都是更低層的類和方法,所以使用外掛的時候要特別當心。

通過 MyBatis 提供的強大機制,使用外掛是非常簡單的,只需實現 Interceptor 介面,並指定了想要攔截的方法簽名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的外掛將會攔截在 Executor 例項中所有的 “update” 方法呼叫, 這裡的 Executor 是負責執行低層對映語句的內部物件。

注意:覆蓋配置類

除了用外掛來修改 MyBatis 核心行為之外,還可以通過完全覆蓋配置類來達到目的。只需繼承後覆蓋其中的每個方法,再把它傳遞到 sqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,這可能會嚴重影響 MyBatis 的行為,務請慎之又慎。

------------------------------------------------------------------------------------------------------------------------------------

至此,Mybatis最入門---配置詳解(下)結束

參考文件:

官方文件:http://www.mybatis.org/mybatis-3/