1. 程式人生 > 實用技巧 >讀MyBatis官方文件--配置

讀MyBatis官方文件--配置

Configuration

MyBatis的配置包括設定和屬性,它們會動態影響MyBatis的行為,下面是高級別(應用級)的文件

properties

外部的、容易替換的屬性可以配置在Java的properties檔案中,或者放在properties元素的子元素中,例如

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

這裡定義的屬性可以被應用到MyBatis的配置檔案中,例如

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

這裡username和password會被properties

元素內的值替代,而driver和url會被config.properties中的值替代

屬性也可以直接傳入SqlSessionFactoryBuilder.build()方法中,例如:

SqlSessionFactory factory =
  sqlSessionFactoryBuilder.build(reader, props);

// ... or ...

SqlSessionFactory factory =
  new SqlSessionFactoryBuilder.build(reader, environment, props);

如果屬性存在於多個地方,MyBatis會按一下順序來載入:

  1. 定義在properties元素標籤下的值
  2. 從classpath中載入的資源或者properties元素中的url元素指定路徑的資源,並且覆蓋已經指定的屬性值
  3. 作為方法引數傳遞的屬性,覆蓋之前已經指定的屬性值

優先順序從下往上(最高為方法引數,最低為xml檔案中指定的引數)

MyBatis 3.4.2開始,可以指定預設的屬性

<dataSource type="POOLED">
  <!-- ... -->
  <property name="username" value="${username:ut_user}"/> <!-- If 'username' property not present, username become 'ut_user' -->
</dataSource>

這個特性預設是關閉的,如果需要指定預設屬性,那麼需要進行如下配置:

<properties resource="org/mybatis/example/config.properties">
  <!-- ... -->
  <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- Enable this feature -->
</properties>

注意:這個設定中的“:”會和屬性的key(db:username)或者SQL定義中OGNL表示式(${tableName != null ? tableName : 'global_constants'})衝突。如果這兩者都用到了,那麼需要修改預設值的分隔符

<properties resource="org/mybatis/example/config.properties">
  <!-- ... -->
  <property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- Change default value of separator -->
</properties>
<dataSource type="POOLED">
  <!-- ... -->
  <property name="username" value="${db:username?:ut_user}"/>
</dataSource>

settings

設定是在執行時對MyBatis的行為做出調整

