mybatis-configuration.xml是MyBatis的全局配置文件(文件名任意),其配置内容和顺序如下:
properties : 属性(文件)加载/配置 settings : 全局配置参数 typeAliases : 定义类型别名 typeHandlers : 类型处理器 objectFactory : 对象工厂 plugins : 插件 environments : 环境集合属性对象方便对配置参数统一管理,供其他XML引用,我们可以将数据库的连接参数抽取出来:
db.properties
## Data Source
mysql.driver.class=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://host:port/db?characterEncoding=utf-8
mysql.user=user
mysql.password=password
mybatis-configuration.xml
<code class=" hljs avrasm"><code class=" hljs xml"><properties resource="db.properties"> <environments default="development"> <environment id="development"> <!--{cke_protected}{C}%3C!%2D%2D%20%E9%85%8D%E7%BD%AEJDBC%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86%2D%2D%3E--> <transactionmanager type="JDBC"> <!--{cke_protected}{C}%3C!%2D%2D%20%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E6%BA%90%2D%2D%3E--> <datasource type="POOLED"> <property name="driver" value="${mysql.driver.class}"> <property name="url" value="${mysql.url}"> <property name="username" value="${mysql.user}"> <property name="password" value="${mysql.password}"> </property></property></property></property></datasource> </transactionmanager></environment> </environments></properties></code></code>
注: MyBatis按照如下顺序加载properties:
1) 在标签内定义的属性;
2) .properties文件中定义的属性;
3) 最后读取作为方法参数传递的属性.
settings
MyBatis全局配置参数,会影响MyBatis运行时行为(如:开启二级缓存/延迟加载).见MyBatis文档.
typeAliases
MyBatis默认支持的类型别名可参考MyBatis文档,我们也可以自定义别名,但并不推荐,使用PO对象的全限定名可以提高Statement的可读性.
typeHandlers
typeHandlers用于Java类型和JDBC类型转换,MyBatis提供了很多默认的类型处理器(详见MyBatis文档),而且也基本满足日常开发需求,因此一般就不再需要单独定义.
mappers
前面已经将SQL语句定义到了mapper文件中,那么
|
|
---|---|
|
|
|
|
|
|
|
|
注意:后两种方式要求mapper接口名和mapper映射文件名称相同,且放在同一个目录中(不推荐).
其他关于MyBatis的配置信息可参考MyBatis文档.
整合Spring
实现MyBatis与Spring整合之后,可以使用Spring来管理SqlSessionFactory和mapper接口,Spring自动使用SqlSessionFactory创建SqlSession,并将实现好DAO接口注册到Spring容器中, 供@Autowired使用.
1. 添加依赖
添加Spring支持
org.springframework
spring-core
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-expression
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-tx
${spring.version}
添加MyBatis-Spring包
org.mybatis
mybatis-spring
${mybatis-spring.version}
添加Hikaricp数据库连接池
com.zaxxer
HikariCP
${hikaricp.version}
不要忘了MySQL数据库驱动
mysql
mysql-connector-java
${mysql.version}
2. 配置文件
精简mybatis-configuration.xml
可以将数据源的配置移到下面的applicationContext-datasource.xml中.
<code class=" hljs avrasm"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20%3F%2D%2D%3E--> <configuration> <mappers> <mapper resource="mybatis/mapper/UserDAO.xml"> </mapper></mappers> </configuration></code></code></code></code></code></code></code>
定义applicationContext-datasource.xml
<code class=" hljs avrasm"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:db.properties"> <!--{cke_protected}{C}%3C!%2D%2D%20%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E6%BA%90%20%2D%2D%3E--> <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig"> <property name="driverClassName" value="${mysql.driver.class}"> <property name="jdbcUrl" value="${mysql.url}"> <property name="username" value="${mysql.user}"> <property name="password" value="${mysql.password}"> <property name="maximumPoolSize" value="5"> <property name="maxLifetime" value="700000"> <property name="idleTimeout" value="600000"> <property name="connectionTimeout" value="10000"> <property name="dataSourceProperties"> <props> <prop key="dataSourceClassName">com.mysql.jdbc.jdbc2.optional.MysqlDataSource</prop> <prop key="cachePrepStmts">true</prop> <prop key="prepStmtCacheSize">250</prop> <prop key="prepStmtCacheSqlLimit">2048</prop> </props> </property> </property></property></property></property></property></property></property></property></bean> <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close"> <constructor-arg ref="hikariConfig"> </constructor-arg></bean> <!--{cke_protected}{C}%3C!%2D%2D%20%E9%85%8D%E7%BD%AESqlSessionFactory%20%2D%2D%3E--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"> <property name="configLocation" value="classpath:mybatis/mybatis-configuration.xml"> </property></property></bean> <!--{cke_protected}{C}%3C!%2D%2D%20%E6%A0%B9%E6%8D%AEmapper%E6%8E%A5%E5%8F%A3%E7%94%9F%E6%88%90%E4%BB%A3%E7%90%86%E5%AF%B9%E8%B1%A1%20%2D%2D%3E--> <bean id="dao" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.fq.mybatis.UserDAO"> <property name="sqlSessionFactory" ref="sqlSessionFactory"> </property></property></bean> </context:property-placeholder></beans></code></code></code></code></code></code></code></code>
上面的配置存在一个问题:需要针对每个mapper配置一个MapperFactoryBean(繁琐),因此这段根据mapper接口生成代理对象的配置可更改如下:
<code class=" hljs avrasm"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><!--{cke_protected}{C}%3C!%2D%2D%20%E5%9F%BA%E4%BA%8E%E5%8C%85%E6%89%AB%E6%8F%8F%E7%9A%84mapper%E9%85%8D%E7%BD%AE%20%2D%2D%3E--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.fq.mybatis"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"> </property></property></bean></code></code></code></code></code></code></code></code></code>
附: applicationContext-database.xml完整配置可参考: Git地址
定义Spring主配置文件applicationContext.xml
定义注解驱动及加载静态配置文件datasource:
<code class=" hljs avrasm"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%2D%2D%3E--> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--{cke_protected}{C}%3C!%2D%2D%20%E6%B3%A8%E8%A7%A3%E9%A9%B1%E5%8A%A8%20%2D%2D%3E--> <context:annotation-config> <!--{cke_protected}{C}%3C!%2D%2D%20%E5%8A%A0%E8%BD%BD%E9%9D%99%E6%80%81%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%20%2D%2D%3E--> <import resource="applicationContext-datasource.xml"> </import></context:annotation-config></beans></code></code></code></code></code></code></code></code></code></code>
Client
/**
* @author jifang
* @since 16/2/22 上午10:20.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/applicationContext.xml")
public class UserDAOClient {
@Autowired
private UserDAO dao;
@Test
public void client() throws Exception {
User user = dao.selectUserById(1);
System.out.println(user);
}
}
缓存
与大多数持久层框架一样,MyBatis也支持一级缓存和二级缓存.
缓存作用是提升系统整体性能(不是提升数据库性能:因为缓存将数据库中的数据存放到内存,下次查询同样内容时直接从内存读取,减轻数据库压力,而且直接从内存中读取数据要比从数据库检索快很多,因此可以提升系统整体性能).
缓存数据更新:当一个作用域(一级缓存为SqlSession/二级缓存为namespace)进行了C/U/D操作后,默认该作用域下所有缓存都被清空.
一级缓存
MyBatis默认开启了一级缓存.一级缓存是基于org.apache.ibatis.cache.impl.PerpetualCache的HashMap本地缓存,其存储作用域为SqlSession,同一个SqlSession几次执行相同SQL,后面的查询会直接从缓存中加载,从而提高查询效率/减轻数据库压力.当SqlSession经flush/close后,该SqlSession中的所有Cache数据被清空.
二级缓存
与一级缓存机制类似,MyBatis二级缓存默认也是采用PerpetualCache的HashMap存储,不同在于二级缓存存储作用域为namespace/mapper,并且可以自定义缓存实现,如Ehcache.
MyBatis默认没有开启二级缓存,需要经过以下步骤才能使用:
启用二级缓存(可选)
其需要在mybatis-configuration.xml的settings全局参数中开启:
<code class=" hljs avrasm"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs java"><code class=" hljs xml"><settings> <setting name="cacheEnabled" value="true"> </setting></settings></code></code></code></code></code></code></code></code></code></code></code></code>
cacheEnabled对此配置文件下的所有cache进行全局性开/关设置(默认为true).
配置缓存策略
在mapper映射文件中添加标签,以指定该namespace开启二级缓存, 并指定缓存策略:
1) eviction:缓存淘汰算法:
算法
描述
释义
LRU
最近最少使用
移除最长时间不被使用的对象(默认).
FIFO
先进先出
按对象进入缓存的顺序移除.
SOFT
软引用
移除基于垃圾回收器状态和软引用规则的对象.
WEAK
弱引用
更积极地移除基于垃圾收集器状态和弱引用规则的对象.
2) flushInterval:刷新间隔(缓存过期时间),单位为毫秒,MyBatis会每隔一段时间自动清空缓存(默认刷新间隔为空, 即永不过期,仅调用语句时刷新).
3) size:引用数目,要记住你缓存的对象的数目和运行环境可用内存资源数目(默认1024).
4) readOnly: 只读.如果为true,则所有相同SQL返回同一对象(因此这些对象不能修改,有助于提高性能,但并发操作同一条数据时,可能不安全);如果为false,则相同SQL后面返回的是cache的clone副本(通过序列化,慢一些但更是安全,因此默认是false).
序列化
PO对象要实现Serializable序列化,因为二级缓存的存储介质不一定只是内存:
public class User implements Serializable {
//...
}
Client
@Test
public void cacheClient() throws Exception {
testCache(factory.openSession());
testCache(factory.openSession());
testCache(factory.openSession());
}
private void testCache(SqlSession session) throws Exception {
UserDAO dao = session.getMapper(UserDAO.class);
dao.selectUserById(1);
// 需要将SqlSession关闭才能将数据写入缓存.
session.close();
}
运行代码, 并观察log输出的命中率(Cache Hit Ratio).
Statement配置
1) 禁用缓存: 在Statement中设置useCache="false"可以禁用当前select语句的二级缓存(默认为true:该SQL启用二级缓存).
<code class=" hljs bash"><code class=" hljs java"><code class=" hljs java"><code class=" hljs vbnet"><select id="selectUserById" parametertype="java.lang.Integer" resulttype="com.fq.domain.User" usecache="true">
SELECT *
FROM user
WHERE id = #{id};
</select></code></code></code></code>
2)刷新缓存: 同一个namespace中,如果还有其它insert/update/delete操作,需要刷新缓存,使用flushCache="true"属性设置(默认为true刷新缓存).
<code class=" hljs bash"><code class=" hljs java"><code class=" hljs java"><code class=" hljs vbnet"><code class=" hljs xml"><insert id="insertUserList" parametertype="java.util.List" flushcache="true">
INSERT INTO user(name, password) VALUES
<if test="list != null and list.size != 0">
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.password})
</foreach>
</if>
</insert></code></code></code></code></code>
整合Ehcache
MyBatis暴露一个org.apache.ibatis.cache.Cache接口出来,通过实现该接口,可以实现各类缓存产品(如Ehcache/Redis/Memcached)与MyBatis的整合(MyBatis的特长操作数据库,缓存管理并不是其擅长,因此整合其他缓存产品可以提高系统整体性能).
Ehcache是一个纯Java开发的进程内缓存框架,具有开源/快速/灵活等特点,是Hibernate默认的CacheProvider.使用Ehcache需要在pom.xml中添加如下依赖:
<code class=" hljs bash"><code class=" hljs java"><code class=" hljs java"><code class=" hljs vbnet"><code class=" hljs xml"><code class=" hljs xml"><dependency>
<groupid>net.sf.ehcache</groupid>
<artifactid>ehcache-core</artifactid>
<version>2.6.11</version>
</dependency>
<dependency>
<groupid>org.mybatis.caches</groupid>
<artifactid>mybatis-ehcache</artifactid>
<version>1.0.3</version>
</dependency></code></code></code></code></code></code>
配置Ehcache
在Resources目录下添加ehcache.xml配置文件
<code class=" hljs bash"><code class=" hljs java"><code class=" hljs java"><code class=" hljs vbnet"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><ehcache>
<diskstore path="/data/cache">
<defaultcache maxelementsinmemory="1000" maxelementsondisk="10000000" eternal="false" overflowtodisk="false" timetoidleseconds="120" timetoliveseconds="120" diskexpirythreadintervalseconds="120" memorystoreevictionpolicy="LRU">
</defaultcache>
</diskstore></ehcache></code></code></code></code></code></code></code>
属性
描述
diskStore
指定缓存数据在磁盘的存储位置
maxElementsInMemory
在内存中缓存element的最大数目
maxElementsOnDisk
在磁盘上缓存element的最大数目,0表示无穷大
eternal
设定缓存的elements是否永远不过期.true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk
设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
timeToIdleSeconds
刷新间隔:缓存数据前后两次访问时间超过timeToIdleSeconds时,这些数据便会删除(默认为0,时间间隔无穷大)
timeToLiveSeconds
缓存element的有效生命期(默认为0,时间无限)
diskSpoolBufferSizeMB
设置DiskStore(磁盘缓存)缓存区大小.默认是30MB.
diskPersistent
在JVM重启时是否使用磁盘保存Ehcache数据,默认是false.
diskExpiryThreadIntervalSeconds
磁盘缓存的清理线程运行间隔,默认是120秒.
memoryStoreEvictionPolicy
当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略.默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
mapper配置ehcache
还可以根据需求调整当前namespace的缓存参数:
<code class=" hljs java"><code class=" hljs bash"><code class=" hljs java"><code class=" hljs java"><code class=" hljs vbnet"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs xml"><code class=" hljs bash"><code class=" hljs xml"><cache type="org.mybatis.caches.ehcache.EhcacheCache">
<property name="timeToIdleSeconds" value="3600">
<property name="timeToLiveSeconds" value="3600">
<!--{cke_protected}{C}%3C!%2D%2D%20%E5%90%8Cehcache%E5%8F%82%E6%95%B0maxElementsInMemory%20%2D%2D%3E-->
<property name="maxEntriesLocalHeap" value="1000">
<!--{cke_protected}{C}%3C!%2D%2D%20%E5%90%8Cehcache%E5%8F%82%E6%95%B0maxElementsOnDisk%20%2D%2D%3E-->
<property name="maxEntriesLocalDisk" value="10000000">
<property name="memoryStoreEvictionPolicy" value="LRU">
</property></property></property></property></property></cache></code></code></code></code></code></code></code></code></code></code>
适用场景
对于查询请求多且对查询结果实时性要求不高的场景,可采用二级缓存降低数据库负担,提高访问速度(业务场景如:微博/动态/订单信息等). 局限
二级缓存对细粒度级别的缓存实现不好,如”缓存所有的商品信息时,二级缓存就无法实现当一个商品信息变化时只刷新该商品缓存而不刷新全部商品缓存“,因为二级缓存区域以namespace为单位划分,当一个商品发生变化会将所有商品缓存清空,因此解决此类问题需要在上层对数据进行业务划分.
二级缓存小结