12. Mybatis 多表查询 动态 SQL

目录

1. 数据库字段和 Java 对象不一致

2. 多表查询

3. 动态 SQL 使用

4. if 标签

5.  trim 标签

6. where 标签 

7. set 标签 

8. foreach 标签

9. 通过注解实现

9.1 查找所有数据 

9.2 通过 id 查找


1. 数据库字段和 Java 对象不一致

我们先来看一下数据库中的数据:

 接下来,我们在之前代码的基础上修改字段的名称:

/**
 * 数据库字段和 Java 对象不完全一致
 */
@Data
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private String photo;
    private Date createtime;
    private Date updatetime;
}
@Slf4j
@SpringBootTest
class UserMapperTest {
    @Autowired
    private UserMapper userMapper;
    @Test
    void queryAll() {
        List<User> users = userMapper.queryAll();
        log.info(users.toString());
    }

    @BeforeEach
    void setUp() {
        log.info("before...");
    }

    @AfterEach
    void tearDown() {
        log.info("after...");
    }
}

可以看到能够获取数据,但是对应字段的值为 null 了: 

因为数据库的字段命名规则和 Java 的命名规则不一致,数据库命名:字母小写,以下划线分割;Java 属性命名:小驼峰,第一个英文单词首字母小写,其他英文单词首字母大写。

一个 xml 文件中,可以存在多个 resultMap,只需要 id 不同即可:

List<User> queryAllMap();
 <resultMap id="BaseMap" type="com.example.demo.model.User">
        <id property="id" column="id"></id>
        <result property="name" column="username"></result>
        <result property="pwd" column="password"></result>
    </resultMap>
    <select id="queryAllMap" resultMap="BaseMap">
        select * from userinfo
    </select>
@Test
    void queryAllMap() {
        List<User> users = userMapper.queryAllMap();
        log.info(users.toString());
    }

此时,我们可以看到成功查询到数据: 

 那么具体的关系如下图标注所示:

2. 多表查询

我们再新建一张表:

-- 创建⽂章表
drop table if exists articleinfo;
create table articleinfo(
 id int primary key auto_increment,
 title varchar(100) not null,
 content text not null,
 createtime datetime default now(),
 updatetime datetime default now(),
 uid int not null,
 rcount int not null default 1,
 `state` int default 1
)default charset 'utf8mb4';

如下图所示: 

添加文章的数据:

-- ⽂章添加测试数据
insert into articleinfo(title,content,uid)
 values('Java','Java正⽂',1);

文章添加数据后,如下图所示: 

首先是 SQL 语句的多表查询:

select * from articleinfo ta 
left join userinfo tb on ta.uid = tb.id;

接下来通过 Mybatis 实现:

首先新建 ArticleInfo 类: 

@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    private Date createtime;
    private Date updatetime;
    private Integer rcount;
    private User user;
}

新建 ArticleMapper.xml 文件:

<resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
        <id property="id" column="id"></id>
        <result property="title" column="title"></result>
        <result property="content" column="content"></result>
        <result property="createtime" column="createtime"></result>
        <result property="updatetime" column="updatetime"></result>
        <association property="user" resultMap="com.example.demo.mapper.UserMapper.BaseMap"></association>
    </resultMap>
    <select id="queryArticle" resultMap="BaseMap">
        select *
        from articleinfo ta
        left join userinfo tb on ta.uid = tb.id
    </select>

添加测试类:

@Slf4j
@SpringBootTest
class ArticleMapperTest {
    @Autowired
    private ArticleMapper articleMapper;
    @Test
    void queryArticle() {
        List<ArticleInfo> articleInfos = articleMapper.queryArticle();
        log.info(articleInfos.toString());
    }
}

运行结果如下图所示:

我们可以看到上述方式的结果显示的不够完整且需要输入的 SQL 语句过多。


接下来,我们通过另一种方式来实现:

List<ArticleInfo> queryArticle2();

在 ArticleMapper.xml 文件中添加以下配置:

<resultMap id="BaseMap2" type="com.example.demo.model.ArticleInfo">
        <id property="id" column="id"></id>
        <result property="title" column="title"></result>
        <result property="content" column="content"></result>
        <result property="createtime" column="createtime"></result>
        <result property="updatetime" column="updatetime"></result>
        <result property="userId" column="userid"></result>
        <result property="username" column="username"></result>
    </resultMap>
    <select id="queryArticle2" resultMap="BaseMap2">
        select
        ta.*,
        tb.id as userid,
        tb.username as username
        from articleinfo ta
        left join userinfo tb on ta.uid = tb.id
    </select>