設定 描述 有效值 預設值
cacheEnabled 在本配置中全域性開啟或禁用任何配置在mapper上的快取 true/false true
lazyLoadingEnabled 全域性開啟或禁用懶載入。開啟時,所有的relation會被懶載入,這個值可以通過使用fetchType屬性來對某個relation無效 true/false false
aggressiveLazyLoading 開啟時,任何方法呼叫都會載入這個物件的所有的lazy property。否則,按需要載入屬性 true/false false(v3.4.1之前是true)
multipleResultSetsEnabled 允許或不允許從單條語句返回多個ResultSet(需要相容此特性的驅動) true/false true
useColumnLabel 使用列label而不是列名。不同的驅動在這條上的表現不同。要參考驅動文件,或者測試兩種模式 true/false true
useGeneratedKeys 允許JDBC支援generated key。需要相容此特性的驅動。如果設為true,這個設定強制使用generated key,一些不相容的驅動也會生效(Derby) true/false false
autoMappingBehavior 指定MyBatis是否和怎樣自動將列對映到欄位/屬性上。NONE禁用auto-mapping,PARTIAL只會auto-map沒有巢狀result的result,FULL會auto-map任何複雜的result NONE/PARTIAL/FULL PARTIAL
autoMappingUnknownColumnBehavior 指定當檢測到一個自動對映目標的未知列/位置屬性時的行為,NONE什麼都不做,WARNING:輸出warning日誌('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior'的日誌等級要被設定為WARN),FAILING丟擲異常(SqlSessionException) NONE/WARNING/FAILING NONE
defaultExecutorType 配置預設的executor,SIMPLE不做任何特殊的事情,REUSE會複用prepared statement,BATCH會重用statements和批量更新(batches updates) SIMPLE/REUSE/BATCH SIMPLE
defaultStatementTimeout 設定driver等待資料庫響應的秒數 任何正整數 沒有設定(null)
defaultFetchSize 給driver設定一個hint,來控制result的fetch size。這個引數值會被查詢這是覆蓋 任何正整數 沒有設定(null)
defaultResultSetType 指定一個滾動策略(scroll strategy),當每個語句設定忽略這個設定時應用此設定(3.5.2以後) FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE/DEFAULT(等同於沒有設定) 沒有設定(null)
safeRowBoundsEnabled 在巢狀語句內允許使用RowBounds,如果允許,設為false ture/false false
safeResultHandlerEnabled 在巢狀語句中允許使用ResultHandler,如果允許,設為false true/false true
mapUnderscoreToCamelCase 允許從傳統資料庫列名(A_COLLUMN)到Java的屬性名(aColumn)的自動對映 true/false false
localCacheScope MyBatis使用local cache來禁止迴圈引用並且加速重複的巢狀查詢。預設(SESSION)一個session中的所有查詢都會被快取。如果設為STATEMENT,local session只會在執行語句時使用,在同一個SqlSession中的不同調用中是沒有共享資料的 SESSION/STATEMENT SESSION
jdbcTypeForNull 當沒有指定的JDBC型別提供給此引數,對null值指定JDBC型別。一些驅動需要指定列的JDBC型別但是其它的一些會使用NULL VARCHAR或者其它 JdbcType列舉。比較通用的是NULL VARCHAR OTHER OTHER
lazyLoadTriggerMethods 指定物件的哪個方法會觸發懶載入 使用逗號分隔的方法名列表 equals,clone,hashCode,toString
defaultScriptingLanguage 指定動態SQL生成使用的預設語言 全類名或類型別名 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler 指定預設處理Enum的TypeHandler(v3.4.5以後) 全類名或類型別名 org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls 指定當一個查詢到的值是null時,是否呼叫setter或map的put方法。當你要使用Map.keySet()或者需要null值初始化,這個引數是有用的。基本資料型別(int,boolean...)不會被設為null true/false false
returnInstanceForEmptyRow MyBatis在返回行的所有列都為null時,會返回null。當這個設定被啟用時。MyBatis會返回一個空例項。這個設定也應用於巢狀的結果(v3.4.2以後) true/false false
logPrefix 指定MyBatis在logger名字前加什麼字首 任何字串 沒有設定
logImpl 指定MyBatis使用什麼日誌實現。如果沒有設定,會自動尋找 SLF4J/LOG4J/LOG4J2/JDK_LOGGING/COMMONS_LOGGING/STDOUT_LOGGING/NO_LOGGING 沒有設定
proxyFactory 指定MyBatis用來建立支援懶載入物件的協議工具 CGLIB/JAVASSIST JAVASSIST(v3.3及以上)
vfsImpl 指定VFS的實現 全類名或者自定義的VFS實現,通過逗號分隔 沒有設定
useActualParamName 允許被宣告在方法簽名的實際名字來引用(referencing)語句引數。要使用這個特性,你的專案必須使用Java8(-parameters)編譯(3.4.1) true/false true
configurationFactory 提供一個類來提供Configuration的例項,返回的例項會用來載入反序列化物件之後的lazy properties。這個類必須包含一個'static Configuration getConfiguration()'簽名的靜態方法(v3.2.3以上) 類型別名或全類名 沒有設定
shrinkWhitespacesInSql 從SQL中去除額外的空白字元。這也會影響到SQL中的字面值(v3.5.5以上) true/false false
defaultSqlProviderType 指定一個sql provider class,提供provider方法(v3.5.6以上)。當這個屬性被忽略時,這個類會將型別(值)屬性應用到sql provider註解(@SelectProvider)上 一個類型別名或全類名 沒有設定

一個配置設定的例子:

<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>

typeAliases

類型別名時Java型別的縮寫名字。它只和XML配置相關,並且只是為了減少全類名的冗餘字元

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

