「Mybatis系列」Mybatis高级应用

1. 关联查询

举例:因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发,关联查询用户信息为一对一查询。如果从用户信息出发,查询用户下的订单信息则为一对多查询,因为一个用户可以下多个订单。

1.1 一对一查询

需求

查询所有订单信息,关联查询下单用户信息。

SQL语句

主信息:订单表

从信息:用户表

SELECT 

  orders.*,

  user.username,

  user.address

FROM

  orders LEFT JOIN user 

  ON orders.user_id = user.id

方法一:resultType

返回resultType方式比较简单,也比较常用,就不做介绍了。

方法二:resultMap

使用resultMap进行结果映射,定义专门的resultMap用于映射一对一查询结果。

创建扩展po类

创建OrdersExt类(该类用于结果集封装),加入User属性,user属性中用于存储关联查询的用户信息,因为订单关联查询用户是一对一关系,所以这里使用单个User对象存储关联查询的用户信息。

public classOrdersExtextendsOrders{

    private User user;// 用户对象

    // get/set。。。。

}

Mapper映射文件

在UserMapper.xml中,添加以下代码:

property:Orders对象的user属性

javaType:user属性对应 的类型

-->

SELECT

o.id,

o.user_id,

o.number,

o.createtime,

o.note,

u.username,

u.address

FROM

orders o

JOIN `user` u ON u.id = o.user_id

association:表示进行一对一关联查询映射

property:表示关联查询的结果存储在com.kkb.mybatis.po.Orders的user属性中

javaType:表示关联查询的映射结果类型

Mapper接口

在UserMapper接口中,添加以下接口方法:

publicListfindOrdersAndUserRstMap()throwsException;

测试代码

在UserMapperTest测试类中,添加测试代码:

publicvoidtestfindOrdersAndUserRstMap()throwsException{

//获取session

SqlSession session = sqlSessionFactory.openSession();

//获限mapper接口实例

UserMapper userMapper = session.getMapper(UserMapper.class);

//查询订单信息

List list = userMapper.findOrdersAndUserRstMap();

System.out.println(list);

//关闭session

session.close();

}

小结

使用resultMap进行结果映射时,具体是使用association完成关联查询的映射,将关联查询信息映射到pojo对象中。

1.2 一对多查询

需求

查询所有用户信息及用户关联的订单信息。

SQL语句

主信息:用户信息

从信息:订单信息

SELECT

    u.*, 

    o.id oid,

    o.number,

    o.createtime,

    o.note

FROM

    `user` u

LEFT JOIN orders o ON u.id = o.user_id

分析

在一对多关联查询时,只能使用resultMap进行结果映射:

1、一对多关联查询时,sql查询结果有多条,而映射对象是一个。

2、resultType完成结果映射的方式的一条记录映射一个对象。

3、resultMap完成结果映射的方式是以[主信息]为主对象,[从信息]映射为集合或者对象,然后封装到主对象中。

修改po类

在User类中加入List orders属性。

Mapper映射文件

在UserMapper.xml文件中,添加以下代码:

SELECT

u.*,

o.id oid,

o.number,

o.createtime,

o.note

FROM

`user` u

LEFT JOIN orders o ON u.id = o.user_id

Collection标签:定义了一对多关联的结果映射。

property="orders":关联查询的结果集存储在User对象的上哪个属性。

ofType="orders":指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。

Mapper接口

// resultMap入门

publicListfindUserAndOrdersRstMap()throwsException;

测试代码

@Test

publicvoidtestFindUserAndOrdersRstMap(){

SqlSession session = sqlSessionFactory.openSession();

UserMapper userMapper = session.getMapper(UserMapper.class);

List result = userMapper.findUserAndOrdersRstMap();

for(User user : result) {

System.out.println(user);

}

session.close();

}

2. 延迟加载

2.1 什么是延迟加载

MyBatis中的延迟加载,也称为懒加载,是指在进行关联查询时,按照设置延迟规则推迟对关联对象的select查询。延迟加载可以有效的减少数据库压力。

