1. Mybatis加载策略
1.1 什么是延迟加载
实际开发过程中很多时间我们并不需要总是在加载用户信息的同时加载他们的订单信息:譬如只对用户的个人信息感兴趣而不是他的订单信息。此时可以只加载其个人信息,订单信息延迟加载,在用到的时候才加载
延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。
- 延迟加载是基于嵌套查询来实现的
* 优点:
先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表
速度要快。
* 缺点:
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时
间,所以可能造成用户等待时间变长,造成用户体验下降。
* 在多表中:
一对多,多对多:通常情况下采用延迟加载
一对一(多对一):通常情况下采用立即加载
* 注意:
延迟加载是基于嵌套查询来实现的
1.2 实现延迟加载
在实现多表嵌套查询时,我们使用association和collection标签来封装实体类属性和集合属性。这两个标签都有一个fetchType属性,通过修改它的值可以实现修改局部的加载策略
局部延迟加载,范围是一个association或者collection
<!--
fetchType="lazy" 懒加载策略
fetchType="eager" 立即加载策略
-->
<collection property="orderList" ofType="order" column="id"
select="com.xxx.dao.OrderMapper.findByUid" fetchType="lazy">
</collection>
设置触发延迟加载的方法
即当调用该方法时执行查询操作
- 在配置了延迟加载策略后,即使没有调用关联对象的任何方法,但是在你调用当前对象的equals、clone、hashCode、toString方法时也会触发关联对象的查询。
- 可以在配置文件中使用lazyLoadTriggerMethods配置项覆盖掉上面四个方法。
<settings>
<!--调用toString时加载-->
<setting name="lazyLoadTriggerMethods" value="toString()"/>
</settings>
全局延迟加载
在Mybatis的核心配置文件中可以使用setting标签修改全局的加载策略。
<settings>
<!--开启全局延迟加载功能-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
2. Mybatis缓存
2.1 为什么使用缓存
- 当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存中。当用户再
次查询这些数据时,不用再通过数据库查询,而是去缓存里面查询。减少网络连接和数据库查询带来的损
耗,从而提高我们的查询效率,减少高并发访问带来的系统性能问题。
一句话概括:经常查询一些不经常发生变化的数据,使用缓存来提高查询效率。
- 像大多数的持久化框架一样,Mybatis也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。 Mybatis中缓存分为一级缓存,二级缓存。
2.2 一级缓存
- 一级缓存是SqlSession级别的缓存,是默认开启的
- 所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
- 一级缓存是SqlSession范围的缓存,执行SqlSession的C(增加)U(更新)D(删除)操作,或者调用clearCache()、commit()、close()方法,都会清空缓存。
设置每次查询时都清除缓存
<select id="" flushCache="true">
2.3 二级缓存
- 二级缓存是namspace级别(跨sqlSession)的缓存,是默认不开启的
二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 - 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置 就可以开启
二级缓存了。
<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
为true代表开启二级缓存;为false代表不开启二级缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>
注意:mybatis的二级缓存因为是namespace级别,所以在进行多表查询时会产生脏读问题
3.Mybatis注解
注解直接写在java文件中,可以代替Mapper映射文件
常用注解
-
@Insert:实现新增,代替了< insert>< /insert>
-
@Delete:实现删除,代替了< delete>< /delete>
-
@Update:实现更新,代替了< update>< /update>
-
@Select:实现查询,代替了< select>< /select>
-
@Result:实现结果集封装,代替了< result>< /result>
-
@Results:可以与@Result 一起使用,封装多个结果集,代替了< resultMap>< /resultMap>
-
@One:实现一对一结果集封装,代替了< association>< /association>
-
@Many:实现一对多结果集封装,代替了< collection>< /collection>
3.2 使用注解实现增删改查
public interface UserMapper {
//查询操作
@Select("select * from user")
public List<User> findAll();
//添加操作
@Insert("insert into user (username,birthday,sex,address) values (#{username,birthday,sex,address})")
public void save(User user);
//修改操作
@Update("update user set username = #{username},birthday = #{birthday},sex = #{sex},address = #{address} where id = #{id}")
public void update(User user);
//删除操作
@Delete("delect from user where id = #{id}")
public void delete(Integer id);
}
核心配置文件
<!-- 指定扫描包含映射关系的接口所在的包 -->
<mappers>
<!--扫描使用注解的Mapper类所在的包-->
<package name="com.xxx.mapper"></package>
</mappers>
3.3 使用注解完全复杂映射
在映射文件中通过配置 <resultMap>、<association>、<collection>
来实现复杂关系映射。
使用注解开发后,可以使用 @Results、@Result,@One、@Many 注解组合完成复杂关系的配置。
3.4 一对一查询
查询一个订单,与此同时查询出该订单所属的用户
a)OrderMapper接口
public interface OrderMapper {
@Select("SELECT * FROM orders")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "ordertime", property = "ordertime"),
@Result(column = "money", property = "money"),
@Result(property = "user", javaType = User.class,
column = "uid", one = @One(select =
"com.xxx.mapper.UserMapper.findById", fetchType = FetchType.EAGER))
})
public List<Order> findAllWithUser();
}
b)UserMapper接口
public interface UserMapper {
@Select("SELECT * FROM `user` WHERE id = #{id}")
public User findById(Integer id);
}
3.5 一对多查询
UserMapper接口
public interface UserMapper {
@Select("SELECT * FROM `user`")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "brithday", property = "brithday"),
@Result(column = "sex", property = "sex"),
@Result(column = "address", property = "address"),
@Result(property = "orderList", javaType = List.class,
column = "id" ,
many = @Many(select = "com.xxx.mapper.OrderMapper.findByUid"))
})
public List<User> findAllWithOrder();
}
3.6 多对多查询
UserMapper接口
public interface UserMapper {
@Select("SELECT * FROM `user`")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "brithday", property = "brithday"),
@Result(column = "sex", property = "sex"),
@Result(column = "address", property = "address"),
@Result(property = "roleList", javaType = List.class,
column = "id" ,
many = @Many(select = "com.xxx.mapper.RoleMapper.findByUid"))
})
public List<User> findAllWithRole();
}
- 注解开发和xml配置优劣分析
1.注解开发和xml配置相比,从开发效率来说,注解编写更简单,效率更高。
2.从可维护性来说,注解如果要修改,必须修改源码,会导致维护成本增加。xml维护性更强。