【Mybatis源码分析 11】多表查询一对一、多对一、一对多、多对多,@One和@Many实现分步查询,fetchType控制延迟加载

目录

一对一查询

一对一立即加载

一对一延迟加载

一对多查询

一对多立即加载

一对多延迟加载

 

一对一查询

当某个字段比较占内存,但不常用时,比如存储一本书book,分很多章节,章节内容不需要在浏览的时候就查询获取到,只需要章节标题标识即可,这中情况下多进行分表存储,采用分布查询的方式获取章节内容。

数据库准备:bookbody 记录章节标题(title)和章节内容(booy),book记录章节(chapter)、章节标题(title)、该章节是否免费(isfree)和该章节内容是否下载(loaded)

#建表
CREATE TABLE bookbody (
title VARCHAR(255) NOT NULL,
body TEXT NOT NULL
)


#添加数据
INSERT bookbody(title, body) 
values
('章节标题1','第一章章节内容...'),
('章节标题2','第二章章节内容...'),
('章节标题3','第三章章节内容...'),
('章节标题4','第四章章节内容...'),
('章节标题5','第五章章节内容...')

#建表
CREATE TABLE book (
chapter VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
isfree enum('0','1') DEFAULT '0',
loaded enum('0','1') DEFAULT '0'
)

#添加数据
INSERT book(chapter,title,isfree,loaded) 
values
('第一章','章节标题1','1','1'),
('第二章','章节标题2','1','0'),
('第三章','章节标题3','0','0'),
('第四章','章节标题4','0','0'),
('第五章','章节标题5','0','0')

poji对象类定义:Book、BookBody

package Entity;

public class Book {
    
    private String chapter;
    private String title;
    private Boolean isfree;
    private Boolean loaded;

    // getter setter toString
}
package Entity;

public class BookBody {
    
    private String title;
    private String body;

    // getter setter toString
}

一对一立即加载

一对一立即加载可以一步联合查询

    @Select("select book.*, bookbody.body from book, bookbody where chapter = #{chapter} and book.title = bookbody.title")
    @Results(id="bookMap", value={   //相当于mapper xml文件中的resultMap, 将数据库查询到的字段对实体类属性封装
            @Result(column="chapter", property="chapter"),
            @Result(column="isfree", property="isfree"),
            @Result(column = "loaded",property = "loaded"),
            // 对bookbody属性封装数据
            @Result(column="title", property="bookbody.title"),  
            @Result(column="body", property="bookbody.body")
    })
    Book selectByChapterFrombook(String chapter) ;

或使用@One注解分步查询

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface One {
    String select() default "";

    FetchType fetchType() default FetchType.DEFAULT;
}

select引用第二步查询的sql接口方法

fetchType控制延迟加载(即第二步查询立即查询还是等到访问第二步查询结果时才开始查询)

fetchType设置FetchType.EAGER表示立即加载

    @Select("select * from bookbody where title = #{title}")
    BookBody selectByTitleFrombookbody(String title) ;

    @Select("select * from book where chapter = #{chapter}")
    @Results(id="bookMap", value={
            @Result(column="chapter", property="chapter"),
            @Result(column="title", property="bookbody", one=@One(select = "DAO.BookDAO.selectByTitleFrombookbody",fetchType = FetchType.EAGER)),
            @Result(column="isfree", property="isfree"),
            @Result(column = "loaded",property = "loaded")
    })
    Book selectByChapterFrombook(String chapter) ;

测试

    @Test
    public void test() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        BookDAO mapper = sqlSession.getMapper(BookDAO.class);

        Book book = mapper.selectByChapterFrombook("第二章");
        System.out.println(book.getChapter());
        System.out.println(book.getIsfree());
        System.out.println(book.getLoaded());
        System.out.println("============================");
        System.out.println(book.getBookbody().getBody());
        
        sqlSession.commit();
        sqlSession.close();
    }
