MyBatis多表查询+动态sql

news2025/6/13 20:23:30

文章目录

  • MyBatis多表查询
    • 1. 多表一对一查询
    • 2. 多表一对多
  • 动态SQL
    • 1.\<if\>标签
    • 2.\<trim\>标签
    • 3. \<where\>标签
    • 4.\<set\>标签
    • 5. \<foreach\>标签


MyBatis多表查询

在全局配置文件中中设置MyBatis执行日志

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

1. 多表一对一查询

假设有一个用户表和文章表,实体类中一个关联关系。

用户实体类

@Getter
@Setter
@ToString
public class UserInfo {
    private int id;
    private String name;
    private String password;
}

文章实体类

@Data
public class BlogInfo {
    private int blogId;
    private String title;
    private String content;
    private Integer userId;
    private Timestamp postTime;
    private String time;
    private UserInfo userInfo;
}

如果想查询的结果包含UserInfo的信息就需要使用,⼀对⼀映射要使⽤ <association> 标签,因为一篇文章只能对应一个作者。

Controller控制器代码

@Controller
@ResponseBody
public class BlogController {
    @Resource
    private BlogService blogService;

    @RequestMapping("/getAllBlog")
    public List<BlogInfo> getAllBlog() {
        return blogService.getAllBlog();
    }
}

Service服务层代码

@Service
public class BlogService {
    @Resource
    private BlogMapper blogMapper;
    public List<BlogInfo> getAllBlog() {
        return blogMapper.getAllBlog();
    }
}

BlogMap接口定义

@Mapper
public interface BlogMapper {

    List<BlogInfo> getAllBlog();
}

MyBatis接口实现xml配置文件

<?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.example.demo.mapper.BlogMapper">
    <resultMap id="BlogBean" type="com.example.demo.model.BlogInfo">
        <!-- 映射主键 -->
        <id column="blogId" property="blogId"></id>
        <!-- 普通列映射(表字段->实体类) -->
        <result column="title" property="title"></result>
        <result column="content" property="content"></result>
        <result column="userId" property="userId"></result>
        <result column="postTime" property="postTime"></result>
        <!-- 关联关系 -->
        <association property="userInfo"
                    resultMap="com.example.demo.mapper.UserMapper.UserBean"
                    columnPrefix="u_">

        </association>
    </resultMap>

    <select id="getAllBlog" resultMap="BlogBean">
        select u.userId u_userId,u.username u_username,b.blogId,b.title,b.postTime,b.userId from blog b left join user u on u.userId=b.userId
    </select>
</mapper>

使用<association>标签,表示一对一的结果映射:

  • property属性:指定文章类BlogInfo中对应的关联属性,也就是用户实例userInfo
  • resultMap属性:指定的是关联关系的结果集映射,也就是UserInfo对象的属性和表之间的映射关系
  • columnPrefix属性:绑定一对一对象的时候,是通过columnPrefix+association.resultMap.column来映射结果集字段。association.resultMap.column是指 <association>标签中 resultMap属性,对应的结果集映射中,column字段 。

在这里插入图片描述

Postman测试打印的部分结果

