Mybatis-Plus:持久层数据的高效处理(新手笔记五)--多表查询的实现

🤚我的博客

  • 欢迎光临我的博客 https://blog.csdn.net/qq_52434217?type=blog

🥛前言

上一章将了多表映射的基础:手写SQL。文章发布之后才发现并没有讲完要讲的内容,比如在第一章就出现的映射图。不过还好是新手笔记,想来没有多少人看。这一章将补上这点知识,然后介绍多表映射如何实现。

📄多表映射的实现

🔷mapper关系映射文件

resultMap标签

在手写sql指定数据输出格式时,除了resultType属性之外还有resultMap属性。而resultMap属性与标签之间是相辅相成。resultMap标签是定义一个实体类与数据表映射关系的核心,规定了实体类的属性与数据表字段之间的对应关系。通过resultMap定义映射关系并对关系进行命名,就可以在resultMap属性接受映射关系名字所对应的结构。
![[Pasted image 20240229135137.png]]

id标签是设置主键列和逐渐属性之间的对应关系。
column属性指定字段名,property属性指定java实体类的属性名。
result标签设置普通字段和java实体类属性之间的关系。
在这里不再测试,读者知道即可。

🔷多表查询

为什么要先介绍映射关系,是因为在多表查询时得到的返回值是可以与一个实体类对应的,而如果我们仅仅使用之前的简单返回类型相当繁琐。所以为了简单和工程开发效率,在实际开发过程中直接定义一个resultMap用于接收返回值。多表查询需要考究的地方有很多,比如定义单独的多表映射resultMap,再比如多表查询的对一情况与对多情况。

模型准备

创建数据表

这里采用顾客与订单之间的映射关系做演示。

