Mybatis 在解决一对多或者多对多的时候,共有两种解决方式:
- 嵌套查询
- 嵌套结果
嵌套查询:即将一条SQL中的多个子查询,分成多个SQL进行查询的方式;
嵌套结果:即使用一条SQL,在 SQL 中使用子查询的方式来完成。
官方推荐使用嵌套结果的方式,可以减少与 SQL 的多次连接,减少资源消耗等;
并且不推荐使用嵌套查询的方式,因为存在 N+1
问题。这个问题,无论是 <association>
标签还是 <collection>
元素都会遇到。
接下来本文以 <association>
标签为例来介绍一下 MyBatis 的延迟加载 & N+1问题。
现在有两个表,分别是db_user
和db_score
。我们需要通过 id 与 user_id 字段关联,来查询学生的分数信息。如下图所示:
为了解决这个问题,我们可以采取两种方法:
1.第一种方法是使用一条SQL语句,把学生信息及其分数信息一次性地查询出来。
我们常用的 <resultMap>
+ <association>
形式用的就是这种方法,我们也推荐这样子的使用。
<resultMap id="ScoreResultMap" type="com.springboot.entity.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="age" property="age"/>
<result column="address" property="address"/>
<collection property="score" javaType="com.springboot.entity.Score">
<id column="user_id" property="userId"/>
<result column="course" property="course"/>
<result column="score" property="score"/>
</collection >
</resultMap>
<select id="queryUserScore" resultMap="ScoreResultMap">
select
t1.id,
t1.username,
t1.age,
t1.address,
t2.course,
t2.score
from
db_user t1
left join db_score t2 on t1.id = t2.user_id
</select>
执行结果:
2. 第二种方法是使用MyBatis 的延迟加载机制。
延迟加载机制需要在 mybatis-config.xml 核心配置文件 <settings>
标签下添加如下配置。
<settings>
<!-- 1.延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false -->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
mapper.xml
<resultMap id="ScoreResultMap" type="com.springboot.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="age" column="age"/>
<result property="address" column="address"/>
<!--property:A表字段 javaType:B表 column:A表要关联B表的字段(A表字段) select:B表查询 SQL的 id -->
<collection
property="score"
javaType="com.springboot.entity.Score"
column="id"
select="queryScore">
</collection>
</resultMap>
<!-- A表 SQL -->
<select id="queryUser" resultMap="ScoreResultMap">
select
id, username, age, address
from
db_user
</select>
<!-- B表 SQL -->
<select id="queryScore" parameterType="int" resultType="com.springboot.entity.Score">
select
user_id, course, score
from
db_score
where
user_id = #{userId}
</select>
执行如下代码:
@Test
public void testQueryUserScore() throws Exception {
//配置文件所在路径
String resource = "mybatis/mybatis-config.xml";
//读取MyBatis配置文件,转换成InputStream
InputStream readerStream = Resources.getResourceAsStream(resource);
//创建一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(readerStream);
//创建一个SqlSession会话
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
//使用getMapper()方法,执行操作。
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUser(3);
//如果开启了延迟加载机制,会在使用的时候才发出SQL去查询
System.out.println("id:"+user.getId());
System.out.println("---------------------------");
System.out.println("score:"+user.getScore());
} finally {
sqlSession.close();
}
}
执行结果:
备注:
在 settings 配置中,还有这么一个属性: aggressiveLazyLoading,当开启时,任何方法(包括equals、clone、hashCode、toString 等方法)的调用都会加载该对象的所有属性。默认为 false。
<settings>
<!-- 2.当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
执行结果:
延迟加载原理:动态代理
如图所示:
Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST
,我们也可以自行设置使用 其他的动态代理机制,比如:cglib。
在 settings 配置中,通过配置属性: proxyFactory,我们对其进行配置即可。(此处切记:需要导入Cglib Jar包)
<settings>
<!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
<setting name="proxyFactory" value="CGLIB" />
</settings>
博主写作不易,来个关注呗
求关注、求点赞,加个关注不迷路 ヾ(◍°∇°◍)ノ゙
博主不能保证写的所有知识点都正确,但是能保证纯手敲,错误也请指出,望轻喷 Thanks♪(・ω・)ノ