[
    {
        "blogId": 2,
        "title": "Java基础",
        "content": null,
        "userId": 1,
        "postTime": "2022-02-25T11:50:52.000+00:00",
        "time": null,
        "userInfo": {
            "id": 1,
            "name": "admin",
            "password": null
        }
    },
    {
        "blogId": 5,
        "title": "我的第一篇博客",
        "content": null,
        "userId": 1,
        "postTime": "2022-02-25T14:05:22.000+00:00",
        "time": null,
        "userInfo": {
            "id": 1,
            "name": "admin",
            "password": null
        }
    },

columnPrefix 如果省略,并且恰好两个表中如果有相同的字段,那么就会导致查询出错

2. 多表一对多

一对多需要使用<collection>标签,用法和<association>相同,比如说一个用户可以发布多篇文章。

用户实体类

@Getter
@Setter
@ToString
public class UserInfo {
    private int id;
    private String name;
    private String password;
    private List<BlogInfo> blogList;
}

映射xml配置文件

<?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.example.demo.mapper.UserMapper">
    <resultMap id="UserMap" type="com.example.demo.model.UserInfo">
        <!-- 映射主键的(表中主键和程序实体类中的主键) -->
        <id column="userId" property="id"></id>
        <!-- 普通字段映射 -->
        <result column="username" property="name"></result>
        <result column="password" property="password"></result>

        <!-- 外部关联管理 -->
        <collection property="blogList"
                    resultMap="com.example.demo.mapper.BlogMapper.BlogBean"
                    columnPrefix="b_">

        </collection>
    </resultMap>
    <select id="getAll" resultMap="UserMap">
        select u.userId,u.username,b.blogId b_blogId,b.title b_title,b.postTime b_postTime from user u left join blog b on u.userId=b.userId
    </select>


</mapper>
  • property属性对应的是UserInfo类中的属性blogList

Postman部分测试结果

在这里插入图片描述

动态SQL

动态SQL是MyBatis强大特征之一,在JDBC拼接SQL时候的痛处,不能忘记必要的空格添加,最后一个列名的逗号也要注意,利用动态SQL就能完成不同场景的SQL拼接。

MyBatis动态SQL

1.<if>标签

在注册某些账号的填写信息的时候,有必填项和非必填项,如果非必填项和少多写个接口就好了,但如果非必填项非常多的话就不行了,这个时候就需要动态标签<if>来判断了。

这里假设性别和年龄是非必填项。

接口定义

int logonUser(String name, String password, String sex, Integer age);

数据表

+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int(11)     | NO   | PRI | NULL    | auto_increment |
| name     | varchar(50) | NO   |     | NULL    |                |
| password | varchar(25) | NO   |     | NULL    |                |
| sex      | varchar(10) | YES  |     | NULL    |                |
| age      | int(11)     | YES  |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+

注意if<test=>是固定语法,里面的判断的字段要和接口中定义的名字对应。

  • 如果满足条件才会添加<if>里的文字
<?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.example.demo.mapper.UserMapper">

    <insert id="logonUser">
        insert into userInfo(id,name,password
        <if test="sex!=null">
            ,sex
        </if>
        <if test="age!=null">
            ,age
        </if>
        ) value(null,#{name},#{password}
        <if test="sex!=null">
            ,#{sex}
        </if>
        <if test="age!=null">
            ,#{age}
        </if>
        )
    </insert>

</mapper>

如果agesex都为null那么执行的sql 就是

insert into userInfo(id,name,password ) value(null,?,? );

2.<trim>标签

前面的新增用户操作,agesex可能是选填项,如果有多个字段,一般考虑用<trim>标签结合<if>标签,对多个字段都采用动态生成的方式

<trim>标签中有如下属性

  • prefix:它表示整个语句块,以prefix的值作为前缀(在语句块最前面添加prefix字符)
  • suffix:它表示整个语句块,以suffix的值作为后缀(在语句块最后添加suffix字符)
  • prefixOverrides:表示整个语句块要去除掉的前缀(删除语句块最前面满足条件的字符)
  • suffixOverrides:表示整个语句块要去除的后缀 (删除语句块最后面满足条件的字符)

那么就可以将插入语句改成如下:

<?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.example.demo.mapper.UserMapper">

        <insert id="logonUser">
            insert into userInfo
            <trim prefix="(" suffix=")" suffixOverrides=",">
                id,name,password,
                <if test="sex!=null">
                    sex,
                </if>
                <if test="age!=null">
                    age
                </if>
            </trim>
            value
            <trim prefix="(" suffix=")" suffixOverrides=",">
            null,#{name},#{password},
            <if test="sex!=null">
                #{sex},
            </if>
            <if test="age!=null">
                #{age}
            </if>

            </trim>
        </insert>


</mapper>

假设调用接口的代码是

userMapper.logonUser("张辉","123456","男",null);

最后执行的SQL就是

insert into userInfo ( id,name,password, sex ) value ( null,?,?, ? )
  • 基于prefix 配置,开始部分加上 (
  • 基于suffix 配置,结束部分加上 )
  • 多个if都已,结尾,使用suffixOverrides就可以把末尾的,给去掉

3. <where>标签

在使用查询语句的时候,如果有多个条件就会用到逻辑运算,如果有些条件不满足就会出现and前面没有条件判,导致sql报错

select * from userInfo where and sex!=null and age!=null;

使用<where>就可以很好的解决这个问题

  • 生成where,如果有查询条件就会生成where,如果没有查询条件就会忽略where
  • where会判断第一个查询条件前面有没有and,如果有则会删除

查找接口定义

List<User> findAllUser(String sex, int age);
<?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.example.demo.mapper.UserMapper">
    <select id="findAllUser" resultType="com.example.demo.model.User">
        select * from userInfo
        <where>
            <if test="sex!=null">
                sex=#{sex}
            </if>
            <if test="age!=null">
                and age=#{age}
            </if>
        </where>
    </select>

</mapper>

以上<where>标签也可以使 <trim prefix=“where” prefixOverrides="and> 替换

4.<set>标签

根据传入的用户对象属性来更新用户数据,可以使用<set>标签来指定动态内容.

示例:

根据用户Id来修改其它不为null的属性

接口定义

int updateUserId(User user);

xml文件的接口实现

  • <set>也是会自动去除末尾的,
  • <set>标签也可以使⽤ <trim prefix="set" suffixOverrides=","> 替换
<?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.example.demo.mapper.UserMapper">
    <update id="updateUserId">
        update userInfo
        <set>
            <if test="name!=null">
                name=#{name},
            </if>
            <if test="password!=null">
                password=#{password},
            </if>
            <if test="sex!=null">
                sex=#{sex},
            </if>
            <if test="age!=null">
                age=#{age},
            </if>
        </set>
        where id=#{id}
    </update>

</mapper>

最后拼接的SQL

update userInfo SET name=?, password=?, sex=?, age=? where id=?

5. <foreach>标签

对集合遍历的时候可以使用该标签,<foreach标签有以下属性:

  • conection:绑定方法中的集合,如 List、Set、Map或者数组对象
  • item:遍历时的每一个对象
  • open:语句块开头的字符串
  • close:语句块结束的字符串
  • separtor:每次遍历之间间隔的字符串

假设要通过多个用户id删除多个用户

接口定义和测试代码

@Mapper
public interface UserMapper {
    // 方法定义
    int deleteListId(List<Integer> listId);
}


@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;

    @Test
    void deleteListId() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        userMapper.deleteListId(list);
    }
}

