Mybatis中的延迟加载、缓存、注解开发
延迟加载
概念
1.Mybatis的延迟加载
问题:在一对多中,当我们有一个用户,它有100个账户。
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?
在查询用户时,用户下的账户信息应该是,什么时候用,什么时候查询的。
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。
(1)什么是延迟加载
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
(2)什么是立即加载
不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载
多对一,一对一:通常情况下我们都是采用立即加载
配置方式
SqlMapConfig.xml中配置
设置名 | 描述 | 有效值 |
---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true、 false(默认T) |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 | true 、false(默认F) |
<!--配置参数-->
<settings>
<!--配置mybatis支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
例子
SqlMapConfig.xml
<!--配置参数-->
<settings>
<!--配置mybatis支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
AccountDao
/**
* 根据用户id查询账户信息
* @return
*/
public List<Account> findAccountById(Integer uid);
AccountDao.xml
<resultMap id="accountMap" type="account">
<id property="aid" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</resultMap>
<select id="findAccountById" resultMap="accountMap" parameterType="java.lang.Integer">
select * from account where uid = #{uid}
</select>
UserDao
public interface UserDao {
public List<User> findAll();
}
UserDao.xml
<resultMap id="userMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="accounts" ofType="account" select="cn.lingnan.dao.AccountDao.findAccountById" column="id">
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
select * from user
</select>
在不使用的情况下执行的SQL语句:
在使用的情况下执行的SQL语句:
(在使用立即加载机制的情况下,无论怎么样都会去查询Account表)
(在使用延迟加载机制的情况下,只有调用需要用到Account表的功能才会发起查询)
缓存
缓存概念
(1)什么是缓存
存在于内存中的临时数据。
(2)为什么使用缓存
减少和数据库的交互次数,提高执行效率。
(3)什么样的数据能使用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。
不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,故事的牌价。
(4)Mybatis分为一级缓存和二级缓存
一级缓存(SqlSession中缓存)
测试语句
校验后发现,执行查询两次,只执行了一次sql语句
尝试清空缓存
执行后结果
测试后发现,当sqlsession关闭后或者手动清空缓存信息,才会再次执行sql语句执行查询
触发清空一级缓存的情况
测试代码
执行结果
结果:当数据库信息发生变化的时候sqlsession会清空缓存再次发起查询
二级缓存(SqlSessionFactory中缓存)
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存
测试代码
测试结果
结果:一级缓存消失,此时没有二级缓存的概念,也没有使用二级缓存
使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
设置名 | 描述 | 有效值 |
---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true 、false(默认T) |
第二步:让当前的映射文件支持二级缓存(在UserDao.xml中配置)
第三步:让当前的操作系统支持二级缓存(在select标签中配置)
第一步:在SqlMapConfig.xml中配置
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
第二步在UserDao.xml中配置
第三步在select标签中配置
测试结果:
在第二次查询中没有查询数据库,而是从缓存中获取
关于二级缓存与一级缓存生命周期
一级缓存
MyBatis一级缓存的生命周期和SqlSession一致。
MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺。
MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。
二级缓存
MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。
MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。
注解开发
mybatis中的注解开发
(1)环境搭建
(2)单表CRUD操作(代理Dao方式)
(3)多表查询操作
(4)缓存的配置
注意事项
在开发的过程中,如果同时采用xml配置和注解配置的情况下,执行会出现错误,修改sqlMapConfig也会出现错误。
解决方法:删除xml配置文件才可以执行成功
注解别名配置方式:
在注解中使用二级缓存
在Dao上添加