延迟加载
当我们执行某些查询操作时,如果可以先查询出一些简单的满足需求,然后当需要某部分时再去查询关联数据,这就叫做延迟加载,这样做可以提高数据库性能
1.需求,例如我们拿以前的例子,查询订单的同时关联查询下单用户,这个关联查询用户的那一部分我们让它延迟加载
2.针对不同的需求编写不同的resultMap,根据不同的对应关系选择association或者collection
3.这里从订单角度出发,订单和下单用户是一对一关系,那么就是使用association
4.实现步骤
1.编写两个statement
2.编写只查询订单的statement
3.编写关联查询用户的statement
<!--只查询订单信息-->
<select id="OrdersUserLazyLoading" resultMap="findOrdersUserLazyLoading">
select * from orders
</select>
<!--编写延迟加载resultMap-->
<resultMap id="findOrdersUserLazyLoading" type="orders">
<!--编写订单映射-->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 实现对用户信息进行延迟加载
select:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement)
要使用userMapper.xml中findUserById完成根据用户id(user_id)用户信息的查询,
如果findUserById不在本mapper中需要前边加namespace
column:订单信息中关联用户信息查询的列,是user_id(外键关联这个user表的id)
关联查询的sql理解为:
SELECT orders.*,
(SELECT username FROM USER WHERE orders.user_id = user.id)username,
(SELECT sex FROM USER WHERE orders.user_id = user.id)sex
FROM orders
-->
<association property="user" javaType="user"
select="com.lfm.mapper.UserMapper.findUserById" column="user_id">
<!-- 实现对用户信息进行延迟加载 -->
</association>
</resultMap>
4.去SqlMapConfig中配置打开延迟加载(默认是不开启的)
5.编写测试类测试
6.debug查看结果
按F8下一步
总结如下:
使用延迟加载方法,先去查询简单的sql(最好单表,也可以关联查询),再去按需要加载关联查询的其它信息。
查询缓存
和hibernate一样,mybatis同样有一级缓存和二级缓存,这都是用于减轻数据压力,提高数据库性能。
对于mybatis而言,缓存如下图所示
1.一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
2.二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
实现一级缓存(默认打开的)
用一个例子测试一下就知道了,就拿根据id查询用户信息来测试把,两次都发送相同的sql语句就会触发缓存
mapper.xml文件编写如下
mapper接口
测试类
结果
如果在两次查询操作之间发生了数据修改呢?没关系,mybatis有个刷新缓存机制,会保持缓存中的数据都是最新的,我们可以测试一下,在两次查询操作中将id=1的用户更新了第二次再查询
1.写个更新sql
2.测试类
3.结果
总结:一级缓存是在SqlSession范围中的,当SqlSession销毁的时候缓存就已经不存在了,如果想要销毁之后仍然能够在其它地方用到这个SqlSession中的缓存,那么就应该开启二级缓存
二级缓存(手动开启)
原理如下
1.开启二级缓存
2.还要在想要使用二级缓存的mapper.xml文件中开启二级缓存,这样整个mapper开算是开启了二级缓存
3.相应的po类还要实现序列化接口,因为二级缓存数据存储介质多种多样,不一定在内容,有可能在硬盘,所以实现该接口为了以后能够反序列化
测试方法
结果
如果中途数据发生改变呢?
查看结果
禁用二级缓存
只需要在select标签加上useCache="false"即可
刷新缓存
设置statement配置中的flushCache=“true” 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读,一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。
最后说一下mybatis整合其它分布式缓存框架
因为mybatis本身是不支持分布式缓存的,这样就不能在集群上实现一些功能了,例如用户在服务器A登录了,但是如果不用分布式缓存的话,服务器B并不知道A登录了。所以mybatis要整合第三方的分布式缓存框架,例如redis、memcached、ehcache
这里说一下整合ehcahe
1.导入jar包
2.mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。
这是mybatis的默认实现
3.配置mapper中cache中的type为ehcache对cache接口的实现类型
在这个包下找到
4.配置ehcache的文件
上面的信息都可以在ehcache核心包中找到,会出现找不到xsd,百度就可解决了不难
5.用上面的测试类测试一下,手动改回数据库信息,结果如下
DEBUG [main] - Created connection 479397964.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - ==> Preparing: select * from user where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User{id=1, username='廖子默', sex='男', birthday=Fri Aug 16 00:00:00 CST 2019, address='广东'}
DEBUG [main] - put added 0 on heap
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - Returned connection 479397964 to pool.
DEBUG [main] - Cache Hit Ratio [com.lfm.mapper.UserMapper]: 0.5
DEBUG [main] - Cache Hit Ratio [com.lfm.mapper.UserMapper]: 0.3333333333333333
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 479397964 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - ==> Preparing: update user set username=? where id=?
DEBUG [main] - ==> Parameters: 廖子默66(String), 1(Integer)
DEBUG [main] - cleared heap usage
/******此处省略大概一百个DEBUG [main] - cleared heap usage和DEBUG [main] - cleared disk usage**********/
DEBUG [main] - put added 0 on heap
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - Returned connection 479397964 to pool.
DEBUG [main] - Cache Hit Ratio [com.lfm.mapper.UserMapper]: 0.25
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 479397964 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - ==> Preparing: select * from user where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User{id=1, username='廖子默66', sex='男', birthday=Fri Aug 16 00:00:00 CST 2019, address='广东'}
DEBUG [main] - put added 0 on heap
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c93084c]
DEBUG [main] - Returned connection 479397964 to pool.
Process finished with exit code 0
二级缓存应用场景
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度
实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
二级缓存局限性
mybatis二级缓存对细粒度的数据级别的缓存实现不好,不能针对庞大的缓存数据中只清空需要修改的数据。
延迟加载和缓存本来就是性能优化的手段,不是一朝一夕能搞定的,现在这是照搬基础,等以后活学活用了再回来补充