有了上述設定之後,Blog可以替換任何需要domain.blog.Blog的地方。或者設定一個MyBatis用來查詢bean的包名

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

在domain.blog中找到的每個bean,如果沒有註解,會使用無大寫的非全類名方式註冊,即domain.blog.Author會被註冊為author。如果@Alias註解在某個類上出現,那麼會使用註解的值

@Alias("author")
public class Author {
    ...
}

對於常見的Java型別,都有對應的內建類型別名,它們時大小寫敏感的,在處理名字時要注意基本資料型別

別名 對映型別
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

typeHandlers

每當MyBatis在PreparedStatement上設定一個引數或者從ResultSet中獲取一個值時,TypeHandler會被用來調整這個值成為合適的Java型別,下表描述了各型別的預設TypeHandler

Type Handler Java Types JDBC Types
BooleanTypeHandler java.lang.Boolean, boolean Any compatible BOOLEAN
ByteTypeHandler java.lang.Byte, byte Any compatible NUMERIC or BYTE
ShortTypeHandler java.lang.Short, short Any compatible NUMERIC or SMALLINT
IntegerTypeHandler java.lang.Integer, int Any compatible NUMERIC or INTEGER
LongTypeHandler java.lang.Long, long Any compatible NUMERIC or BIGINT
FloatTypeHandler java.lang.Float, float Any compatible NUMERIC or FLOAT
DoubleTypeHandler java.lang.Double, double Any compatible NUMERIC or DOUBLE
BigDecimalTypeHandler java.math.BigDecimal Any compatible NUMERIC or DECIMAL
StringTypeHandler java.lang.String CHAR, VARCHAR
ClobReaderTypeHandler java.io.Reader CLOB, LONGVARCHAR
NStringTypeHandler java.lang.String NVARCHAR, NCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] Any compatible byte stream type
BlobTypeHandler byte[] BLOB, LONGVARBINARY
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, or unspecified
EnumTypeHandler Enumeration Type VARCHAR any string compatible type, as the code is stored (not index)
EnumOrdinalTypeHandler Enumeration Type Any compatible NUMERIC or DOUBLE, as the position is stored (not the code itself).
SqlxmlTypeHandler java.lang.String SQLXML
InstantTypeHandler java.time.Instant TIMESTAMP
LocalDateTimeTypeHandler java.time.LocalDateTime TIMESTAMP
LocalDateTypeHandler java.time.LocalDate DATE
LocalTimeTypeHandler java.time.LocalTime TIME
OffsetDateTimeTypeHandler java.time.OffsetDateTime TIMESTAMP
OffsetTimeTypeHandler java.time.OffsetTime TIME
ZonedDateTimeTypeHandler java.time.ZonedDateTime TIMESTAMP
YearTypeHandler java.time.Year INTEGER
MonthTypeHandler java.time.Month INTEGER
YearMonthTypeHandler java.time.YearMonth VARCHAR or LONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

允許你覆蓋type handler,或者建立自己的typehandler來處理不支援的型別。要這樣做,實現org.apache.ibatis.type.TypeHandler介面,或者繼承org.apache.ibatis.type.BaseTypeHandler類,來將某個型別對映到JDBC型別

@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>

使用這個TypeHandler會覆蓋對於Java的String和結果集中的VARCHAR引數和結果使用的handler。MyBatis不會根據資料庫的元資料來決定型別,所以你必須指定在引數部分是VARCHAR欄位,並且結果對映時要應用正確的type handler。這樣做的原因是MyBatis在語句執行前是不知道資料的型別

MyBatis可以通過檢視它的泛型型別知道你想使用這個type handler來處理的Java型別,可以通過兩種方式來覆蓋這個行為

  1. 在 typeHandler元素中新增一個javaType屬性(javaType="String")
  2. 在TypeHandler類上新增一個@MappedTypes註解,來指定和它關聯的所有java型別。這個註解如果javaType被指定,則會被忽略

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

  1. 在typeHandler元素中指定jdbcType屬性(jdbcType="VARCHAR")
  2. 在TypeHandler類上新增@MappedJdbcTypes註解,指定其關聯的所有JDBC型別。如果jdbcType被指定,那麼這個註解會被忽略