Mybatis的延迟加载,需要通过resultMap标签中的association和collection子标签才能演示成功。

Mybatis的延迟加载,也被称为是嵌套查询,对应的还有嵌套结果的概念,可以参考一对多关联的案例。

注意:MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的sql

2.2 延迟加载的分类

MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载、侵入式加载与深度延迟加载

直接加载:执行完对主加载对象的select语句,马上执行对关联对象的select查询。

侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的某个属性(该属性不是关联对象的属性)时,就会马上执行关联对象的select查询。

深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的select查询。

延迟加载策略需要在Mybatis的全局配置文件中,通过标签进行设置。

2.3 案例准备

查询订单信息及它的下单用户信息。

2.4 直接加载

通过对全局参数:lazyLoadingEnabled进行设置,默认就是false。

    

   

 

    2.5 侵入式延迟加载

  

    

    

     

 

2.6 深度延迟加载

   

   

    

     

 

2.7 N+1问题

深度延迟加载的使用会提升性能。

如果延迟加载的表数据太多,此时会产生N+1问题,主信息加载一次算1次,而从信息是会根据主信息传递过来的条件,去查询从表多次。

3. 动态SQL

动态SQL的思想:就是使用不同的动态SQL标签去完成字符串的拼接处理、循环判断。

解决的问题是:

在映射文件中,会编写很多有重叠部分的SQL语句,比如SELECT语句和WHERE语句等这些重叠语句,该如何处理

SQL语句中的where条件有多个,但是页面只传递过来一个条件参数,此时会发生问题。

3.1 if标签

综合查询的案例中,查询条件是由页面传入,页面中的查询条件可能输入用户名称,也可能不输入用户名称。

      

   SELECT * FROM user where 1=1     

          

              

      AND username like '%${user.username}%'      

          

      

  

注意:要做『不等于空』字符串校验。

3.2 where标签

上边的sql中的1=1,虽然可以保证sql语句的完整性:但是存在性能问题。Mybatis提供where标签解决该问题。

代码修改如下:

    

     SELECT * FROM user     

         

         

        

                  

             AND username like '%${user.username}%'           

            

         

       

 

3.3 sql片段

在映射文件中可使用sql标签将重复的sql提取出来,然后使用include标签引用即可,最终达到sql重用的目的,具体实现如下:

原映射文件中的代码:

  

    SELECT * FROM user         

        

           

                

          AND username like '%${user.username}%'        

             

               

      

 

将where条件抽取出来:

     

         

                 

     AND username like '%${user.username}%'     

            

   

使用include引用:

   

    

     SELECT * FROM user     

     

            

          

       

 

注意:

1、如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace,如下:

     

         

           

          AND username like '%${user.username}%'      

         

         

     0">     

           

                

      

                 

               

  

          close=" ) " separator=",">      

          #{id}      

         

       

  

测试代码

在UserMapperTest测试代码中,修改testFindUserList方法,如下:

@Test

publicvoidtestFindUserList()throwsException{

SqlSession sqlSession = sqlSessionFactory.openSession();

// 获得mapper的代理对象

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

// 创建QueryVo对象

QueryVo queryVo =newQueryVo();

// 创建user对象

User user =newUser();

user.setUsername("老郭");

queryVo.setUser(user);

List ids =newArrayList();

ids.add(1);// 查询id为1的用户

ids.add(10);// 查询id为10的用户

queryVo.setIds(ids);

// 根据queryvo查询用户

List list = userMapper.findUserList(queryVo);

System.out.println(list);

sqlSession.close();

}

注意事项

如果parameterType不是POJO类型,而是List或者Array的话,那么foreach语句中,collection属性值需要固定写死为list或者array。

以上内容都是我自己的一些感想,分享出来欢迎大家指正,顺便求一波关注

作者:双哥

出处:https://juejin.im/post/5e4f33526fb9a07c9645920e

你可能感兴趣的:(「Mybatis系列」Mybatis高级应用)