延迟加载
如果查询订单并且关联查询用户(一对一),如果先查询订单信息即可满足需求,当我们需要查询用户信息时,再查询用户信息,把对用户信息的按需求去查询就是延迟加载。resultMap可以实现高级映射,association、collection标签具备延迟加载功能,可以实现一对一及一对多映射。
第一步:在sqlMapConfig.xml中开启延迟加载。
<settings>
<!--打开延迟加载的开关,lazyLoadingEnabled默认是false-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--把积极加载换成消极加载,默认是true-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
第二步:定义Mapper.xml。
<resultMap type="orders" id="findOrderUserLazyLoadingResultMap">
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createTime" property="createTime"/>
<result column="note" property="note"/>
<result column="updateTime" property="updateTime"/>
<!--当我们调用getUser()这个方法时,<association>标签的内容才会执行。
select:执行延迟加载的statement的id。
column:订单信息中,关联查询用户的列,就是user_id。
要使用UserMapper.xml中的selectUserById这个Statement,注意要加上namespace属性。
<collection>的延迟加载和<association>类似。
-->
<association property="user" javaType="user" select= selectUserById" column="user_id">
</association>
</resultMap>
<select id="findOrderUserLazyLoading" resultMap="findOrderUserLazyLoadingResultMap">
select * from orders
</select>
第三步:定义Mapper.java
public List<Orders> findOrderUserLazyLoading() throws Exception;
第四步:测试
执行上面方法findOrderUserLazyLoading,只查询Orders信息。在程序中遍历上一步查出来的List
查询缓存
Mybatis提供查询缓存,用于减轻数据库压力,提高数据库性能,Mybatis提供一级缓存和二级缓存。
一级缓存是SqlSession级别的缓存,而二级缓存是Mapper级别的缓存,多个SqlSession去操作同一个Mapper的SQL语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession。Mybatis的一级缓存是指SqlSession。一级缓存的作用域是一个SqlSession。Mybatis默认开启一级缓存。在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中。二级缓存的作用域是同一个namespace下的Mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。在同一个namespace下的Mapper文件中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中,第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则二级缓存清空。
一级缓存
根据id查询用户,Mybatis的一级缓存是默认开启的。
查询用户id为1的用户信息,会先去找缓存中是否有id是1的用户信息,如果没有,从数据库查询用户信息,得到用户信息,然后将用户信息存储到一级缓存中。如果SqlSession去执行commit操作(增,删,改),Mybatis将清空一级缓存,目的是为了避免脏读。正式开发时,是将Mybatis和Spring进行整合开发,事务控制在service中。一个service方法中包括很多Mapper方法调用。开始执行时,开启事务,创建SqlSession对象。第一次调用Mapper的方法findUserById(1)。第二次调用Mapper的方法findUserById(1)。方法结束,关闭SqlSession,期间是使用的同一个Mapper代理对象。Mybatis的内部缓存使用一个HashMap,key为hashcode+statementId+sql语句。Value为查询出来的结果集映射成的java对象。SqlSession执行insert、update、delete等操作commit后会清空该SQLSession缓存。
二级缓存
二级缓存是Mapper级别的, Mybatis默认是没有开启二级缓存。
SqlSession1去查询用户id为1的用户信息,查询到用户信息会储到二级缓存中。SqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在就直接从缓存中取出数据。二级缓存与一级缓存区别,二级缓存的范围更大,多个SqlSession可以共享一个UserMapper的二级缓存。每一个Mapper都有一个二级缓存区域,二个Mapper的namespace如果相同,这俩个Mapper执行SQL查询到的数据将存储在相同的二级缓存区域中。
第一步,SqlMapConfig.xml中开启二级缓存的总开关。
<settings>
<!--开启二级缓存,默认的是true-->
<setting name="cacheEnabled" value="true"/>
</settings>
第二步,在UserMapper.xml中开启二级缓存。
<mapper namespace="cn.feige.mapper.UserMapper">
<!--开启本mapper的namespace下的二级缓存-->
<cache></cache>
</mapper>
某些Statement不想用二级缓存,可以禁用二级缓存,useCache默认属性是true。
<!--设定不使用二级缓存-->
<select id="selectUserById" parameterType="java.lang.Integer" resultType="user" useCache="false"></select>
刷新间隔,可以设置任意的正整数,而且它们代表一个合理的毫秒形式的时间,段默认情况是关闭的,也就是没有刷新时间间隔。
<!--设置二级缓存的自动刷新时间-->
<cache flushInterval="2000"></cache>
第三步,调用POJO类的序列化接口,为了将数据取出进行反序列化操作,因为二级缓存的存储介质多种多样不一定在内存。
public class User implements Serializable{
private int id;
private String username;
......
}
第四步,测试。
public class test {
private SqlSessionFactory sqlSessionFactory = null; //定义会话工厂对象
@Before
public void init() throws IOException {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void selectUserById() throws IOException {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
User user1 = userMapper1.selectUserById(1);
System.out.println(user1);
sqlSession1.close(); //关闭会话1
User user2 = userMapper2.selectUserById(1);
user2.setSex("女");
userMapper2.updateUserMes(user2);
System.out.println(user2);
sqlSession2.commit(); //清空UserMapper下的二级缓存。
sqlSession2.close(); //关闭会话2
User user3 = userMapper3.selectUserById(1);
System.out.println(user3);
sqlSession3.close(); //关闭会话3
}
}
应用场景:
对于访问多的查询请求,且用户对查询的结果实时性要求不高时,此时可采用Mybatis二级缓存技术,降低数据库访问量,提高访问速度。业务场景比如:耗时较高的SQL,电话账单查询SQL等。实现方法:通过设置刷新时间间隔,由Mybatis每隔一段时间清空缓存,根据时间变化频率设置时间刷新间隔flushInterval,比如设置60分钟,24小时等,根据需求而定。
局限性:
Mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品的信息访问量大,但是要求用户每次都能查询到最新的商品信息,此时如果使用Mybatis的二级缓存,就无法实现当一个商品更新时只刷新该商品缓存信息而不刷新其他商品信息,因为Mybatis的二级缓存区域以Mapper为单位划分,当一个商品信息变化时,所有的商品信息都会清空,解决此类问题需要在业务层根据需求对数据有针对性缓存。