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会按一下顺序来加载:
- 定义在
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语句