1、Mybatis如何解决循环依赖问题
mybatis的循环依赖,即是mapper.xml里面的A查询的resultMap包含了B属性(B属性是通过子查询得到的),而B属性中又包含了A(B查询的resultMap中又包含了A的查询),就会造成A-B-A的情况。
实际的代码样例如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mapper.BlogMapper">
<cache></cache>
<resultMap id="blogMap" type="com.entity.Blog" autoMapping="true">
<result column="title" property="title"></result>
<collection property="comments" column="id" select="selectCommentsByBlogId">
</collection>
</resultMap>
<resultMap id="commentMap" type="com.entity.Comment" autoMapping="true">
<result column="title" property="title"></result>
<association property="blog" column="blog_id" select="selectBlogById"></association>
</resultMap>
<select id="selectCommentsByBlogId" resultMap="commentMap">
select * from comment where blog_id = #{id}
</select>
<select id="selectBlogById" resultMap="blogMap">
select * from blog where id = #{id}
</select>
</mapper>
package com.entity;
import java.io.Serializable;
import java.util.List;
public class Blog implements Serializable {
private List<Comment> comments;
private String title;
public Blog(){
}
public List<Comment> getComments() {
return comments;
}
public void setComments(List<Comment> comments) {
this.comments = comments;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
package com.entity;
import java.io.Serializable;
import java.util.Date;
public class Comment implements Serializable {
private int id;
private String content;
private Date date;
private Blog blog;
public Blog getBlog() {
return blog;
}
public void setBlog(Blog blog) {
this.blog = blog;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
Mybatis中利用的是一级缓存、空占位符,和延迟加载来解决循环依赖问题的。
Mybatis 框架是一个轻量级的 ORM 框架,通常不会出现循环依赖问题。但是,如果在实际应用场景中出现了循环依赖的情况,Mybatis 也提供了一些解决方案,以下是其中两种:
-
使用延迟加载(Lazy Loading)特性
Mybatis 支持延迟加载,可以将对象的加载推迟到真正需要使用它时再进行加载,这样就可以避免一些不必要的依赖。具体实现方式是通过在 Mapper 文件中配置使用延迟加载的关联属性或集合属性。- 全局配置
<settings> <!-- 开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> </settings>
- 局部配置
在和标签中修改"fetchType"属性的值
fetchType = FetchType.EAGER 立即加载
fetchType = FetchType.LAZY 延迟加载
<association property="idCard" column="card_id" select="com.ycy.mapper.IdCardMapper.findById" fetchType="eager">
- 使用二级缓存(Second Level Cache)
Mybatis 的二级缓存是在 SqlSessionFactory 层面上的,可以共享多个 SqlSession 实例之间的缓存数据。在使用二级缓存时,需要对每个需要缓存的 Mapper 进行配置,将其标识为需要开启缓存的 Mapper。这样在查询数据时,先从缓存中获取数据,如果缓存中不存在,则执行 SQL 查询,并将查询结果放入缓存中。
需要注意的是,虽然 Mybatis 提供了延迟加载和二级缓存等解决方案,但是过度地依赖这些特性可能会增加系统的复杂度和调试难度,因此在实际使用中需要根据具体情况进行选择和权衡。同时,在设计数据库表结构和应用程序架构时,应尽量避免出现循环依赖的情况,以确保系统的稳定性和可维护性。
2、一级缓存
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的
过程分析
- 第一次查询id为1 的用户,此时先去一级缓存查找,如果查找不到,则去数据库查询,把查询后的 结果存储到一级缓存中。
- 第二次查询id为1 的用户,此时先去一级缓存查找,如果查找到,则直接从一级缓存中把数据取出,不去查询数据库。
- 只要中间发生增删改操作,那么一级缓存就清空,默认开启一级缓存。
3、二级缓存
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的
过程分析
- 第一次查询id为1 的用户,此时先去二级缓存查找,如果查找不到,则去数据库查询,把查询后的 结果存储到二级缓存中
- 第二次查询id为1 的用户,此时先去二级缓存查找,如果查找到,则直接从二级缓存中把数据取出,不去查询数据库
- 只要中间发生增删改操作,那么二级缓存就清空,二级缓存默认不开启,需要手动开启。
一级缓存和二级缓存的作用范围图
3.1、开启二级缓存
使用 Mybatis 的二级缓存需要进行以下配置:
- 开启全局缓存支持
在 mybatis-config.xml 配置文件中配置如下内容:
<configuration>
<settings>
<setting name="cacheEnabled" value="true" />
</settings>
</configuration>
其中,cacheEnabled 表示开启全局缓存支持。
- 配置 Mapper 接口开启缓存支持,在 Mapper.xml 文件中,可以针对每个 Mapper 接口进行配置,以标识某个 Mapper 接口需要使用二级缓存。例如:
<mapper namespace="com.example.mybatis.mapper.UserMapper">
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
<select id="getUserById" resultType="com.example.mybatis.model.User" useCache="true">
select * from user where id = #{id}
</select>
</mapper>
其中,cache 标签表示使用的缓存实现类,这里使用 Ehcache 作为缓存实现;useCache 属性表示是否启用二级缓存,这里设置为 true。
- 配置 Ehcache 缓存实现
如果使用 Ehcache 作为缓存实现,则需要在项目中引入 Ehcache 相关依赖,并在 Ehcache 的配置文件 ehcache.xml 中进行配置。关于 Ehcache 的详细配置和使用方法可以参考 Ehcache 官方文档。
以上就是使用 Mybatis 二级缓存的配置方法。需要注意的是,二级缓存虽然可以提高查询性能,但也会增加系统的复杂度,因此在实际使用中需要根据具体情况进行选择和权衡。同时,在设计数据库表结构和应用程序架构时,应尽量避免出现循环依赖的情况,以确保系统的稳定性和可维护性。