MyBatis多表查询+动态sql

文章目录

  • 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.mfbz.cn/a/10159.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

hadoop使用MapReduce统计单词出现次数案例

前言 前面的文章已经展示了如何在windows上传文件到hdfs&#xff0c;上传后如何简单的做统计&#xff0c;本文展示一下。上传文件到HDFS链接 这里我们做一个案例&#xff0c;对一个上传到HDFS的文档中统计good出现的次数。 文件内容如下 这里我使用的是【上传文件到HDFS链接…

南方猛将加盟西方手机完全是臆测,他不会希望落得兔死狗烹的结局

早前南方某科技企业因为命名的问题闹得沸沸扬扬&#xff0c;于是一些业界人士就猜测该猛将会加盟西方手机&#xff0c;对于这种猜测可以嗤之以鼻&#xff0c;从西方手机以往的作风就可以看出来它向来缺乏容纳猛将的气量。一、没有猛将的西方手机迅速沉沦曾几何时&#xff0c;西…

linux服务器禁止ping命令,linux服务器禁ping如何解除

linux服务器禁止ping命令&#xff0c;linux服务器禁ping如何解除 我是艾西&#xff0c;在我们搭建网站或做某些程序时&#xff0c;不少人会问禁ping是什么意思&#xff0c;怎么操作的对于业务有哪些好处等&#xff0c;今天艾西一次给你们说清楚。 禁PING的意思是&#xff1a;不…

《花雕学AI》12:从ChatGPT的出现看人类与人工智能的互补关系与未来发展

马云说道&#xff0c;ChatGPT这一类技术已经对教育带来挑战&#xff0c;但是ChatGPT这一类技术只是AI时代的开始。 谷歌CEO桑德尔皮猜曾说&#xff1a;“人工智能是我们人类正在从事的最为深刻的研究方向之一&#xff0c;甚至要比火与电还更加深刻。” 360周鸿祎认为&#xf…

Java Web 实战 15 - 计算机网络之网络编程套接字

文章目录一 . 网络编程中的基本概念1.1 网络编程1.2 客户端(client) / 服务器(server)1.3 请求(request) / 响应(response)1.4 客户端和服务器之间的交互数据1.4.1 一问一答1.4.2 多问一答1.4.3 一问多答1.4.4 多问多答二 . socket 套接字2.1 UDP 的 Socket API2.1.1 引子2.1.2…

Ubuntu20.04 个人配置和i3美化

Ubuntu20.04 个人配置和i3美化 本文是基于个人习惯和审美&#xff0c;快速配置一个新ubuntu的步骤。脚本在资源里给出&#xff0c;但仍有部分配置文件需在脚本执行后手动修改,文中已用红色字体标出 更新apt源 备份原来的源更换阿里源 # 备份 sudo mv /etc/apt/sources.list…

基于Pytorch的可视化工具

深度学习网络通常具有很深的层次结构&#xff0c;而且层与层之间通常会有并联、串联等连接方式。当使用PyTorch建立一个深度学习网络并输出文本向读者展示网络的连接方式是非常低效的&#xff0c;所以需要有效的工具将建立的深度学习网络结构有层次化的展示&#xff0c;这就需要…

RK3399平台开发系列讲解(基础篇)Linux 传统间隔定时器

🚀返回专栏总目录 文章目录 一、设置间隔定时器 setitimer()二、查询定时器状态 getitimer()三、更简单的定时接口 alarm()四、传统定时器的应用4.1、为阻塞操作设置超时4.2、性能剖析五、传统定时器的局限性沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将详细…

【Vue】el与data的两种写法

data与el的2种写法 el有两种写法 new Vue时配置el属性。先创建Vue实例。随后再通过vm.$mount(‘root’)指定el的值。 data有2种写法 对象式: data:{}函数式: data(){ return {}} 如何选择&#xff1a;目前哪种写法都可以&#xff0c;以后学习到组件时&#xff0c;data必须使…

ERP软件的作用