添加测试类:

@Test
    void queryArticle2() {
        List<ArticleInfo> articleInfos = articleMapper.queryArticle2();
        log.info(articleInfos.toString());
    }

查询数据如下图所示:

需要注意,在实际的开发中,要尽量避免使用 *,无论数据库中有多少字段都需要一一罗列出来

如下所示: 

<resultMap id="BaseMap2" type="com.example.demo.model.ArticleInfo">
        <id property="id" column="id"></id>
        <result property="title" column="title"></result>
        <result property="content" column="content"></result>
        <result property="createtime" column="createtime"></result>
        <result property="updatetime" column="updatetime"></result>
        <result property="userId" column="userid"></result>
        <result property="username" column="username"></result>
    </resultMap>
    <select id="queryArticle2" resultMap="BaseMap2">
        select
        ta.id as id,
        ta.title as title,
        ta.content as content,
        ta.createtime as createtime,
        ta.updatetime as updatetime,
        tb.id as userid,
        tb.username as username
        from articleinfo ta
        left join userinfo tb on ta.uid = tb.id
    </select>

3. 动态 SQL 使用

在实际的应用中,并不是每个信息都是必填的,也就是动态 SQL根据输入参数的不同动态的拼接 SQL。

我们先来看一下表中现有的数据:

 接下来,我们插入数据:

void insert(ArticleInfo articleInfo);
<insert id="insert">
        insert into articleinfo(title,content,uid,state)values (#{title},#{content},#{userId},#{state})
    </insert>
 @Test
    void insert() {
        ArticleInfo articleInfo = new ArticleInfo();
        articleInfo.setTitle("测试文章");
        articleInfo.setContent("测试文章内容");
        articleInfo.setUserId(1);
        articleInfo.setState(null);
        articleMapper.insert(articleInfo);
    }

可以看到,上面我们是自行将 state 的值设置为了 null,那么如果我们没有给这个字段赋值呢?

修改 XML 文件: 

 <insert id="insert">
        insert into articleinfo(title,content,uid)values (#{title},#{content},#{userId})
    </insert>

 可以看到当我们没有对 state 赋值时,进行了自动默认赋值为1:

那么,这显然是不符合我们的预期的,我们想要实现的是当用户没有输入数据时,应该为默认值;输入数据时,显示为输入的数据。此时就需要用到标签了。

4. <if> 标签

我们的目标是根据用户输入的情况,动态拼接 SQL。

void insertByCondition(ArticleInfo articleInfo);
<insert id="insertByCondition">
        insert into articleinfo(title,content,uid
        <if test="state!=null">
            ,state
        </if>
        )
        values

        (#{title},#{content},#{userId}
        <if test="state!=null">
            ,#{state}
        </if>
        )
    </insert>
@Test
    void insertByCondition() {
        ArticleInfo articleInfo = new ArticleInfo();
        articleInfo.setTitle("测试文章2");
        articleInfo.setContent("测试文章内容2");
        articleInfo.setUserId(1);
        articleMapper.insert(articleInfo);
    }

 由于我们并没有设置 state 的状态,因此默认为1:

接下来我们设置 state 为0:

@Test
    void insertByCondition() {
        ArticleInfo articleInfo = new ArticleInfo();
        articleInfo.setTitle("测试文章3");
        articleInfo.setContent("测试文章内容3");
        articleInfo.setUserId(1);
        articleInfo.setState(0);
        articleMapper.insertByCondition(articleInfo);
    }

可以看到成功运行: 

 当我们需要对多个字段应用 if 标签时,会存在报错:

如果统一把逗号放在字段前面,当第一个字段为 null 时,整个 SQL 的最前面就会多一个逗号;如果统一把逗号放在字段后面,当最后一个字段为 null 时,整个 SQL 的最后面会多一个逗号。

5. <trim> 标签

上面的插入数据功能,如果所有字段都是非必填项,就考虑使用标签结合标签,对多个字段都采取动态生成的方式。 标签中有如下属性:

  • prefix:表示整个语句块,以prefix的值作为前缀
  • suffix:表示整个语句块,以suffix的值作为后缀
  • prefixOverrides:表示整个语句块要去除掉的前缀
  • suffixOverrides:表示整个语句块要去除掉的后缀

 使用 <trim> 标签:

<insert id="insertByCondition">
        insert into articleinfo
        <trim prefix="(" suffix=")" prefixOverrides="," suffixOverrides=",">
            <if test="title!=null">
                title,
            </if>
            <if test="content!=null">
                content,
            </if>
            <if test="userId!=null">
                uid,
            </if>
            <if test="state!=null">
                state
            </if>
        </trim>
        values
        <trim prefix="(" suffix=")" prefixOverrides="," suffixOverrides=",">
            <if test="title!=null">
                #{title},
            </if>
            <if test="content!=null">
                #{content},
            </if>
            <if test="userId!=null">
                #{content},
            </if>
            <if test="state!=null">
                #{state},
            </if>
        </trim>
    </insert>

可以看到此时能够正确执行: 

6. <where> 标签 

当我们需要使用 where 语句进行条件筛选时: 

List<ArticleInfo> queryBycondition(@Param("uid") Integer uid,@Param("state")Integer state);
<select id="queryBycondition" resultType="com.example.demo.model.ArticleInfo">
        select * from articleinfo
        where
        <if test="uid!=null">
            uid = #{uid}
        </if>
        <if test="state!=null">
            and state=#{state}
        </if>
    </select>
@Test
    void queryBycondition() {
        List<ArticleInfo> articleInfos = articleMapper.queryBycondition(1,1);
        log.info(articleInfos.toString());
    }

可以看到成功执行: 

此时我们修改代码如下:

 @Test
    void queryBycondition() {
        List<ArticleInfo> articleInfos = articleMapper.queryBycondition(1,null);
        log.info(articleInfos.toString());
    }

依然可以成功执行: 

 当我们修改代码如下时:

 @Test
    void queryBycondition() {
        List<ArticleInfo> articleInfos = articleMapper.queryBycondition(null,1);
        log.info(articleInfos.toString());
    }

产生报错信息: 

添加语句 1 = 1,继续修改代码如下: 

<select id="queryBycondition" resultType="com.example.demo.model.ArticleInfo">
        select * from articleinfo
        where 1 = 1
        <if test="uid!=null">
            and uid = #{uid}
        </if>
        <if test="state!=null">
            and state=#{state}
        </if>
    </select>

此时可以看到成功执行: 


接下来,我们使用 where 标签实现以上功能。

在 XML 文件中,添加以下语句: 

<select id="queryBycondition" resultType="com.example.demo.model.ArticleInfo">
        select * from articleinfo
        <where>
            <if test="uid!=null">
                and uid = #{uid}
            </if>
            <if test="state!=null">
                and state=#{state}
            </if>
        </where>
    </select>

可以已经生成了 where 并且去掉了 and : 

当两个字段均为 null 时,可以看到直接去掉了 where:

@Test
    void queryBycondition() {
        List<ArticleInfo> articleInfos = articleMapper.queryBycondition(null,null);
        log.info(articleInfos.toString());
    }

综上,我们可以知道 where 标签具有以下作用:

  1. 生成 where 关键字
  2. 去除多余的 and
  3. 如果没有 where 条件,就不会生成 where 关键字

7. <set> 标签 

void updateByCondition(@Param("uid") Integer uid,@Param("state")Integer state);
<update id="updateByCondition">
        update articleinfo
        set
        <if test="uid!=null">
            uid = #{uid},
        </if>
        <if test="state!=null">
            state = #{state},
        </if>
    </update>
@Test
    void updateByCondition() {
        articleMapper.updateByCondition(1,null);
    }

运行后,产生以下报错:

接下来,我们使用 set 标签:

<update id="updateByCondition">
        update articleinfo
        <set>
            <if test="uid!=null">
                uid = #{uid},
            </if>
            <if test="state!=null">
                state = #{state},
            </if>
        </set>
    </update>

运行成功: 

综上,我们可以看到 set 标签的作用:

  1. 生成 set 关键字
  2. 去除最后一个逗号(也可以使用 trim 标签)

8. <foreach> 标签

对集合进行遍历时可以使用该标签。标签有如下属性:

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

因此,我们来通过 foreach 标签实现以下目标:

 接下来我们通过代码实现:

void batchDelete(List<Integer> ids);
<delete id="batchDelete">
        delete from articleinfo where id in
        <foreach collection="list" open="(" close=")" separator="," item="id">
            #{id}
        </foreach>
    </delete>
@Test
    void batchDelete() {
        List<Integer> ids = Arrays.asList(2,3,4,5,6,10,11);
        articleMapper.batchDelete(ids);
    }

可以看到成功运行: 

表中相应的数据也删除了: 

注意: 

还需要注意的是 collection 也可以是参数的名称:

9. 通过注解实现

Mybatis 的实现有两种方式:

  • xml
  • 注解

9.1 查找所有数据 

接下来,我们来看一下如何通过注解来实现:

@Mapper
public interface UserMapper2 {
    @Select("select * from userinfo")
    List<User> queryAll();
}
@Slf4j
@SpringBootTest
class UserMapper2Test {
    @Autowired
    private UserMapper2 userMapper2;
    @Test
    void queryAll() {
       List<User> userList = userMapper2.queryAll();
       log.info(userList.toString());
    }
}

运行结果如下:

9.2 通过 id 查找

@Mapper
public interface UserMapper2 {

    @Select("select * from userinfo where id = #{uid}")
    User queryById(Integer aaa);
}
@Slf4j
@SpringBootTest
class UserMapper2Test {
    @Autowired
    private UserMapper2 userMapper2;

    @Test
    void queryById() {
        User user = userMapper2.queryById(1);
        log.info(user.toString());
    }
}

可以看到运行结果如下: 

一个项目中,注解和 XML 的方式可以并存,对于简单的 SQL 使用注更加方便,但是对于动态 SQL 注解写起来非常麻烦。


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

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

相关文章

一条命令重启supervisor所有RUNNING状态的进程

supervisorctl status | grep RUNNING | awk {print $1} | xargs -n1 supervisorctl restart

Uniapp_app端使用重力感应实现横屏竖屏自动切换

1、进入页面默认是竖屏当手机横着的时候页面也跟着横着 进入页面开启定时器调用相关api去触发横屏竖屏&#xff0c;主要核心代码都在onShow()里面和onHide()里 <template> <view class"monitor"><u-no-network></u-no-network><web-view …

安全加固服务器

根据以下的内容来加固一台Linux服务器的安全。 首先是限制连续密码错误的登录次数&#xff0c;由于RHEL8之后都不再使用pam_tally.so和pam_tally2.so&#xff0c;而是pam_faillock.so 首先进入/usr/lib64/security/中查看有什么模块&#xff0c;确认有pam_faillock.so 因为只…

.Net6 Core Web API 配置 log4net + MySQL

目录 一、导入NuGet 包 二、添加配置文件 log4net.config 三、创建MySQL表格 四、Program全局配置 五、帮助类编写 六、效果展示 小编没有使用依赖注入的方式。 一、导入NuGet 包 ---- log4net 基础包 ---- Microsoft.Extensions.Logging.Log4Net…

【C#学习笔记】值类型(1)

虽然拥有编程基础的人可以很快地上手C#&#xff0c;但是依然需要学习C#的特性和基础。本系列是本人学习C#的笔记&#xff0c;完全按照微软官方文档编写&#xff0c;但是不适合没有编程基础的人。 文章目录 .NET 体系结构Hello&#xff0c;World类型和变量&#xff08;重要&…

Flink之JDBC Sink

这里介绍一下Flink Sink中jdbc sink的使用方法,以mysql为例,这里代码分为两种,事务和非事务 非事务代码 import org.apache.flink.connector.jdbc.JdbcConnectionOptions; import org.apache.flink.connector.jdbc.JdbcExecutionOptions; import org.apache.flink.connector.…

opencv 31-图像平滑处理-方框滤波cv2.boxFilter()

方框滤波&#xff08;Box Filtering&#xff09;是一种简单的图像平滑处理方法&#xff0c;它主要用于去除图像中的噪声和减少细节&#xff0c;同时保持图像的整体亮度分布。 方框滤波的原理很简单&#xff1a;对于图像中的每个像素&#xff0c;将其周围的一个固定大小的邻域内…

vue3和typescript_组件

1 components下新建myComponent.vue 2 页面中引入组件&#xff0c;传入值&#xff0c;并且绑定事件函数。 3

【Valgrind】如何使用Valgrind监控内存

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

计算机视觉与图形学-神经渲染专题-ConsistentNeRF

摘要 Neural Radiance Fields (NeRF) 已通过密集视图图像展示了卓越的 3D 重建能力。然而&#xff0c;在稀疏视图设置下&#xff0c;其性能显着恶化。我们观察到&#xff0c;在这种情况下&#xff0c;学习不同视图之间像素的 3D 一致性对于提高重建质量至关重要。在本文中&…

【LeetCode每日一题】——766.托普利茨矩阵

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【题目进阶】八【解题思路】九【时间频度】十【代码实现】十一【提交结果】 一【题目类别】 矩阵 二【题目难度】 简单 三【题目编号】 766.托普利茨矩阵 四【题目描述…

【测试开发】Mq消息重复如何测试?

本篇文章主要讲述重复消费的原因&#xff0c;以及如何去测试这个场景&#xff0c;最后也会告诉大家&#xff0c;目前互联网项目关于如何避免重复消费的解决方案。 Mq为什么会有重复消费的问题? Mq 常见的缺点之一就是消息重复消费问题&#xff0c;产生这种问题的原因是什么呢…

16、外部配置源与外部配置文件及JSON配置

外部配置源与外部配置文件及JSON配置 application.properties application.yml 这些是配置文件&#xff0c; 命令行配置、环境变量配置、系统属性配置源&#xff0c;这些属于配置源。 外部配置源的作用&#xff1a; Spring Boot相当于对Spring框架进行了封装&#xff0c;Spri…

webrtc的回声消除延迟时间估算

叫回声消除的延迟时间估算不太合理&#xff0c;这里核心就是估算调用webrtc的条件边界&#xff0c;都知道webrtc回声消除的生效的前提就是一定要拿到远端声音的信息&#xff0c;然后拿近端声音和远端声音对齐&#xff0c;从近端声音中&#xff0c;结合远端声音模拟出远端声音在…

Windows用户如何安装新版本cpolar内网穿透超详细教程

Windows用户如何安装新版本cpolar内网穿透 文章目录 Windows用户如何安装新版本cpolar内网穿透 在科学技术高度发达的今天&#xff0c;我们身边充斥着各种电子产品&#xff0c;这些电子产品不仅为我们的工作带来极大的便利&#xff0c;也让生活变得丰富多彩。我们可以使用便携的…

[Python] Pylance 插件打开 Python 的类型检查

安装 Python 插件 2.打开一个 Python 文件 可以看到右下角有一个花括号和 Python 字样&#xff0c;点击花括号&#xff08;不是 Python 字样&#xff09;打开类型检查即可&#xff1a;

酷开系统 | 酷开科技,让数据变得更有价值!

身处信息时代&#xff0c;我们每个人时刻都在生成、传递和应用数据&#xff0c;数据已经成为了现代社会中宝贵的资源之一&#xff0c;而在人工智能领域&#xff0c;数据更是被称为人工智能的“燃料”。 而在AI的发展中&#xff0c;只有拥有高质量、多样性且充分代表性的数据集…

java 定时任务不按照规定时间执行

这里写目录标题 使用异步启动可能出现的问题排查代码中添加的定时任务步骤是否正确排查是否任务阻塞&#xff0c;如果定时任务出现异常阻塞后&#xff0c;将不会在次执行java中多个Scheduled定时器不执行为了让Scheduled效率更高&#xff0c;我们可以通过两种方法将定时任务变成…

vxworks文件系统分析

参考https://www.freebuf.com/articles/endpoint/335030.html 测试固件 https://service.tp-link.com.cn/detail_download_7989.html 固件提取 binwalk解压固件&#xff0c;在第一部分即为要分析的二进制文件&#xff0c;可以拖进ida分析 设置为arm小端字节序&#xff0c;点…

【HarmonyOS】性能优化之低代码开发加载多张轮播图

【关键字】 HarmonyOS、低代码开发、Swiper组件、性能优化、分页加载 写在前面 目前使用DevEco Studio的低代码工具开发元服务时&#xff0c;通过实际测试发现&#xff0c;Swiper组件加载多张轮播图时加载显示耗时较长&#xff08;实际测试网络状态一般的情况下显示耗时达到8…