當決定在ResultMap中使用哪個TypeHandler時,Java型別是一直的(結果型別得知),但是JDBC型別是未知的。MyBatis使用 javaType=[TheJavaType] jdbcType=null來選擇一個TypeHandler。這意味著使用@MappedJdbcTypes註解限制了TypeHandler的範圍,並且讓它除了在結果集為空時都不可用。為了讓TypeHandler可以使用,在註解中設定includeNullJdbcType=true。v3.4.0之後,如果一個TypeHander被註冊來處理Java型別,在沒有這個設定的前提下,它也會被預設用來在ResultMaps中處理java型別

可以讓MyBatis尋找自定義的TypeHandler:

<!-- mybatis-config.xml -->
<typeHandlers>
  <package name="org.mybatis.example"/>
</typeHandlers>

注意:當使用自動發現特性時,JDBC型別只能通過註解來指定

可以建立泛型的TypeHandler,它可以處理一個以上的類。為了實現這個目的,新增一個構造器(接收類型別作為引數),在建立TypeHandler時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都是泛型的TypeHandler,之後會進行學習

Handling Enums

如果對映一個列舉,需要EnumTypeHandler或者EnumOrdinalTypeHandler

例如,我們要儲存一種進位方式,對於一些數字來決定如何進位。預設,MyBatis使用EnumTypeHandler來將Enum值轉為它們的名字

注意:EnumTypeHandler不是處理某個特殊的類,而是所有繼承了Enum類的類。然而,我們不一定想儲存名字。可能DBA要求儲存數字。要這樣做,在配置檔案中的typeHandlers新增EnumOrdinalTypeHandler,現在每個RoundingMode會對映成它們各自儲存的整數值

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

如果想將同一個列舉在一個地方對映成字串而在另一個地方對映成數字呢?auto-mapper會自動使用EnumOrdinalTypeHandler,所以如果我們想使用EnumTypeHandler,就需要顯式指定在SQL語句中使用的handler

<!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>

注意:這會強制我們在select語句中使用resultMap而不是resultType
Note that this forces us to use a resultMap instead of a resultType in our select statements.

objectFactory

每次MyBatis建立一個結果物件的新例項時,它使用ObjectFactory的例項來建立。預設的ObjectFactory只是使用預設的構造器來例項化目標類,如果parameter mapping存在則使用有參構造器。如果你想覆蓋這個類的行為,可以建立自己的

