延迟加载策略
1、背景
问题:在一对多的关系中,当我们有一个用户,它有100个账户。
在查询用户的时候,要不要把关联的100个账户查出来?
在查询账户的时候,要不要把关联的用户信息查出来?
思路:在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来
2、概念
延迟加载:在真正使用数据时才发起查询,不用的时候不查询,即按需加载(懒加载) lazy
立即加载:不管用不用,只要一调用方法,马上发起查询 eager
3、多表关系
在对应的四种表关系中:一对多、多对一、一对一、多对多
一对多、多对多:通常采用延迟加载
多对一、一对一:通常采用立即加载
4、使用方法
1、修改SqlMapConfig.xml配置文件中的settings设置
1)lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态,默认false
2)aggressiveLazyLoading:开启时,任一方法的调用都会加载该对象的所有延迟加载属性, 否则,每个延迟加载属性会按需加载, 3.4.1 版本后默认关闭(false)
2、修改映射文件
1)一对一(多对一):可以实现延迟加载,但是我们一般用立即加载
account配置文件:
<resultMap id="accountMap" type="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" javaType="user" column="uid" select="q.perter.dao.IUserDao.findOne" fetchType="eager">
</association>
</resultMap>
<select id="findAll" resultMap="accountMap">
select * from account
</select>
user配置文件:
<select id="findOne" resultType="user" parameterType="Integer">
select * from user where id = #{id}
</select>
association标签属性:
property:实体类中的属性
javaType:查询结果封装到的具体类(全限定类名)
column:将要执行查询的语句对应的参数
select:将要执行查询的语句(全限定类名+方法名)
fetchType:加载方式:eager(立即加载)、lazy(懒加载)
2)一对多(多对多):
user配置文件:
<resultMap id="userAccountMap" 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" column="id" select="q.perter.dao.IAccountDao.findAccountByUId">
</collection>
</resultMap>
<select id="findAll" resultMap="userAccountMap">
select * from user
</select>
account配置文件:
<select id="findAccountByUId" resultType="account" parameterType="Integer">
select * from account where uid = #{uid}
</select>
3、测试
延迟加载(一对多)测试:
当只调用方法:List<User> users = userDao.findAll();时,mybatis只进行一次查询,即查询所有user对象
当使用users列表中的某一个对象时,则会再次执行查询
立即加载(一对一)测试:
当调用方法:List<Account> accounts = accountDao.findAll();时,mybatis只进行了一次查询,但是执行了多条语句
缓存
1、定义
缓存是指存在于内存中的临时数据
使用缓存可以减少和数据库的交互次数,提高执行效率
2、范围
适用缓存的条件
1)经常查询并且不经常改变的
2)数据的正确与否对最终结果影响不大的
不适用于缓存的情况
1)经常改变的数据
2)数据的正确与否对最终结果影响很大的,如:商品的库存,银行的汇率,股市的牌价
一级缓存
它指的是Mybatis中SqlSession对象的缓存
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用
当SqlSession对象消失时,mybatis的一级缓存也就消失
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
二级缓存
它指的是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存
开启二级缓存
1)让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
cacheEnabled:全局性地开启或关闭所有映射器配置文件中已配置的任何缓存,默认true
2)让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
<cache></cache>:开启user支持二级缓存
3)让当前的操作支持二级缓存(在select标签中配置)
<select id="findOne" resultType="user" parameterType="Integer" useCache="true">
select * from user where id = #{id}
</select>
在<select>标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false
注意
1、针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存
2、当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象
测试
同一个factory,创建第一个的sqlSession,查询完成后,关闭第一个sqlSession,然后创建第二个sqlSession,再次查询进行打印测试
测试结果发现:
1、第一次查询是查询的数据库,第二次查询读取的是二级缓存
2、两次查询所得到的内存地址不一致,说明二级缓存只是序列化存储了对象中的数据,当再次查询时,将数据封装为新的对象返回
注解开发
1、常用注解
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
2、常用注解功能
user表:
--Select、Results(many)
@Select("select * from user")
@Results(id = "userAccountMap", value = {
@Result(id = true, column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "sex", property = "sex"),
@Result(column = "birthday", property = "birthday"),
@Result(column = "address", property = "address"),
@Result(property = "accounts", column = "id",
many = @Many(select = "q.perter.dao.IAccountDao.findByUserId", fetchType = FetchType.LAZY))
})
List<User> findAll();
--Insert、SelectKey
@Insert("insert into user(username, sex, birthday, address) values(#{username}, #{sex}, #{birthday}, #{address} )")
@SelectKey(keyProperty = "id", keyColumn = "id", before = false, statement="select last_insert_id()", resultType = Integer.class)
void saveUser(User user);
--Update
@Update("update user set username = #{username}, sex = #{sex}, birthday = #{birthday}, address = #{address} where id = #{id}")
void updateUser(User user);
--Delete
@Delete("delete from user where id = #{uid} ")
void deleteUser(Integer id);
--Select、ResultMap
@Select("select * from user where id = #{uid}")
@ResultMap("userAccountMap")
User findById(Integer id);
--Select
@Select("select * from user where id = #{uid}")
User findOne(Integer id);
--Select、ResultMap
@Select("select * from user where username like #{username}")
@ResultMap("userAccountMap")
List<User> findUserByName(String userName);
--Select、ResultMap
@Select("select * from user where username like '%${value}'")
@ResultMap("userAccountMap")
List<User> findUserByName2(String userName);
--Select
@Select("select count(id) from user")
Integer findTotal();
账户表:
--Select、Results(one)
@Select("select * from account")
@Results(id="accountUserMap", value = {
@Result(id=true, property = "id", column = "id"),
@Result(property = "uid", column = "uid"),
@Result(property = "money", column = "money"),
@Result(property = "user", column = "uid",
one = @One(select = "q.perter.dao.IUserDao.findOne", fetchType = FetchType.EAGER))
})
List<Account> findAll();
3、注解标签解析
注解中的标签都比较容易理解,只有在使用注解实现复杂关系映射时相对复杂,详细解释如下:
1)@Results 注解
代替的是标签<resultMap>
属性:id,用来标识当前注解,其他方法想要使用时,可以直接通过@ResultMap("userAccountMap")使用
该注解中可以使用单个@Result 注解,也可以使用@Result 集合@Results({@Result(),@Result()})或@Results(@Result())
2)@Result 注解
代替了 <id>标签和<result>标签
@Result 中 属性介绍:
id:是否是主键字段
column:数据库的列名
property:需要装配的属性名(类实体中的属性名)
one:需要使用的@One 注解(@Result(one=@One)()))
many:需要使用的@Many 注解(@Result(many=@many)()))
3)@One 注解(一对一)
代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象
@One 注解属性介绍:
select:指定用来多表查询的 sqlmapper,即map的key(接口的全限定类名+方法名)
fetchType:会覆盖全局的配置参数 lazyLoadingEnabled
使用格式:
@Result(column="",property="",one=@One(select=""))
4)@Many 注解(多对一)
代替了<Collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合
注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义
使用格式:
@Result(property="",column="",many=@Many(select=""))