17:03:49.637 [main] DEBUG DAO.BookDAO.selectByChapterFrombook - ==>  Preparing: select * from book where chapter = ? 
17:03:49.670 [main] DEBUG DAO.BookDAO.selectByChapterFrombook - ==> Parameters: 第二章(String)
17:03:49.720 [main] DEBUG DAO.BookDAO.selectByChapterFrombook - <==      Total: 1
17:03:49.721 [main] DEBUG DAO.BookDAO.selectByTitleFrombookbody - ==>  Preparing: select * from bookbody where title = ? 
17:03:49.722 [main] DEBUG DAO.BookDAO.selectByTitleFrombookbody - ==> Parameters: 章节标题2(String)
17:03:49.729 [main] DEBUG DAO.BookDAO.selectByTitleFrombookbody - <==      Total: 1
第二章
true
false
第二章章节内容...

一对一延迟加载

延迟加载必须分布查询

在查询book中的章节时,我们如果是需要浏览章节标题,等需要查看章节内容时再执行分布查询的下一步的情况下,可以通过fetchType控制是否使用延迟加载,注意需要先再mybatis全局配置文件中设置开启延迟加载

<!--    设置-->
    <settings>
<!--        是否开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>

    </settings>

然后在@One设置fetchType=FetchType.LAZY使用延迟加载

    @Select("select * from bookbody where title = #{title}")
    BookBody selectByTitleFrombookbody(String title) ;

    @Select("select * from book where chapter = #{chapter}")
    @Results(id="bookMap", value={
            @Result(column="chapter", property="chapter"),
            @Result(column="title", property="bookbody", 
                    one=@One(select = "DAO.BookDAO.selectByTitleFrombookbody",fetchType = FetchType.LAZY)),
            @Result(column="isfree", property="isfree"),
            @Result(column = "loaded",property = "loaded")
    })
    Book selectByChapterFrombook(String chapter) ;

从结果打印中可以看到,第二步查询直到调用第一步查询的结果book调用getBookbody()方法时才执行,这里体现的延迟加载

17:48:15.193 [main] DEBUG DAO.BookDAO.selectByChapterFrombook - ==>  Preparing: select * from book where chapter = ? 
17:48:15.232 [main] DEBUG DAO.BookDAO.selectByChapterFrombook - ==> Parameters: 第二章(String)
17:48:15.291 [main] DEBUG DAO.BookDAO.selectByChapterFrombook - <==      Total: 1
第二章
true
false
============================
17:48:15.292 [main] DEBUG DAO.BookDAO.selectByTitleFrombookbody - ==>  Preparing: select * from bookbody where title = ? 
17:48:15.293 [main] DEBUG DAO.BookDAO.selectByTitleFrombookbody - ==> Parameters: 章节标题2(String)
17:48:15.302 [main] DEBUG DAO.BookDAO.selectByTitleFrombookbody - <==      Total: 1
第二章章节内容...


一对多查询

多表查询中一对多和多对多查询一般借助中间表记录对应关系

比如在购物商城系统中,存在用户账户信息表account和商品信息表commodity, 一个用户可以购买多样商品,假设我们查询用户"huahua"购买的商品,应该通过中间表user_commodity查到"huahua"购买了衣服、裤子和鞋子这三个商品,这种是多表查询中的一对多的情况。

用户账户信息表account

商品信息表commodity

中间表account_commodity

pojo类准备

package Entity;

public class Commodity {

    private Integer id;
    private String name;
    private Double price;

    // ...
}
package Entity;

import java.util.Arrays;

public class Account {

    private Integer id;
    private String name;
    private String sex;
    private Commodity[] commodities;   //一个用户可以购买多件商品

    // ...
}

一对多立即加载

一对多需要分步查询,比如这里对"huahua"的购买商品需要在第二步查询时通过Commodity对象接收

使用@Many注解接收第二步查询得到的多条数据

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Many {
    String select() default "";

    FetchType fetchType() default FetchType.DEFAULT;
}

