讀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
config.properties
中的值替代
屬性也可以直接傳入SqlSessionFactoryBuilder.build()
方法中,例如:
SqlSessionFactory factory =
sqlSessionFactoryBuilder.build(reader, props);
// ... or ...
SqlSessionFactory factory =
new SqlSessionFactoryBuilder.build(reader, environment, props);
如果屬性存在於多個地方,MyBatis會按一下順序來載入:
- 定義在
properties
元素標籤下的值 - 從classpath中載入的資源或者
properties
元素中的url
元素指定路徑的資源,並且覆蓋已經指定的屬性值 - 作為方法引數傳遞的屬性,覆蓋之前已經指定的屬性值
優先順序從下往上(最高為方法引數,最低為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型別,可以通過兩種方式來覆蓋這個行為
- 在 typeHandler元素中新增一個javaType屬性(javaType="String")
- 在TypeHandler類上新增一個@MappedTypes註解,來指定和它關聯的所有java型別。這個註解如果javaType被指定,則會被忽略
相關聯的JDBC型別可以通過兩種方式指定:
- 在typeHandler元素中指定jdbcType屬性(jdbcType="VARCHAR")
- 在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型別
- UNPOOLED - 每次請求時開啟/關閉連線。對效能不敏感的程式可以使用這種方式,它的配置項
- driver - JDBC驅動的全類名
- url - JDBC URL
- username - 使用者名稱
- password - 密碼
- defaultTransactionIsolationLevel - 對此連線預設的事務隔離級別
- defaultNetworkTimeout - 毫秒,預設的網路超時(等待資料庫操作完成)檢視java.sql.Connection#setNetworkTimeout()文件獲取詳情
- 給資料庫驅動傳入屬性。在屬性上加字首 driver. 例如 driver.encoding=UTF8 此屬性會通過 DriverManager.getConnection(url, driverProperties)應用
- 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的情況下)
- 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語句