CREATE TABLE `t_customer` (`customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR(100), PRIMARY KEY (`customer_id`) );

CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR(100), `customer_id` INT, PRIMARY KEY (`order_id`) ); 

INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');

INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1'); 
生成模型

利用mybatis plus插件逆向生成模型,这里不用勾选hashCode等方法的重写。这里需要对实体类进行修改,以满足在订单类中包含客户实体类,同理需要在客户实体类中添加订单列表属性。

TOrder类。

package com.vinta.my_test.entity;  
  
import com.baomidou.mybatisplus.annotation.IdType;  
import com.baomidou.mybatisplus.annotation.TableField;  
import com.baomidou.mybatisplus.annotation.TableId;  
import com.baomidou.mybatisplus.annotation.TableName;  
import java.io.Serializable;  
import lombok.Data;  
  
/**  
 ** @TableName t_order  
 */@TableName(value ="t_order")  
@Data  
public class TOrder implements Serializable {  
    /**  
     *     
     * */   
     @TableId(type = IdType.AUTO)  
    private Integer orderId;  
  
    /**  
     *     
     * */    
     private String orderName;  
  
    /**  
     *    需要修改的字段  
     * */  
    private Integer customerId;  
  
    @TableField(exist = false)  
    private static final long serialVersionUID = 1L;  
}

TCustomer

package com.vinta.my_test.entity;  
  
import com.baomidou.mybatisplus.annotation.IdType;  
import com.baomidou.mybatisplus.annotation.TableField;  
import com.baomidou.mybatisplus.annotation.TableId;  
import com.baomidou.mybatisplus.annotation.TableName;  
import java.io.Serializable;  
import java.util.List;  
  
import lombok.Data;  
  
/**  
 ** @TableName t_customer  
 */@TableName(value ="t_customer")  
@Data  
public class TCustomer implements Serializable {  
    /**  
     *     
     * * /    
     @TableId(type = IdType.AUTO)  
    private Integer customerId;  
  
    /**  
     *     
     * 
     * */    
      private String customerName;  
  
    @TableField(exist = false)  
    private static final long serialVersionUID = 1L;  
}
DTO模型准备

DTO模型是为了接收联表查询的结果。
CustomerDTO

package com.vinta.my_test.entity;  
import lombok.Data;  
  
import java.io.Serializable;  
import java.util.List;  
  
@Data  
public class CustomerDTO implements Serializable {  
    private Integer customerId;  
    private String customerName;  
    private List orderList;  
  
    private static final long serialVersionUID = 1L;  
}

OrderDTO

package com.vinta.my_test.entity;  
  
  
import lombok.Data;  
  
import java.io.Serializable;  
  
@Data  
public class OrderDTO implements Serializable {  
    private Integer orderId;  
  
    private String orderName;  
  
    private TCustomer customer;  
  
    private static final long serialVersionUID = 1L;  
}

多表查询测试

在多表映射时,除了需要定义resultMap中的id标签和result标签外,还有定义连表关系。

对一映射

对一关系映射时需要在TOrderMapper.xml中的resultMap标签中添加association标签指定连表映射关系。

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE mapper  
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.vinta.my_test.mapper.TOrderMapper">  
  
    <resultMap id="BaseResultMap" type="com.vinta.my_test.entity.OrderDTO">  
        <id property="orderId" column="order_id" jdbcType="INTEGER"/>  
        <result property="orderName" column="order_name" jdbcType="CHAR"/>  
        <!--        指定映射关系-->  
        <association property="customer" column="customer_id" javaType="com.vinta.my_test.entity.TCustomer">  
            <id property="customerId" column="customer_id" jdbcType="INTEGER"/>  
            <result property="customerName" column="customer_name" jdbcType="CHAR"/>  
        </association>  
    </resultMap>  
  
    <select id="selectOrderWithCustomer" resultMap="BaseResultMap">  
        SELECT order_id, order_name, c.customer_id, customer_name  
        FROM t_order o  
                 LEFT JOIN t_customer c  
                           ON o.customer_id = c.customer_id  
        WHERE o.order_id = #{orderId}  
    </select>  
  
    <sql id="Base_Column_List">  
        order_id  
        ,order_name,customer_id  
    </sql>  
</mapper>

抽象方法

package com.vinta.my_test.mapper;  
  
import com.vinta.my_test.entity.OrderDTO;  
import com.vinta.my_test.entity.TOrder;  
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
  
/**  
* @author VINTA  
* @description 针对表【t_order】的数据库操作Mapper  
* @createDate 2024-03-03 17:50:42  
* @Entity com.vinta.my_test.entity.TOrder  
*/  
public interface TOrderMapper extends BaseMapper<TOrder> {  
    OrderDTO selectOrderWithCustomer(Integer orderId);  
}

测试代码

package com.vinta.my_test;  
  
import com.vinta.my_test.mapper.TOrderMapper;  
  
import jakarta.annotation.Resource;  
import org.junit.jupiter.api.Test;  
import org.springframework.boot.test.context.SpringBootTest;  
  
  
@SpringBootTest  
class MyTestApplicationTests {  
  
    @Resource  
    private TOrderMapper tOrderMapper;  
  
    @Test  
    void multi2one(){  
        Integer customerId= 1;  
        System.out.println(tOrderMapper.selectOrderWithCustomer(customerId));  
    }
}
对多映射

对多关系映射时,需要在TCustomerMapper.xml中添加collection标签和ofType属性

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE mapper  
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.vinta.my_test.mapper.TCustomerMapper">  
  
    <resultMap id="BaseResultMap" type="com.vinta.my_test.entity.CustomerDTO">  
            <id property="customerId" column="customer_id" jdbcType="INTEGER"/>  
            <result property="customerName" column="customer_name" jdbcType="CHAR"/>  
        <collection property="orderList" ofType="com.vinta.my_test.entity.OrderDTO">  
            <id property="orderId" column="order_id" jdbcType="INTEGER"/>  
            <result property="orderName" column="order_name" jdbcType="CHAR"/>  
        </collection>  
    </resultMap>  
  
    <select id="selectCustomerWithOrderList" resultMap="BaseResultMap">  
        SELECT c.customer_id,c.customer_name,o.order_id,o.order_name  
        FROM t_customer c  
                 LEFT JOIN t_order o  
                           ON c.customer_id=o.customer_id  
        WHERE c.customer_id=#{customerId}  
    </select>  
  
    <sql id="Base_Column_List">  
        customer_id,customer_name  
    </sql>  
</mapper>

抽象方法

package com.vinta.my_test.mapper;  
  
import com.vinta.my_test.entity.CustomerDTO;  
import com.vinta.my_test.entity.TCustomer;  
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
  
/**  
* @author VINTA  
* @description 针对表【t_customer】的数据库操作Mapper  
* @createDate 2024-03-03 17:50:42  
* @Entity com.vinta.my_test.entity.TCustomer  
*/  
public interface TCustomerMapper extends BaseMapper<TCustomer> {  
    CustomerDTO selectCustomerWithOrderList(Integer customerId);  
}

测试代码

package com.vinta.my_test;  
  
import com.vinta.my_test.mapper.TCustomerMapper;  
import com.vinta.my_test.mapper.TOrderMapper;  
  
import jakarta.annotation.Resource;  
import org.junit.jupiter.api.Test;  
import org.springframework.boot.test.context.SpringBootTest;  
  
  
@SpringBootTest  
class MyTestApplicationTests {  
  
    @Resource  
    private TCustomerMapper tCustomerMapper;  
  
  
    @Test  
    void multi2one(){  
        Integer customerId= 1;  
        System.out.println(tCustomerMapper.selectCustomerWithOrderList(customerId));  
    }}

📄动态SQL语句

实际开发中的条件查询时往往不止一个条件且条件并非固定的,而这就需要我们采用动态SQL,实现一个函数满足多个不同条件的查询语句。在使用动态查询语句时,要切记不能在sql语句的末尾加分号。下面详细介绍动态sql语句的标签。

SQL片段提取

为了重复利用相同的sql语句段,我们采用sql标签将可以重复利用的sql语句包裹起来,然后通过include标签引用

<select id="selectTCustomerById" resultType="com.vinta.my_test.entity.TCustomer">  
    SELECT  
    <include refid="Base_Column_List"></include>  
    FROM t_customer WHERE customer_id=#{customerId}  
</select>

<sql id="Base_Column_List">  
    order_id  
    ,order_name,
    customer_id  
</sql>

抽象方法

TCustomer selectTCustomerById(Integer customerId);

测试代码

@Test  
void duplicateSQLTest(){  
    Integer customerId= 1;  
    System.out.println(tCustomerMapper.selectTCustomerById(customerId));  
}

if与where标签

if标签是条件判断,需要在标签的test属性中写下判断表达式,由于是在xml中书写的判断语句,所以在比较大小时要采用xml语法,如大于符号为&gt;和小于符号为&lt;

<select id="selectWhereDIY" resultMap="tOrderBaseMap">  
    SELECT  
    <include refid="Base_Column_List"></include>  
    from t_order o  
    <where>  
        <if test="orderName != null">  
            o.order_name=#{orderName}  
        </if>  
        <if test="orderId &gt; 0">  
            o.order_id>#{orderId}  
        </if>  
    </where>  
</select>

抽象方法

List<TOrder> selectWhereDIY(@Param("orderId") Integer orderId, @Param("orderName") String orderName);

测试代码

@Test  
void test1(){  
    Integer customerId= 1;  
    System.out.println(tCustomerMapper.selectTCustomerById(customerId));  
}

set标签

set标签与where标签相同,这里不过多介绍

<update id="updateCustomerIdInt">  
    UPDATE t_customer  
    <set>  
        <!--    '&amp;' 为 &-->        
        <if test="customerId != null &amp;&amp; customerId != 0">  
            customer_name=#{customerName}  
        </if>  
    </set>  
</update>

抽象方法

int updateCustomerIdInt(@Param("customerId")Integer customerId, @Param("customerName") String customerName);

测试代码

@Test  
void test1(){  
    Integer customerId= 1;  
    System.out.println(tCustomerMapper.updateCustomerIdInt(customerId,"张三"));  
}

foreach标签

批量操作
collection属性:要遍历的集合

item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象

separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符

open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串

close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串

index属性:这里起一个名字,便于后面引用 遍历List集合,这里能够得到List集合的索引值 遍历Map集合,这里能够得到Map集合的key

<update id="updateTOrderByBatch"> 
	<foreach collection="orderList" item="order" separator=";"> 
		update t_order set order_name=#{order.orderName} where order_id=#{order.orderId} 
	</foreach>
 </update>

抽象方法

int updateTOrderByBatch(@Param("orderList") List<TOrder> orderList);

上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置。
关于foreach标签的collection属性

如果没有给接口中List类型的参数使用@Param注解指定一个具体的名字,那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来:

Parameter 'orderList' not found. Available parameters are [arg0, collection, list]

在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用@Param注解明确声明变量的名称,然后在foreach标签的collection属性中按照@Param注解指定的名称来引用传入的参数。

choose/when/otherwise标签

该标签与switch语句有异曲同工之妙。在多个分支条件中,仅执行一个。

  • 从上到下依次执行条件判断
  • 遇到的第一个满足条件的分支会被采纳
  • 被采纳分支后面的分支都将不被考虑
  • 如果所有的when分支都不满足,那么就执行otherwise分支
<select id="selectOrderByConditionByChoose" resultType="com.vinta.my_test.entity.TOrder">
    select *
    where
    <choose>
        <when test="orderName != null">order_name=#{orderName}</when>
        <when test="orderId &lt; 2">order_id &lt; 3000</when>
        <otherwise>1=1</otherwise>
    </choose>
    
    <!--
     第一种情况:第一个when满足条件 where order_name=?
     第二种情况:第二个when满足条件 where order_id < 2
     第三种情况:两个when都不满足 where 1=1 执行了otherwise
     -->
</select>

抽象方法

List<TOrder> selectOrderByConditionByChoose(TOrder order)

END

至此mybatis plus的大部分内容就介绍完了,关于mybatis plus的篇章就告一段落。下面的时间将更新我的项目笔记,当然如果有时间也想分享一下深度学习的笔记,最近正好在学习yolo模型。等我完全学透了就会整理出笔记发布到我的博客。如果有什么问题可以关注我的微信公众号。

公众号

更多内容请关注小夜的公众号,将持续更新java spring全家桶,vue全家桶,python数据分析与爬虫以及js与爬虫逆向。
欢迎关注小夜的公众号


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值