点击进入我的博客
1 MyBatis XML配置文件层次结构
以下的图片展示了MyBatis的全部配置元素
MyBatis配置文件层次结构
2 properties元素
properties是一个配置属性的元素,让我们能在配置文件的上下文中使用它,MyBatis提供3种配置方式。
- property子元素。
- properties配置文件。
- SqlSessionFactoryBuilder使用Properties文件构建。
property子元素
<property name="driver" value="com.mysql.jdbc.Driver"/>
properties配置文件
一般情况下,我们会使用一个单独的properties配置文件来配置属性值,以方便我们在多个配置文件中重复使用它们,也方便日后维护和随时修改。我们可以通过${key}
的形式,取出在配置文件中配置的值。
<configuration>
<!-- 引入配置文件 -->
<properties resource="datasource.properties"/>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 使用配置文件中的属性 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
</dataSource>
</environment>
</environments>
</configuration>
SqlSessionFactoryBuilder使用Properties文件构建
出于安全考虑,properties配置文件中的账号密码等元素可能是加密的,这个时候就需要对加密的元素进行处理。
public static void func() throws Exception {
Properties properties = new Properties();
properties.load(Resources.getResourceAsStream("datasource.properties"));
// 对原账号密码解密
properties.setProperty("username", decode(properties.getProperty("username")));
properties.setProperty("password", decode(properties.getProperty("password")));
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// SqlSessionFactoryBuilder可以使用一个InputStream和一个Properties构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is, properties);
}
三种方式的优先级
MyBatis支持的3种配置方式可能同时出现,并且属性还会重复配置,MyBatis将按照下面的顺序来加载:
- 在properties元素体内指定的属性首先被读取。
- 根据 properties元素中的resource属性读取类路径下属性文件,或者根据ur属性指定的路径读取属性文件,并覆盖已读取的同名属性。
- 读取作为build()方法参数传递的属性,并覆盖已读取的同名属性。
因此,通过build()方法参数传递的属性具有最高优先级,resource/url属性中指定的配置文件
次之,最低优先级的是 properties属性中指定的属性。因此,我们尽量不要使用混合的方式来定义配置,首选的方式是使用properties文件。
3 settings设置
settings(设置)在MyBatis中是最复杂的配置,它会改变 MyBatis运行时的行为。
设置参数 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响所有映射器中配置的缓存全局开关 | true、 false | true |
lazyLoadingEnabled | 延迟加载的全局开关,当它开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态 | true、false | false |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 | true、 false | true |
multipleresultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动) | true、 false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 | true、 false | true |
useGeneratedKeys | 允许JDBC支持自动生成主键,需要驱动兼容。如果设置为true,则这个设置强制使用自动生成主键 | true、fase | false |
autoMappingBehavior | 指定 MyBatis应如何自动映射列到字段或属性:NONE表示取消自动映射、PARTIAL只会自动映射没有定义嵌套结果集映射的结果集、FULL会自动映射任意复杂的结果集 | NONE、 PARTIAL、FULL | PARTIAL |
defaultExecutorType | 配置默认的执行器:SIMPLE是普通的执行器、REUSE执行器会重用预处理语句(prepared statements)、BATCH执行器将重用语句并执行批量更新 | SIMPLE、 REUSE、SIMPLE | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。当没有设置的时候它取的就是驱动默认的时间 | any positive integer | Not set(null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(Row Bounds) | true、 false | False |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则映射,即从经典数据库列名 A_COLUMN到经典Java属性名 aColumn的类似映射 | true、false | false |
localCacheScope | MyBatis利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为STATEMENT,本地 STATEMENT会话仅用在语句执行上,对相同 SqlSession的不同调用将不会共享数据 | SESSION、STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况情况直接使用一般类型即可,比如NULL、 VARCHAR、OTHER | JdbcType枚举 | OTHER |
lazyLoadTriggerMethods | 指定对象的方法触发一次延迟加载 | 如果是一个方法列表,我们则用逗号将它们隔开 | equals, clone, hashCode, toString |
defaultScriptingLanguage | 指定动态SQL生成的默认语言 | 你可以配置类的别名或者类的全限定名 | org....XMLDynamicLanguageRiver |
callSettersOnNulls | 指定当结果集中值为null的时候是否调用映射对象的setter(map对象时为put)方法,这对于有Map.keySet()依赖或null值初始化的时候是有用的。注意基本类型是不能设置成null的 | true、 false | false |
logPrefix | 指定MyBatis增加到日志名称的前缀 | 任何字符串 | 没有设置 |
logImpl | 指定 MyBatis所用日志的具体实现,未指定时将自动查找 | SLF4J、LOG4J、JDK LOGGING、NO LOGGING | 没有设置 |
proxyFactory | 指定MyBatis创建具有延迟加载能力的对象所用到的代理工具 | CGLIB、JAVASSIST | (3.3.0以上版本是)JAVASSIST,否则CGLIB |
4 typeAliases类型命名
别名(typeAliases)是一个指代的名称。因为我们遇到的类全限定名过长,所以我们希望用一个简短的名称去指代它,而这个名称可以在MyBatis上下文中使用,在 MyBatis中别名是不分大小写的。一个 typeAliases的实例是在解析配置文件时生成的,然后长期保存在 Configuration对象中,这样就没有必要运行的时候再次生成它的实例了。
系统定义别名
通过org.apache.ibatis.type.TypeAliasRegistry可以查看所有系统定义的别名,主要是基本数据类型、字符串、基本数据类型数组、日期、容器类。
通过XML自定义别名
在mybatis-config.xml中通过<typeAliases>
元素可以自定义别名。
<typeAliases>
<typeAlias type="com.ankeetc.spring.domain.UserDomain" alias="user"/>
</typeAliases>
通过注解的方式自定义别名
<!-- 通过package扫描包 -->
<typeAliases>
<package name="com.ankeetc.spring.domain"/>
</typeAliases>
@Alias("user")
public class UserDomain {
}
当配合上面的配置,MyBatis就会自动扫描包,将扫描到的类装载到上下文中。如果配置了包扫描的路径,而没有注解@Alias的类也会被MyBatis装载,MyBatis会自动把类名的第一个字母变为小写,作为MyBatis的别名。
5 typeHandler类型处理器
MyBatis在预处理语句(PreparedStatement)中设置一个参数或者从结果集(ResultSet)中取出一个值时,都会用注册了的typeHandler进行处理。typeHandler常用的配置为Java类型(javaType)、JDBC类型(jdbcType)。typeHandler的作用就是将参数从javaType转化为 jdbcType,或者从数据库取出结果时把jdbcType转化为javaType。
5.1 系统定义的typeHandler
在org.apache.ibatis.type.TypeHandlerRegistry中MyBatis定义了很多系统内部的typeHader,主要是基本类型、字符串、时间类型、枚举类型的处理。
5.2 自定义typeHandler
继承BaseTypeHandler或实现TypeHandler
对于自定义typeHandler的要求是必须实现接口org.apache.ibatis.type.TypeHandler
,更简单的方式是继承已经实现了该接口的实现类org. apache.ibatis.type.BaseTypeHandler
。
确定拦截类型
可以在自定义typeHandler里用注解配置@MappedTypes
和@MappedJdbcTypes
,也可以在<typeHandler>
元素中指定。
-
@MappedTypes
定义的是JavaType类型,可以指定哪些Java类型被拦截; -
@MappedJdbcTypes
定义的是JdbcType类型,取值范围在枚举类org.apache.ibatis.type.JdbcType
中找到。
@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyStringTypeHandler implements TypeHandler<String> {
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
System.out.println("MyStringTypeHandler#setParameter");
ps.setString(i, parameter);
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
System.out.println("MyStringTypeHandler#getResult 1");
return rs.getString(columnName);
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
System.out.println("MyStringTypeHandler#getResult 2");
return rs.getString(columnIndex);
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
System.out.println("MyStringTypeHandler#getResult 3");
return cs.getString(columnIndex);
}
}
启用自定义的typeHander
- 可以在mybatis-config.xml里面配置,可以选择配置一个类或者一个包。
- 在Mapper.xml的
<resultMap>
直接指定typeHandler。 - 在Mapper.xml的参数中指定typeHandler。
<!-- 1. 在mybatis-config.xml中配置 -->
<typeHandlers>
<typeHandler handler="com.ankeetc.spring.type.MyStringTypeHandler"/>
</typeHandlers>
<!-- 2. 在Mapper.xml中resultMap元素中配置-->
<resultMap id="BaseResultMap" type="com.ankeetc.spring.domain.UserDomain">
<result column="name" property="name" javaType="java.lang.String" jdbcType="VARCHAR" typeHandler="com.ankeetc.spring.type.MyStringTypeHandler"/>
</resultMap>
<!-- 注意必须使用上面的resultMap -->
<select id="select" resultMap="BaseResultMap" parameterType="java.lang.Long">
SELECT * FROM tb_name WHERE id = #{id}
</select>
<!-- 3. 在Mapper.xml参数中配置-->
<insert id="insert" parameterType="com.ankeetc.spring.domain.UserDomain">
INSERT INTO tb_name (name) VALUES (#{name, jdbcType=VARCHAR, javaType=String, typeHandler=com.ankeetc.spring.type.MyStringTypeHandler})
</insert>
5.3 枚举类型typeHandler
MyBatis中枚举类型的typeHandler则有自己特殊的规则,MyBatis内部提供了两个转化枚举类型的typeHandler给我们使用。
- org.apache.ibatis.type.EnumTypeHandler:使用枚举字符串名称作为参数传递的
- org.apache.ibatis.type.EnumOrdinalTypeHandler:使用整数下标作为参数传递的
6 objectFactory对象工厂
当MyBatis在构建一个结果返回的时候,都会使用ObjectFactory(对象工厂)去构建POJO,我们可以定制自己的对象工厂。MyBatis中默认的ObjectFactory是由org.apache.ibatis.reflection.factory.DefaultObjectFactory
来提供服务的。
自定义objectFactory
- 实现ObjectFactory接口,也可以通过继承DefaultObjectFactory扩展功能。
- 在mybatis-config.xml中使用
<objectFactory>
元素。
7 environments环境变量
配置环境可以注册多个环境,每一个环境分为两大部分:一个是数据库源(dataSource)的配置,另外一个是数据库事务(transactionManager)的配置。
7.1 概述
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
</dataSource>
</environment>
</environments>
- default:表示默认使用哪个数据源
- id:表示数据源的名称
- transactionManager的事务类型type一共有三种:JDBC,采用JDBC方式管理事务,独立编码中我们常常使用;MANAGED,采用容器方式管理事务,在JNDI数据源中常用;自定义,由使用者自定义数据库事务管理办法,适用于特殊应用。
- property元素配置数据源的各类属性
- dataSource的type属性是提供我们对数据厍连接方式的配置:UNPOOLED(非连接池数据库)、POOLED(连接池数据库)、JNDI(JNDI数据源)、自定义数据源。
7.2 数据库事务
数据库事务是交由SqlSession去控制的,我们可以通过SqlSession提交或者回滚。在大部分的工作环境下,我们都会使用 Spring框架来控制它。
7.3 数据源
MyBatis内部为我们提供了3种数据源的实现方式:
- UNPOOLED,使用
org.apache.ibatis.datasource.unpooled.UnpooledDataSource
实现。 - POOLED,使用
org.apache.ibatis.datasource.pooled.PooledDataSource
实现。 - JNDI,使用
org.apache.ibatis.datasource.jndi.JndiDataSourceFactory
实现。
7.4 自定义数据源
使用自定义数据源,它必须实现org.apache.ibatis.datasource.DataSourceFactory
接口。然后在<dataSource>中type属性指定对应的全限定类名。
public class MyDataSourceFactory implements DataSourceFactory {
DataSource dataSource;
public MyDataSourceFactory() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/test");
druidDataSource.setUsername("root");
dataSource = druidDataSource;
}
private Properties properties;
@Override
public void setProperties(Properties props) {
this.properties = props;
}
@Override
public DataSource getDataSource() {
return dataSource;
}
}
<dataSource type="com.ankeetc.spring.config.MyDataSourceFactory"/>
8 databaseIdProvider数据库厂商标识
在相同数据库厂商的环境下,数据库厂商标识没有什么意义,在实际的应用中使用得比较少,因为使用不同厂商数据库的系统还是比较少的。 MyBatis可能会运行在不同厂商的数据库中,它为此提供一个数据库标识,作用在于指定SQL到对应的数据库厂商提供的数据库中运行。
8.1 系统默认规则
系统默认规则type="DB VENDOR"
是启动MyBatis内部注册的策略器。首先 MyBatis会将你的配置读入Configuration类里面,在连接数据库后调用getDatabaseProductName()方法去获取数据库的信息,然后用我们配置的name值去做匹配来得到DatabaseId。
获得数据库的Id
可以用下面的代码来获得数据库的Id:sqlSessionFactory. getConfiguration(). getDatabaseId()
指定数据库厂商
可以指定SQL在哪个数据库厂商执行,需要在Mapper.XML中通过指定。
<insert id="insert" parameterType="com.ankeetc.spring.domain.UserDomain" databaseId="mysql">
INSERT INTO tb_name (name) VALUES (#{name, typeHandler=com.ankeetc.spring.type.MyStringTypeHandler})
</insert>
8.2 自定义规则
- 实现
org.apache.ibatis.mapping.DatabaseIdProvider
接口 - 配置
<databaseIdProvider>
标签。
public class MyDatabaseIdProvider implements DatabaseIdProvider {
private Properties properties;
@Override
public void setProperties(Properties p) {
this.properties = p;
}
@Override
public String getDatabaseId(DataSource dataSource) throws SQLException {
String dbName = dataSource.getConnection().getMetaData().getDatabaseProductName();
return this.properties.getProperty(dbName);
}
}
<databaseIdProvider type="MyDatabaseIdProvider">
<property name="SQL Server" value="sqlserver"/>
<property name="MySQL" value="mysql"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
网友评论