mybatis的延迟加载就是按需查询,在需要的时候进行查询。
有两张表:
图书表(book):
图书类型表(category):
他们之间通过类型id进行关联,现在我要显示图书类型名,点击类型名再显示该类型下的所有图书。
我们可以这样做在类型实体类里面添加一个属性存放该类型下的图书
public class Category {
private int cid;
private String cname;
private List<Book> books;
//省略get set
}
一次性的把图书类型和图书查询出来,Sql语句如下:
SELECT book.*,cname FROM book,category WHERE book.cid = category.cid
这样做可以完成功能,但是我们只是需要显示图书类型,点击的时候才显示该类型的图书,如果能做到开始只查询类型,点击类型的时候再查询该类型的图书,就不需要进行两表联查了,可以提高查询的效率,也比较节省内存,这就是延迟加载。
延迟加载如何实现?
1. Category实体类同上
2. UserDao.xml:
<mapper namespace="cn.xh.dao.UserDao">
<select id="findCategoryWithLazingload" resultMap="categoryMap">
select * from category
</select>
<resultMap id="categoryMap" type="cn.xh.pojo.Category">
<id column="cid" property="cid"></id>
<result column="cname" property="cname"></result>
<collection property="books" column="cid" select="findBookWithLazy"></collection>
</resultMap>
<select id="findBookWithLazy" parameterType="int" resultType="cn.xh.pojo.Book">
select * from book where cid = #{cid}
</select>
</mapper>
只有我们点击类型的时候才需要查询该类型下的图书,所以这里我们没有用两表联查,而是将类型表的查询语句和图书表的查询语句分开。
重点来看下这个配置:
<collection property="books" column="cid" select="findBookWithLazy"></collection>
collection,association是支持延迟加载的,这里的select属性表示要执行的sql语句,column表示执行sql语句要传的参数,该参数为select * from category查询出来的字段cid,property=”books”表示查询出来的结果给到books属性。
3. 在mybatis的核心配置文件中配置:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
注意,这个配置必须写在properties配置的后面,typeAliases的前面。
将lazyLoadingEnabled设置为true表示开启延迟加载,默认为false.
将aggressiveLazyLoading设置为false表示按需加载,默认为true
关于这两个设置后面再详加探讨。
4. 测试:
@Test
public void testDo(){
SqlSession session = sqlSessionFactory.openSession();
UserDao u = session.getMapper(UserDao.class);//用动态代理的方式自动生成接口的实现类 List<Category> lst = u.findCategoryWithLazingload();
for (Category c : lst) {
System.out.println(c.getCname());
}
session.close();
}
执行看看日志:
这段代码只涉及到了图书类型,并未涉及到图书部分,所以只执行了select * from category从类型表中查询出类型信息。
再来测试这段代码:
@Test
public void testDo1(){
SqlSession session = sqlSessionFactory.openSession();
UserDao u = session.getMapper(UserDao.class);//用动态代理的方式自动生成接口的实现类 List<Category> lst = u.findCategoryWithLazingload();
for (Category c : lst) {
System.out.println(c.getCname());
}
List<Book> lstBook = lst.get(0).getBooks();
session.close();
}
执行看看日志:
这里执行了两个sql语句:
Select * from category
Select * from book where cid = ?
对比这两段代码,可以看到, 只有当执行List<Book> lstBook = lst.get(0).getBooks();这行代码的时候才会去执行sql语句Select * from book where cid = ?。
这就是延迟加载里的按需执行sql语句,只有在需要的时候才会去执行。
回顾一下第三个步骤中的配置:
此时 lazyLoadingEnabled设置为true, aggressiveLazyLoading设置为false,表示延迟加载开启,按需加载也开启。分析一下在这种配置下代码的每一步都做了什么:
1.List<Category> lst = u.findCategoryWithLazingload();执行到这行代码的时候从数据库中查询图书类型的信息。
2.System.out.println(c.getCname());执行这行代码的时候因为图书类型信息已经被查询出来,所以不需要再和数据库交互。
3.List<Book> lstBook = lst.get(0).getBooks();执行这行代码的时候从数据库中查询该类型下图书的信息。
如果将lazyLoadingEnabled设置为true, aggressiveLazyLoading设置为true,表示延迟加载开启,按需加载关闭,代码每一步都做了什么呢?
1.List<Category> lst = u.findCategoryWithLazingload();执行到这行代码的时候从数据库中查询图书类型的信息。
2.System.out.println(c.getCname());执行这行代码的时候,需要加载图书类型的属性“类型名”,因为将按需加载关闭,所以此时会把Category的所有属性都加载进来,包括List<Book> books,会去数据库中查询图书的信息。
3.List<Bosok> lstBook = lst.get(0).getBooks();执行这行代码的时候因为图书的信息已经被加载进来,不需要查询数据库。
如果将lazyLoadingEnabled设置为false,相当于关闭了延迟加载,此时无论aggressiveLazyLoading是true还是false都会在执行
List<Category> lst = u.findCategoryWithLazingload();
的时候将类型和图书的信息都查询出来。
总结一下:
一:延迟加载就是按需加载,在需要查询的时候再去查询,使用延迟加载可以避免表连接查询,表连接查询比单表查询的效率低,但是它需要多次与数据库进行交互,所以延迟加载并不是银弹,使用需谨慎。
二:关于延迟加载有两个重要的设置:lazyLoadingEnabled表示延迟加载是否开启,如果设置为true表示开启,此时还需要设置aggressiveLazyLoading为false,才能做到按需加载,如果aggressiveLazyLoading设置为true则按需加载关闭,此时只要加载了某个属性就会将所有属性都加载。
lazyLoadingEnabled的默认值为false
aggressiveLazyLoading的默认值为true