ERP软件的运用是在企业管理系统的数据基础上实现的&#xff0c;它的应用涉及到企业的各个部门。ERP软件是在制造资源计划的基础上进一步发展而成的对企业供应链的管理软件。ERP是集采购、销售和库存、财务、生产管理和委托加工为一体的企业管理软件。它是集企业管理理念、业务流…

快速排序详解

一、定义 快速排序&#xff08;英语&#xff1a;Quicksort&#xff09;&#xff0c;又称分区交换排序&#xff08;英语&#xff1a;partition-exchange sort&#xff09;&#xff0c;简称「快排」&#xff0c;是一种被广泛运用的排序算法。 二、基本原理 快速排序是一个基于 分…

PostgreSQL下载、安装、Problem running post-install step的解决、连接PostgreSQL

我是参考《SQL基础教程》来安装的&#xff0c;关于书的介绍、配套视频、相关代码可以参照下面的链接&#xff1a; SQL基础教程&#xff08;第2版&#xff09; (ituring.com.cn) 一、下载 我直接打开书中的下载链接时&#xff0c;显示的是这个界面&#xff1a; You are not …

二维(三维)坐标系中旋转矩阵

求三维坐标系的旋转矩阵通常需要求分别沿3个坐标轴的二维坐标系下的旋转矩阵&#xff0c;二维坐标系下的旋转矩阵的推导过程通常以某一点逆时针旋转θ\thetaθ角度进行推理。以下将通过此例来详细讲解二维坐标系下的旋转矩阵推导过程&#xff0c;并进一步给出其他方式的旋转矩阵…

Surfshark下载到使用完整教程|2023最新

2023年3月16日更新 在正式介绍surfshark的教程( 教程直达学习地址: qptool.net/shark.html )之前&#xff0c;我们可以来看看最近surfshark的服务与产品退化到什么程度了。我曾经是Surshark两年的忠实用户&#xff0c;但是&#xff0c;现在&#xff0c;作为一个负责人的测评&a…

文件操作File类,OutputStream、InputStream、Reader、Writer的用法

文章目录File 类OutputStream、InputStreamInputStreamOutputStreamReader、WriterReaderWriter注意事项简单模拟实战File 类 Java标准库中提供的File类是对硬盘上的文件的抽象&#xff0c;每一个File对象代表了一个文件&#xff0c;因为文件在硬盘上存储&#xff0c;而直接操…

网络编程三要素

网络编程三要素 IP、端口号、协议 三要素分别代表什么 ip&#xff1a;设备在网络中的地址&#xff0c;是唯一的标识 端口号&#xff1a;应用程序在设备中的唯一标识 协议&#xff1a;数据在网络中传输的规则 常见的协议有UDP、TCP、http、https、ftp ip&#xff1a;IPv4和…

Java通过继承的方法来实现长方形的面积和体积

目录 前言 一、测试.java类 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 二、Changfangxing.java类 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 三、Jxing.java类 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 前言 1.若有选择…

五、Locust之HTTP用户类

HttpUser是最常用的用户。它增加了一个客户端属性&#xff0c;用来进行HTTP请求。 from locust import HttpUser, task, betweenclass MyUser(HttpUser):wait_time between(5, 15)task(4)def index(self):self.client.get("/")task(1)def about(self):self.client.…

Java避免死锁的几个常见方法(有测试代码和分析过程)

目录 Java避免死锁的几个常见方法 死锁产生的条件 上死锁代码 然后 &#xff1a;jstack 14320 >> jstack.text Java避免死锁的几个常见方法 Java避免死锁的几个常见方法 避免一个线程同时获取多个锁。避免一个线程在锁内同时占用多个资源&#xff0c;尽量保证每个锁…

DMDSC问题测试

问题一&#xff1a;手动停止两节点&#xff0c;单独启动节点二测试 集群停库前状态&#xff0c;登录监视器查看 dmcssm INI_PATHdmcssm.ini show 节点一&#xff1a; [dmdbalocalhost ~]$ DmServiceDMSERVER stop Stopping DmServiceDMSERVER: …