xml文件

<?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.example.demo.mapper.UserMapper">
    <delete id="deleteListId">
        delete from userInfo where id in
        <foreach collection="listId" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </delete>

</mapper>

我的list集合里有3个用户Id,最后拼装的SQL

delete from userInfo where id in ( ? , ? , ? )

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/412204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用手机远程锁定电脑?

​“有时我已经到家了&#xff0c;却忘记锁上我的公司的电脑。每次我都害怕我电脑上的数据丢失。我可以在手机上远程锁定我的Windows计算机以避免这个问题吗&#xff1f;” 答案是肯定的&#xff01;很多人可能会遇到同样的下班不锁电脑的问题&#xff0c;有的人可能尝…

AOP执行顺序

AOP执行顺序 下面是个人的一点总结&#xff01;理解可能存在欠缺欢迎批评指正 1.AOP相关概念 Aspect&#xff1a;切面&#xff0c;由连接点和增强组成的。Aspect注解进行标识。Join point&#xff1a;连接点。代表一次方法的执行。Advice&#xff1a;增强。在连接点执行的操作。…

微服务+springcloud+springcloud alibaba学习笔记【Eureka服务注册中心】(3/9)

Eureka服务注册中心 3/91、服务注册与发现1.1 什么是服务治理&#xff1a;1.2 什么是服务注册与发现&#xff1a;1.3 Eureka服务注册与发现2、单机版eureka2.1 创建module2.2改pom依赖2.3写yml配置文件:2.4主启动类2.5 修改服务提供者 cloud-provider-payment8001 模块&#xf…