同样的fetchType设置FetchType.EAGER表示立即加载

    @Select("select c.*  from commodity c, account_commodity ac where ac.aid = #{id} and ac.cid = c.cid")
    @Results(id="commodityMap",value = {
            @Result(column = "cid", property = "id"),
            @Result(column = "cname", property = "name"),
            @Result(column = "cprice", property = "price")
    })
    public Commodity findById(String id);
    
    
    @Select("select * from account a where a.aname = #{name}")
    @Results(id="accountMap",value = {
            @Result(column = "aid", property = "id"),
            @Result(column = "aname", property = "name"),
            @Result(column = "asex", property = "sex"),
            @Result(column = "aid", property = "commodities", many = @Many(select = "DAO.AccountDAO.findById",fetchType = FetchType.EAGER))
    })
    public Account findByName(String name);

测试

    @Test
    public void test() throws IOException {

        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        AccountDAO mapper = sqlSession.getMapper(AccountDAO.class);

        Account account = mapper.findByName("huahua");
        System.out.println("-----------------------------------------------");
        System.out.println(account.getId());
        System.out.println(account.getName());
        System.out.println(account.getSex());
        for(Commodity c : account.getCommodities())
            System.out.println(c);
        System.out.println("-----------------------------------------------");


        sqlSession.commit();
        sqlSession.close();
    }
20:17:51.371 [main] DEBUG DAO.AccountDAO.findByName - ==>  Preparing: select * from account a where a.aname = ? 
20:17:51.409 [main] DEBUG DAO.AccountDAO.findByName - ==> Parameters: huahua(String)
20:17:51.434 [main] DEBUG DAO.AccountDAO.findById - ====>  Preparing: select c.* from commodity c, account_commodity ac where ac.aid = ? and ac.cid = c.cid 
20:17:51.439 [main] DEBUG DAO.AccountDAO.findById - ====> Parameters: 1(String)
20:17:51.441 [main] DEBUG DAO.AccountDAO.findById - <====      Total: 3
20:17:51.441 [main] DEBUG DAO.AccountDAO.findByName - <==      Total: 1
-----------------------------------------------
1
huahua
女
Commodity{id=2, name='裤子', price=200.0}
Commodity{id=1, name='衣服', price=100.0}
Commodity{id=3, name='鞋子', price=300.0}
-----------------------------------------------

从打印日志中可以看到,两条sql语句紧接着执行

一对多延迟加载

同样的fetchType设置FetchType.LAZY表示延迟加载

    @Select("select c.*  from commodity c, account_commodity ac where ac.aid = #{id} and ac.cid = c.cid")
    @Results(id="commodityMap",value = {
            @Result(column = "cid", property = "id"),
            @Result(column = "cname", property = "name"),
            @Result(column = "cprice", property = "price")
    })
    public Commodity findById(String id);


    @Select("select * from account a where a.aname = #{name}")
    @Results(id="accountMap",value = {
            @Result(column = "aid", property = "id"),
            @Result(column = "aname", property = "name"),
            @Result(column = "asex", property = "sex"),
            @Result(column = "aid", property = "commodities", many = @Many(select = "DAO.AccountDAO.findById",fetchType = FetchType.LAZY))
    })
    public Account findByName(String name);
20:25:42.535 [main] DEBUG DAO.AccountDAO.findByName - ==>  Preparing: select * from account a where a.aname = ? 
20:25:42.561 [main] DEBUG DAO.AccountDAO.findByName - ==> Parameters: huahua(String)
20:25:42.623 [main] DEBUG DAO.AccountDAO.findByName - <==      Total: 1
-----------------------------------------------
1
huahua
女
20:25:42.631 [main] DEBUG DAO.AccountDAO.findById - ==>  Preparing: select c.* from commodity c, account_commodity ac where ac.aid = ? and ac.cid = c.cid 
20:25:42.632 [main] DEBUG DAO.AccountDAO.findById - ==> Parameters: 1(String)
20:25:42.635 [main] DEBUG DAO.AccountDAO.findById - <==      Total: 3
Commodity{id=2, name='裤子', price=200.0}
Commodity{id=1, name='衣服', price=100.0}
Commodity{id=3, name='鞋子', price=300.0}
-----------------------------------------------

从打印日志中可以看到, 第二条sql语句在访问Account对象的commodities属性时才执行,这里体现延迟加载

 



多对一查询本质是进行多个一对一查询

多对多查询本质是进行多个一对多查询

这里就不举例了。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值