现在我们有 Author 作者类,其属性中有 List<Book> bookList 用来表示该作者所出版的书籍。我们配置好映射关系之后,希望在获取到一个 Author 类时,其 bookList 的值只有当我们在用到的时候才会去数据库获取,而不是每次都给我们全部的完整数据,这也就是MyBatis延迟加载要解决的问题:即映射集合内存在级联时,我们实际需要的数据少于数据库查出来的数据。这就会造成数据库多查出来的数据派不上用场,同时也加大了数据库负担。
我们在提到 MyBatis 的内联时,必然会涉及到的就是两个元素 association 和 collection,在之前的阐述中我们也说过,对于内联查询的方式有一种利用到了 select 属性,也即是我们延迟加载的主人公。我们说使用 select 实际上是将SQL语句分成了两部分单独执行,而不是采用联表查询的SQL,也正是这样,能让我们实现延迟加载。
在MyBatis中实现延迟加载有两种方式,如果你希望全局开启延迟加载,那么你需要在mybatis-config.xml全局配置文件的<settings>中加入下面这两个配置:
<!--开启全局延迟加载(懒加载)-->
<setting name="lazyLoadingEnabled" value="true" />
<!--任何方法调用均加载该对象所有属性-->
<setting name="aggressiveLazyLoading" value="false"/>
4
1
<!--开启全局延迟加载(懒加载)-->
2
<setting name="lazyLoadingEnabled" value="true" />
3
<!--任何方法调用均加载该对象所有属性-->
4
<setting name="aggressiveLazyLoading" value="false"/>
如果你并不想全局开启,只是针对某个类的某些属性进行延迟加载开启,那么则需要你在 resultMap 中的 association 或 collection 设置属性值 fetchType="lazy",如下示例:
<association fetchType="lazy" property="author" column="author_id" javaType="dulk.learn.mybatis.pojo.Author" select="findAuthorById"/>
1
1
<association fetchType="lazy" property="author" column="author_id" javaType="dulk.learn.mybatis.pojo.Author" select="findAuthorById"/>
刚才我们提到了作者和书之间的一对多关系,下面我们就来以此为例看下相关的代码和配置。
Author类:
public class Author {
private long id;
private String name;
private int age;
private List<Book> bookList;
//... getter and setter
}
8
1
public class Author {
2
private long id;
3
private String name;
4
private int age;
5
private List<Book> bookList;
6
7
//... getter and setter
8
}
Dao接口:
public interface AuthorDao {
Author findAuthorById(long id);
Book findBookByAuthorId(long authorId);
}
4
1
public interface AuthorDao {
2
Author findAuthorById(long id);
3
Book findBookByAuthorId(long authorId);
4
}
mapper.xml:
<mapper namespace="dulk.learn.mybatis.dao.AuthorDao">
<resultMap id="authorResultMap" type="dulk.learn.mybatis.pojo.Author">
<id property="id" column="id"/>
<result property="name" column="name" />
<result property="age" column="age" />
<!--采用select形式,设定fetchType延迟加载,column为关联查询的列-->
<collection fetchType="lazy" property="bookList" column="id" select="findBookByAuthorId" />
</resultMap>
<select id="findAuthorById" parameterType="long" resultMap="authorResultMap">
SELECT *
FROM author
WHERE id = #{id}
</select>
<select id="findBookByAuthorId" parameterType="long" resultType="dulk.learn.mybatis.pojo.Book">
SELECT *
FROM book
WHERE author_id = #{authorId}
</select>
</mapper>
23
1
<mapper namespace="dulk.learn.mybatis.dao.AuthorDao">
2
3
<resultMap id="authorResultMap" type="dulk.learn.mybatis.pojo.Author">
4
<id property="id" column="id"/>
5
<result property="name" column="name" />
6
<result property="age" column="age" />
7
<!--采用select形式,设定fetchType延迟加载,column为关联查询的列-->
8
<collection fetchType="lazy" property="bookList" column="id" select="findBookByAuthorId" />
9
</resultMap>
10
11
<select id="findAuthorById" parameterType="long" resultMap="authorResultMap">
12
SELECT *
13
FROM author
14
WHERE id = #{id}
15
</select>
16
17
<select id="findBookByAuthorId" parameterType="long" resultType="dulk.learn.mybatis.pojo.Book">
18
SELECT *
19
FROM book
20
WHERE author_id = #{authorId}
21
</select>
22
23
</mapper>
- 使用select方式加载内联属性的数据(而非联表查询)
- 设置全局延迟加载,或者局部设置 fetchType="lazy"
- column列为关联查询的列
写个单元测试看看:
public class TestAuthor {
@org.junit.Test
public void testMyBatis() throws IOException {
//读取配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取工厂类
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//获取SqlSession数据库会话对象
SqlSession sqlSession = factory.openSession();
//获取Dao
AuthorDao authorDao = sqlSession.getMapper(AuthorDao.class);
Author author = authorDao.findAuthorById(2);
System.out.println("-------------------------------------");
List<Book> bookList = author.getBookList();
}
}
19
1
public class TestAuthor {
2
3
junit.Test .
4
public void testMyBatis() throws IOException {
5
//读取配置文件
6
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
7
//获取工厂类
8
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
9
//获取SqlSession数据库会话对象
10
SqlSession sqlSession = factory.openSession();
11
//获取Dao
12
AuthorDao authorDao = sqlSession.getMapper(AuthorDao.class);
13
14
Author author = authorDao.findAuthorById(2);
15
System.out.println("-------------------------------------");
16
List<Book> bookList = author.getBookList();
17
}
18
19
}
如下输出日志,可以看到bookList在调用时才执行了SQL语句去查询:
Opening JDBC Connection
Created connection 1685232414.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@64729b1e]
==> Preparing: SELECT * FROM author WHERE id = ?
==> Parameters: 2(Long)
<== Columns: id, name, age
<== Row: 2, 李四, 18
<== Total: 1
-------------------------------------
==> Preparing: SELECT * FROM book WHERE author_id = ?
==> Parameters: 2(Long)
<== Columns: id, name, price, author_id
<== Row: 1, 小帽子历险记, 23, 2
<== Row: 2, 红帽子探险记, 12, 2
<== Total: 2
1
15
1
Opening JDBC Connection
2
Created connection 1685232414.
3
Setting autocommit to false on JDBC Connection com.mysql.jdbc.JDBC4Connection@64729b1e
4
==> Preparing SELECT * FROM author WHERE id = ?
5
==> Parameters 2(Long)
6
<== Columns id, name, age
7
<== Row 2, 李四, 18
8
<== Total1
9
-------------------------------------
10
==> Preparing SELECT * FROM book WHERE author_id = ?
11
==> Parameters 2(Long)
12
<== Columns id, name, price, author_id
13
<== Row 1, 小帽子历险记, 23, 2
14
<== Row 2, 红帽子探险记, 12, 2
15
<== Total2