软件测试工作主要做什么

随着信息技术的发展和普及&#xff0c;人们对软件的使用越来越普及。但是在软件的使用过程中&#xff0c;软件的效果却不尽如人意。为了确保软件的质量&#xff0c;整个软件业界已经逐渐意识到测试的重要性&#xff0c;也有越来越多的小伙伴加入了软件测试这个行业中来。软件测…

微积分——极值定理的证明

1. 提出问题 极值定理(The Extreme Value Theorem)最初是由捷克数学家波尔查诺(Bernard Bolzano(1781年10月5号-1848年11月18号), 他是一位意大利血统的波希米亚数学家、逻辑学家、哲学家、神学家和天主教神父&#xff0c;也以其自由主义观点而闻名)证明&#xff0c;在1830年代…

基于t-SNE的Digits数据集降维与可视化

基于t-SNE的Digits数据集降维与可视化 描述 t-SNE(t-分布随机邻域嵌入)是一种基于流形学习的非线性降维算法&#xff0c;非常适用于将高维数据降维到2维或者3维&#xff0c;进行可视化观察。t-SNE被认为是效果最好的数据降维算法之一&#xff0c;缺点是计算复杂度高、占用内存…

高效时间管理日历 DHTMLX Event Calendar 2.0.3 Crack

DHTMLX Event Calendar用于高效时间管理的轻量级 JavaScript 事件日历 DHTMLX 可帮助您开发类似 Google 的 JavaScript 事件日历&#xff0c;以高效地组织约会。 用户可以通过拖放来管理事件&#xff0c;并以六种不同的模式显示它们。 JavaScript 事件日历功能 轻的简单的 Java…

UDP/TCP的相关性你知道几个?

TCP/IP网络原理——主要围绕UDP/TCP进行讲解 文章目录TCP/IP网络原理——主要围绕UDP/TCP进行讲解应用层传输层UDP/TCPTCP丢包总结应用层 网络协议的五层协议分别是应用层&#xff0c;传输层&#xff0c;网络层&#xff0c;数据链路层&#xff0c;物理层&#xff0c;这五层构成…

【STL十】关联容器——set容器、multiset容器

【STL十】关联容器——set容器、multiset容器一、set简介二、头文件三、模板类四、set的内部结构五、成员函数1、迭代器2、元素访问3、容量4、修改操作~~5、操作~~5、查找6、查看操作六、demo1、查找find2、修改操作insert3、修改操作erase、clear七、multisetset和multiset会根…

linux四剑客 grep awk sed find

Grep 过滤来自一个文件或标准输入匹配模式内容。 除了grep外&#xff0c;还有egrep、fgrep。egrep是grep的扩展&#xff0c;相当于grep -E。fgrep相当于grep -f&#xff0c;用的少。 Usage: grep [OPTION]… PATTERN [FILE]… 支持的正则描述-E&#xff0c;–extended-regexp…

爱智EdgerOS之深入解析VSCode的EdgerOS插件

一、安装插件 EdgerOS 插件是一个专门为应用开发者提供的在 EdgerOS 下提供应用构建、应用部署、应用更新等功能的插件&#xff0c;同时它还可以监视爱智应用的执行状态&#xff0c;方便开发者更好地调试应用。EdgerOS 插件需要在 VSCode 的 “拓展” 中下载安装&#xff0c;如…

vue-vue2和vue3的diff算法

核心要点 数据变化时&#xff0c;vue如何更新节点虚拟DOM 和 真实DOM 的区别vue2 diff 算法vue3 diff 算法 一、 数据变化时&#xff0c;vue如何更新节点 首先渲染真实DOM的开销是很大&#xff0c;比如有时候我们修改了某个数据且修改的数据量很大时&#xff0c;此时会频繁的…

自学编程的5大误区,早知道早避坑,过来人的宝贵经验

前言 有的人自学很快&#xff0c;几乎一个多月就能掌握一门技术&#xff0c;而有的人苦苦坚持&#xff0c;最后还是半途而废&#xff0c;很大的原因就在于在学习的时候掉进了一些误区没能走出来。 今天我们就来讲讲自学编程常见的5大误区&#xff0c;避开这些误区我们定能在自…

【Java Web】014 -- SpringBoot原理(配置优先级、Bean管理、SpringBoot原理)

目录 一、配置优先级 1、配置&#xff08;3种&#xff1a;.properties、yml、yaml&#xff09; ①、配置文件优先级 ②、如何指定Java系统属性和命令行参数 ③、5种配置文件的优先级 二、Bean管理 1、获取bean&#xff08;3种方法&#xff09; 2、bean作用域&#xff08;5种&am…

Arduino开发之如何连接超声波模块?

文章目录0、引言1、超声波模块说明2、代码编写3、功能演示0、引言 在利用Arduino开发过程中&#xff0c;若需知道设备与周围环境的距离&#xff0c;可利用超声波模块测量短程距离&#xff0c;使运动设备感知周围环境。本文在【Arduino如何进行开发&#xff1f;】基础上&#xf…

探索树形数据结构,通识树、森林与二叉树的基础知识(专有名词),进一步利用顺序表和链表表示、遍历和线索树形结构

树与二叉树 1.树 1.树形结构(非线性结构) 结点之间有分支&#xff0c;具有层次关系 树的定义&#xff1a; 树(tree)是n(n≥0)个有限集。 若n 0&#xff0c;则称为空树&#xff1b; 若n > 0&#xff0c;则它满足如下两个条件&#xff1a; 有且仅有一个特定的称为根(Ro…

NIFI大数据进阶_Json内容转换为Hive支持的文本格式_实际操作_02---大数据之Nifi工作笔记0032

然后首先我们来看一下hdfs中的数据的格式可以看到,还是json的格式对吧 而且也没有回行 然后我们来操作,首先添加一个evaluateJsonPath处理器 可以看到找到这个处理器 添加以后,我们去配置 ​​​​​​​ 点击去配置evaluateJsonPath处理器 可以看到首先我们destination这里配…

Rancher部署K8s集群

一、集群配置 服务器CPU内存磁盘操作系统k8s-master16核16G60GCentOS Linux release 7.5.1804k8s-node-116核16G60GCentOS Linux release 7.5.1804k8s-node-216核16G60GCentOS Linux release 7.5.1804 Rancher version : 2.6.3 二、环境初始化 所有服务器均执行一遍 1、将…

如何在矩池云上部署 Carla,模拟自动驾驶

简介 Carla 是一款基于 Python 编写和 UE&#xff08;虚幻引擎&#xff09;的开源仿真器&#xff0c;用于模拟自动驾驶车辆在不同场景下的行为和决策。它提供了高度可定制和可扩展的驾驶环境&#xff0c;包括城市、高速公路和农村道路等。Carla 还提供了丰富的 API 和工具&…

vscode中的配置

首先&#xff0c;运行或调试某文件&#xff0c;需要该文件是活动文件&#xff08;当前打开的文件&#xff09;。 下面依次介绍tasks.json和launch.json的配置参数。 tasks.json 1.tasks.json的用途 用于指定编译器和链接文件等。默认路径在.vscode下面。 2.如何打开一个tas…