public class ExampleObjectFactory extends DefaultObjectFactory {
  @Override
  public <T> T create(Class<T> type) {
    return super.create(type);
  }

  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }

  @Override
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }

  @Override
  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介面很簡單,包含兩個create方法,一個來處理預設構造器,另一個處理有參構造器。最後setProperties`方法可以用來配置ObjectFactory,在objectFactory元素(config檔案)中定義的屬性在例項化ObjectFactory例項之後會傳入(呼叫)該方法

plugins

MyBatis允許你在一個mapped statement執行過程中在某個點攔截呼叫。預設,MyBatis允許外掛攔截如下方法呼叫

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

如果你想做處理監控呼叫以外的事情,你應該理解你正在覆寫的方法的行為。如果你嘗試修改或者覆寫這些方法,那麼可能會破壞MyBatis的核心。這些是底級類和方法。要小心使用

使用外掛很簡單,只要實現Interceptor介面,確定要攔截的方法簽名

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre-processing if needed
    Object returnObject = invocation.proceed();
    // implement post-processing if needed
    return returnObject;
  }

  @Override
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

這個外掛會攔截在Executor(負責底層的mapped statement執行)例項上所有的“update”方法的呼叫

注意:覆寫Configuration類

除了通過plugins來修改MyBatis的核心行為,還能直接覆寫Configuration類。只需要繼承它並且複習內部的任何方法,然後將其傳入SqlSessionFactoryBuilder.build(myConfig),這可能對MyBatis的行為有很嚴重的影響

environments

MyBatis可以配置多個環境。這使得將SQL對映到多種資料庫。例如,測試環境和生產環境。或者共享同一個模式的多種生產資料庫,並且向將同樣的SQL對映到所有生產資料庫

注意:在配置多個環境時,每個環境只有一個SqlSessionFactory例項。如果想連線兩個資料庫,需要建立兩個SqlSessionFactory的例項。對於三個資料庫,就需要三個例項,以此類推

要指定在哪個環境來build,將其作為引數傳入即可

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
// 如果不指定則使用預設環境
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);
<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

核心內容:
預設的環境id(default="development")
每個環境的id(id="development")
事務的配置(type="JDBC")
資料來源配置(type="POOLED")

預設環境和環境id是自解釋的,可以隨便命名,只要確認default值和它匹配

transactionManager

MyBatis有兩種TransactionManager型別,包括 JDBC 和 MANAGED

JDBC - 直接使用JDBC的commit和rollback。它依賴從資料來源獲取的連線來管理事務範圍
MANAGED - 什麼都不做。從不commit或者roll back。它讓容器來管理事務的生命週期(JEE 應用伺服器上下文)。預設,它會關閉此連線。然而,一些容器不希望這樣,因此如果你需要通過關閉連線來停止,將closeConnection屬性設為false

<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>

注意:如果結合Spring使用,那麼不需要配置任何TransactionManager,因為Spring模組會設定它自己的,來覆蓋MyBatis的配置

這兩個型別不需要任何屬性。然而,它們都是類型別名,所有除了使用它們,還可以通過全類名或定義型別縮寫來使用自己實現的TransactionManager

// 通過實現這個介面
public interface TransactionFactory {
  default void setProperties(Properties props) { // Since 3.5.2, change to default method
    // NOP
  }
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

任何配置在xml中的屬性都會在例項化之後傳入該例項的setProperties()方法。你的實現也需要建立一個Transaction的實現,這也很簡單

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}

通過這兩個介面可以自己去定義事務的處理方法

dataSource

dataSource元素使用標準的JDBC DataSource介面來配置JDBC連線物件。很多MyBatis應用都配置了dataSource,然而並不需要。要實現懶載入,需要dataSource

有三個內建的dataSource型別

  1. UNPOOLED - 每次請求時開啟/關閉連線。對效能不敏感的程式可以使用這種方式,它的配置項
    • driver - JDBC驅動的全類名
    • url - JDBC URL
    • username - 使用者名稱
    • password - 密碼
    • defaultTransactionIsolationLevel - 對此連線預設的事務隔離級別
    • defaultNetworkTimeout - 毫秒,預設的網路超時(等待資料庫操作完成)檢視java.sql.Connection#setNetworkTimeout()文件獲取詳情
    • 給資料庫驅動傳入屬性。在屬性上加字首 driver. 例如 driver.encoding=UTF8 此屬性會通過 DriverManager.getConnection(url, driverProperties)應用
  2. POOLED - 在建立連線物件時不需要初始化和認證過程。對於並行web應用可以得到更快的響應速度,除了上述的屬性,還包括
    • poolMaximumActiveConnections - 同時存在的活躍(使用中)連線,預設10
    • poolMaximumIdleConnections - 同時存在的不活躍連線
    • poolMaximumCheckoutTime - 連線池中的連線可以被“check out”的次數,在它被強制返回之前(回收),預設20000ms
    • poolTimeToWait - 底層設定,在花費非常長時間(避免由於連線池錯誤配置而永遠獲取連線失敗)的情況下給連線池機會來列印日誌狀態並且重新嘗試獲取連線,預設20000ms
    • poolMaximumLocalBadConnectionTolerance - 底層設定,任意執行緒獲取的壞連線的容忍度。如果一個執行緒得到了壞連線,它可能仍然有機會重新嘗試獲取另一個有效連線。但是重試次數不能超過poolMaximumIdleConnections和poolMaximumLocalBadConnectionTolerance的和,預設3(v3.4.5以上)
    • poolPingQuery - Ping Query傳送到資料庫來驗證連線是否正常工作,並且準備好接收請求。預設為 NO PING QUERY SET,這個設定下大多數資料庫發生錯誤會有適當的錯誤資訊
    • poolPingEnabled - 允許或者不允許 ping query。如果這裡允許,那麼必須通過有效的SQL來設定poolPingQuery,預設false
    • poolPingConnectionsNotUsedFor - poolPingQuery的使用頻率,它可以被設定為資料庫連線的典型超時值。預設0(所有連線無限ping,在poolPingEnabled為true的情況下)
  3. JNDI - 這個實現是為了讓EJB或者應用伺服器這類容器(中心化或額外配置DataSource,在JNDI上下文中引用它)使用的
    • initial_context - 從InitialContext中查詢Context,這個屬性是可選的,如果沒有,data_source屬性會在InitialContext中直接查詢
    • data_source - 上下文路徑,對DataSource例項的引用可以被找到。它會覆蓋通過initial_context查詢到的引用,或者通過InitialContext直接查詢到的引用
    • 可以通過給其它屬性加 env. 的字首來將屬性直接傳入 InitialContext,例如 env.encoding=UTF8

通過實現org.apache.ibatis.datasource.DataSourceFactory介面,可以引入任何第三方的DataSource

public interface DataSourceFactory {
  void setProperties(Properties props);
  DataSource getDataSource();
}

org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory可以被作為構建新的datasource的介面卡的超類。例如這個引入C3P0的程式碼

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

  public C3P0DataSourceFactory() {
    this.dataSource = new ComboPooledDataSource();
  }
}

對應的配置檔案

<dataSource type="org.myproject.C3P0DataSourceFactory">
  <property name="driver" value="org.postgresql.Driver"/>
  <property name="url" value="jdbc:postgresql:mydb"/>
  <property name="username" value="postgres"/>
  <property name="password" value="root"/>
</dataSource>

databaseIdProvider

MyBatis可以根據資料庫不同來執行不同的語句。這個支援是基於databaseId屬性來實現的。MyBatis將會載入所有不含databaseId屬性的語句或者帶有databaseId的匹配當前資料庫的語句。一旦找到了相同的語句,那麼不帶databaseId的語句會被拋棄。要支援此特性

<databaseIdProvider type="DB_VENDOR" />

<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>
  <property name="Oracle" value="oracle" />
</databaseIdProvider>

DB_VENDOR實現的databaseIdProvider設定databaseId成DatabaseMetaData#getDatabaseProductName()的返回值。通常這個字串很長,同一個產品的不同版本返回的值還不同,所以可以將其轉換為更短的版本

當這些屬性被提供之後,DB_VENDOR databaseIdProvider會搜尋和在返回的資料庫產品名繫結的第一個key相關的屬性值,如果找不到就是null。即如果getDatabaseProductName()返回"Oracle (DataDirect)",那麼databaseId就設定為oracle

可以將自己的DatabaseIdProvider(實現org.apache.ibatis.mapping.DatabaseIdProvider介面)註冊到配置檔案中

public interface DatabaseIdProvider {
  default void setProperties(Properties p) { // Since 3.5.2, changed to default method
    // NOP
  }
  String getDatabaseId(DataSource dataSource) throws SQLException;
}

mappers

當配置完成之後,我們可以定義自己的mapped SQL語句,但是首先我們要告訴MyBatis在哪裡能找到它們。Java並沒有提供很好的手段來自動查詢,所以最好的方式就是顯式給出這些語句的地址。可以使用類路徑的相對資源引用,全url路徑(file:///),類名或包名

<!-- Using classpath relative resources -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

<!-- Using url fully qualified paths -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>

<!-- Using mapper interface classes -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

<!-- Register all interfaces in a package as mappers -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

這些語句只是告訴了MyBatis哪裡存放著SQL語句,在每個Mapper(xml)檔案中,才是最終的SQL語句