使用MyBatis的动态SQL注解实现实体的CRUD操作

使用MyBatis的动态SQL注解实现实体的CRUD操作

  • 1. 引言
  • 2. 准备工作
    • 2.1 创建数据表
    • 2.2 创建实体类 `Book`
    • 2.3 修改Mapper接口 `BookMapper`
  • 3. 测试CRUD操作
    • 3.1 配置日志
    • 3.2 查询操作
    • 3.3 新增操作
    • 3.4 修改操作
    • 3.5 删除操作
  • 4. 与MyBatis Plus的对比
  • 5. 动态SQL注解的适用场景
    • 5.1 动态查询条件
    • 5.2 动态插入和更新
    • 5.3 复杂的业务逻辑
    • 5.4 查询结果动态列的聚合
  • 6. 小结

1. 引言

在使用MyBatis进行数据库操作时,动态SQL注解提供了一种优雅的方式来编写动态SQL语句。MyBatis 3.x 版本提供了以下四个CRUD的高级注解:

  • @SelectProvider:用于构建动态查询SQL。
  • @InsertProvider:用于构建动态新增SQL。
  • @UpdateProvider:用于构建动态更新SQL。
  • @DeleteProvider:用于构建动态删除SQL。

这些注解可以帮助开发者在Mapper接口中动态地构建SQL语句,避免了在XML配置文件中编写大量的SQL代码,使代码更加简洁和易于维护。本文将详细介绍如何使用这些动态SQL注解来实现书籍信息的查询、新增、修改和删除操作。

此外,我们将与MyBatis Plus中的@Select注解进行对比,展示MyBatis动态SQL注解的优势和灵活性。

2. 准备工作

2.1 创建数据表

首先,需要创建一个代表书籍信息的表 tb_book

-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tb_book;-- 创建“书籍信息”数据表
CREATE TABLE IF NOT EXISTS tb_book
(book_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '书籍编号',book_name VARCHAR(50) NOT NULL COMMENT '书名',author VARCHAR(50) NOT NULL COMMENT '作者',publisher VARCHAR(50) COMMENT '出版社',publish_date DATE COMMENT '出版日期'
) COMMENT = '书籍信息表';-- 添加数据
INSERT INTO tb_book(book_name, author, publisher, publish_date) VALUES ('书籍1', '作者1', '出版社1', '2023-01-01');

2.2 创建实体类 Book

com.zhouquan.entity 包中创建 Book 类,使用 @Data 注解来自动生成getter和setter方法。

package com.zhouquan.entity;
import lombok.Data;
import java.util.Date;/*** 书籍信息实体类*/
@Data
public class Book {/*** 书籍编号*/private int bookId;/*** 书名*/private String bookName;/*** 作者*/private String author;/*** 出版社*/private String publisher;/*** 出版日期*/private Date publishDate;
}

2.3 修改Mapper接口 BookMapper

com.zhouquan.mapper 包中创建 BookMapper 接口,并使用动态SQL注解来实现CRUD操作。

package com.zhouquan.mapper;import com.zhouquan.entity.Book;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.jdbc.SQL;
import org.springframework.stereotype.Repository;@Mapper
@Repository
public interface BookMapper {@SelectProvider(type = BookSqlBuilder.class, method = "buildGetBookByIdSql")public Book getBookById(@Param("bookId") int bookId);@InsertProvider(type = BookSqlBuilder.class, method = "buildInsertBookSql")@Options(useGeneratedKeys = true, keyColumn = "book_id", keyProperty = "bookId")public int insertBook(Book book);@UpdateProvider(type = BookSqlBuilder.class, method = "buildUpdateBookSql")public int updateBook(Book book);@DeleteProvider(type = BookSqlBuilder.class, method = "buildDeleteBookSql")public int deleteBook(@Param("bookId") int bookId);class BookSqlBuilder {public String buildGetBookByIdSql(@Param("bookId") int bookId) {return new SQL() {{SELECT("*");FROM("tb_book");WHERE("book_id = #{bookId}");}}.toString();}public String buildInsertBookSql(Book book) {return new SQL() {{INSERT_INTO("tb_book");VALUES("book_name", "#{bookName}");VALUES("author", "#{author}");VALUES("publisher", "#{publisher}");VALUES("publish_date", "#{publishDate}");}}.toString();}public String buildUpdateBookSql(Book book) {return new SQL() {{UPDATE("tb_book");SET("book_name = #{bookName}", "author = #{author}", "publisher = #{publisher}", "publish_date = #{publishDate}");WHERE("book_id = #{bookId}");}}.toString();}public String buildDeleteBookSql(@Param("bookId") int bookId) {return new SQL() {{DELETE_FROM("tb_book");WHERE("book_id = #{bookId}");}}.toString();}}
}

3. 测试CRUD操作

为了测试CRUD操作,我们将使用JUnit进行单元测试,并通过日志记录操作的结果。

3.1 配置日志

在Maven的pom.xml文件中引入SLF4J和Logback的依赖:

<dependencies><!-- MyBatis 依赖 --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></dependency><!-- SLF4J 依赖 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.30</version></dependency></dependencies>

3.2 查询操作

import com.zhouquan.entity.Book;
import com.zhouquan.mapper.BookMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class BookMapperTest {private static final Logger logger = LoggerFactory.getLogger(BookMapperTest.class);@Autowiredprivate BookMapper bookMapper;/*** 根据书籍ID,获取书籍信息*/@Testpublic void getBookById() {Book book = bookMapper.getBookById(1);logger.info("书籍编号:{}", book.getBookId());logger.info("书名:{}", book.getBookName());logger.info("作者:{}", book.getAuthor());logger.info("出版社:{}", book.getPublisher());logger.info("出版日期:{}", book.getPublishDate());}
}

3.3 新增操作

@Autowired
private BookMapper bookMapper;/*** 新增书籍,并获取自增主键*/
@Test
public void insertBook() {Book book = new Book();book.setBookName("新书");book.setAuthor("新作者");book.setPublisher("新出版社");book.setPublishDate(new Date());bookMapper.insertBook(book);logger.info("新增书籍ID:{}", book.getBookId());
}

3.4 修改操作

@Autowired
private BookMapper bookMapper;/*** 更新书籍信息*/
@Test
public void updateBook() {Book book = bookMapper.getBookById(1);book.setBookName("更新后的书名");bookMapper.updateBook(book);logger.info("更新后的书名:{}", book.getBookName());
}

3.5 删除操作

@Autowired
private BookMapper bookMapper;/*** 删除书籍*/
@Test
public void deleteBook() {bookMapper.deleteBook(1);logger.info("删除书籍ID为1的记录");
}

4. 与MyBatis Plus的对比

MyBatis Plus提供了一种更加简洁的方式来编写SQL语句,例如,使用@Select注解可以直接在Mapper接口中编写SQL语句,而无需单独定义SQL构建器类。

例如,使用MyBatis Plus可以这样定义BookMapper接口:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhouquan.entity.Book;
import org.apache.ibatis.annotations.Select;public interface BookMapper extends BaseMapper<Book> {@Select("SELECT * FROM tb_book WHERE book_id = #{bookId}")Book getBookById(int bookId);
}

这种方式相对于MyBatis动态SQL注解而言,更加直接和易读。但是,MyBatis动态SQL注解在处理复杂SQL语句时具有更高的灵活性和可维护性,特别是当需要根据不同条件动态生成SQL时,MyBatis动态SQL注解显得更为强大和适用

5. 动态SQL注解的适用场景

5.1 动态查询条件

在一些应用中,查询条件是动态的,可能根据不同的用户输入或业务逻辑生成不同的查询条件。使用动态SQL注解可以在运行时根据这些条件动态构建查询语句,避免了硬编码查询条件的问题。

public String buildDynamicQuerySql(Map<String, Object> params) {return new SQL() {{SELECT("*");FROM("tb_book");if (params.get("author") != null) {WHERE("author = #{author}");}if (params.get("publisher") != null) {WHERE("publisher = #{publisher}");}if (params.get("publishDate") != null) {WHERE("publish_date = #{publishDate}");}}}.toString();
}

5.2 动态插入和更新

在一些情况下,插入或更新的数据字段可能不是固定的,而是根据业务需求动态变化的。使用动态SQL注解可以根据传入的实体对象构建动态插入或更新语句。

public String buildDynamicInsertSql(Book book) {return new SQL() {{INSERT_INTO("tb_book");if (book.getBookName() != null) {VALUES("book_name", "#{bookName}");}if (book.getAuthor() != null) {VALUES("author", "#{author}");}if (book.getPublisher() != null) {VALUES("publisher", "#{publisher}");}if (book.getPublishDate() != null) {VALUES("publish_date", "#{publishDate}");}}}.toString();
}public String buildDynamicUpdateSql(Book book) {return new SQL() {{UPDATE("tb_book");if (book.getBookName() != null) {SET("book_name = #{bookName}");}if (book.getAuthor() != null) {SET("author = #{author}");}if (book.getPublisher() != null) {SET("publisher = #{publisher}");}if (book.getPublishDate() != null) {SET("publish_date = #{publishDate}");}WHERE("book_id = #{bookId}");}}.toString();
}

5.3 复杂的业务逻辑

在一些复杂的业务场景中,SQL语句的构建逻辑可能非常复杂,涉及多个表的联接、多种条件的判断等。使用动态SQL注解可以在Java代码中使用条件逻辑来构建复杂的SQL语句,使代码更加清晰和易于维护。

public String buildComplexQuerySql(Book book) {return new SQL() {{SELECT("b.*, a.author_name, p.publisher_name");FROM("tb_book b");JOIN("tb_author a ON b.author_id = a.author_id");JOIN("tb_publisher p ON b.publisher_id = p.publisher_id");if (book.getBookName() != null) {WHERE("b.book_name LIKE CONCAT('%', #{bookName}, '%')");}if (book.getAuthor() != null) {WHERE("a.author_name LIKE CONCAT('%', #{author}, '%')");}if (book.getPublisher() != null) {WHERE("p.publisher_name LIKE CONCAT('%', #{publisher}, '%')");}}}.toString();
}

5.4 查询结果动态列的聚合

需求类似于下,根据专题聚合出不同的资料类型,select中的列需要动态的拼接,显然MP的mapper对于此种需求的支持并不友好

image-20240621171513659

 /*** 资源量统计-根据专题* 之所以使用此种方式是因为统计的列是不固定的,所以需要动态拼接select** @param resourceTypeNameList 资源名称列表* @param resourceTypeSidList  资源sid列表* @param start                加工开始时间* @param end                  加工结束时间* @return*/public String groupTopicDynamicSQL(final List<String> resourceTypeNameList, final List<String> resourceTypeSidList,final LocalDate start, final LocalDate end) {// 构建外部查询SQL outerQuery = new SQL();outerQuery.SELECT("COALESCE(topic_name, '总计') AS '专题'");if (resourceTypeNameList != null && !resourceTypeNameList.isEmpty()) {for (String rt : resourceTypeNameList) {outerQuery.SELECT(String.format("SUM(CASE WHEN resource_type_name = '%s' THEN count ELSE 0 END) AS " +"'%s'", rt, rt));}}// 构建子查询SQL subQuery = new SQL();subQuery.SELECT("rt.name AS resource_type_name, t.name AS topic_name, COUNT(*) AS count").FROM("works w").JOIN("works_topic wt ON w.sid = wt.work_sid").JOIN("topic t ON wt.topic_sid = t.sid").JOIN("resource_type rt ON w.type_leaf_sid = rt.sid").WHERE(" w.deleted=0 ");if (resourceTypeNameList != null && !resourceTypeNameList.isEmpty()) {String joinedResourceTypes = resourceTypeSidList.stream().map(rt -> "'" + rt + "'").collect(Collectors.joining(", "));subQuery.WHERE("w.type_leaf_sid IN (" + joinedResourceTypes + ")");}if (start != null) {subQuery.WHERE("w.update_time >= #{start}");}if (end != null) {subQuery.WHERE("w.update_time <= #{end}");}subQuery.GROUP_BY("rt.name, t.name");outerQuery.SELECT("SUM(count) AS '总计'").FROM("(" + subQuery + ") AS subQuery").GROUP_BY("topic_name WITH ROLLUP");return outerQuery.toString();}
/*** 统计分析-资源量统计** @param resourceTypeNameList* @param resourceTypeSidList* @param start* @param end* @return*/
@SelectProvider(type = WorksSqlProvider.class, method = "groupTopicDynamicSQL")
List<Map<String, Object>> staticByTopic(@Param("resourceTypeNameList") List<String> resourceTypeNameList,@Param("resourceTypeSidList") List<String> resourceTypeSidList,@Param("start") LocalDate start,@Param("end") LocalDate end);

6. 小结

MyBatis动态SQL注解通过提供更加灵活和动态的方式来构建SQL语句,使得代码更加简洁和易于维护。而MyBatis Plus通过简化SQL编写过程,提供了一种更加便捷的方式来进行数据库操作。根据具体项目的需求和复杂度,可以选择适合的工具来实现数据库操作。

通过上述介绍,可以看出动态SQL注解在处理动态查询条件、动态插入和更新以及复杂的业务逻辑时具有明显的优势和适用性。在实际开发中,合理使用动态SQL注解可以提高代码的灵活性和可维护性。

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

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

相关文章

API低代码平台介绍6-数据库记录删除功能

数据库记录删除功能 在前续文章中我们介绍了如何插入和修改数据库记录&#xff0c;本篇文章会沿用之前的测试数据&#xff0c;介绍如何使用ADI平台定义一个删除目标数据库记录的接口&#xff0c;包括 单主键单表删除、复合主键单表删除、多表删除&#xff08;整合前两者&#x…

JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)

用 js 描述树 let tree [{label:a,children:[{label:b,children:[{label:d},{label:e}]},{label:c,children:[{label:f}]}]} ]使用数组是因为树的节点有顺序 深度优先遍历 从根节点出发&#xff0c;优先遍历最深的节点 遍历顺序为 abdecf function DFS(tree) {tree.forEach(…

揭示SOCKS5代理服务器列表的重要性

在复杂的网络安全领域中&#xff0c;SOCKS5代理在保护在线活动方面发挥着关键作用。本文深入探讨了SOCKS5代理服务器列表的细节&#xff0c;探讨了它们的应用、优势以及在增强在线安全和隐私方面不可或缺的功能。 一、理解SOCKS5代理服务器列表 作为在客户端和服务器之间进行通…

QListWidget、QMenu、Action、customContextMenuRequested

QListWidget的初始化、清空、Append添加、Insert添加、删除item QListWidget的事件的使用 QToolBox的使用&#xff0c;每个Page可以添加其他控件 QToolBar使用代码添加QMenu,QMenu添加3个Action QToolButton绑定Action 布局 其中 QSplitter比较特殊&#xff0c; 允许在水平或垂…

实现锚点链接点击tab跳转到指定位置 并且滚动鼠标顶部锚点的样式也跟随变化

实现效果如下 不管是点击还是 滚动鼠标 顶部的样式也会跟随变化 点击会跳转到指定的位置 通过IntersectionObserver 监听是否可见 下面代码可以直接执行到vue的文件 <template><div><ul class"nav"><li v-for"tab in tabs" :key…

Nvidia Isaac Sim组装机器人和添加传感器 入门教程 2024(5)

Nvidia Isaac Sim 入门教程 2024 版权信息 Copyright 2023-2024 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. …

采购管理系统:反向竞价失败的 6 个常见原因

在当今快节奏和竞争激烈的商业环境中&#xff0c;采购专业人员一直在寻找创新战略来节约成本和简化供应链流程。反向竞价就是其中一种广受欢迎的策略。 反向竞价提供了一种独特的采购方法&#xff0c;允许买家邀请多个供应商参与实时竞标&#xff0c;以争取他们的业务。虽然反…

构建个人文件上传服务:Python Flask实现上传和下载完整指南

介绍 在本教程中&#xff0c;我们将学习如何使用Python Flask框架将文件上传到服务器&#xff0c;并使用SQLite数据库来跟踪上传的文件。我们将提供后端代码和一个示例项目的Git链接&#xff0c;以便您可以轻松地跟随本教程。 准备工作 首先&#xff0c;您需要安装Python和F…

太爱这种数据可视化效果,零售行业的都看过来

在当今数字化浪潮下&#xff0c;数据可视化已成为零售行业洞察市场趋势、优化运营决策的关键技术。奥威BI零售数据分析方案凭借其卓越的数据可视化效果&#xff0c;成为零售企业的得力助手。接下来就通过BI节假日分析报表来简单地感受一下。 注&#xff1a;该BI节假日分析报表…

DDMA信号处理以及数据处理的流程---cfar检测

Hello,大家好,我是Xiaojie,好久不见,欢迎大家能够和Xiaojie一起学习毫米波雷达知识,Xiaojie准备连载一个系列的文章—DDMA信号处理以及数据处理的流程,本系列文章将从目标生成、信号仿真、测距、测速、cfar检测、测角、目标聚类、目标跟踪这几个模块逐步介绍,这个系列的…

Nacos安装教程(很细很简单),解决启动报错Please set the JAVA_HOME

nacos安装 找到你要下载的版本解压到任意非中文目录下端口默认8848&#xff0c;如有端口冲突&#xff0c;可修改配置文件中的端口。编辑shutdown.cmd文件&#xff0c;路径换成你的jdk安装地址否则会报错Please set the JAVA_HOME variable in your environment, We need java(x…

Manim本地安装

目录 背景Manim安装及配置一个上手例子参考文献 背景 通过上一期的介绍&#xff0c;我们对Manim有了初步的认识也知道Manim版本的区别&#xff0c;这一期&#xff0c;我们来给自己的计算机安装一个社区版ManimCE&#xff0c;方便以后玩Manim。笔者的硬件配置是联想笔记本Windo…

机器学习:人工智能的子领域之一

引言 人工智能&#xff08;AI&#xff09;已经成为现代科技的重要组成部分&#xff0c;推动了许多领域的创新与进步。在人工智能的诸多子领域中&#xff0c;机器学习&#xff08;ML&#xff09;无疑是最关键和最具影响力的一个。机器学习通过自动分析和学习数据中的模式&#x…

JavaScript的学习之强制类型转换

目录 一、什么是强制类型转换 二、其他类型转化为String类型 方式一&#xff1a;调用被转化数据类型的toString()方法 方式二&#xff1a;调用String函数&#xff0c;并将我们要转换的数据添加进去为参数 三、其他类型转化为Number类型 方式一&#xff1a;使用Number()函数…

环境配置04:Pytorch下载安装

说明&#xff1a; 显存大于4G的建议使用GPU版本的pytorch&#xff0c;低于4G建议使用CPU版本pytorch&#xff0c;直接使用命令安装对应版本即可 GPU版本的pytorch的使用需要显卡支持&#xff0c;需要先安装CUDA&#xff0c;即需要完成以下安装 1.查看已安装CUDA版本 GPU对应…

常见的结构型设计模式

设计模式&#xff08;二&#xff09; 常见的结构型模式 1.代理模式: 提供一种代理方法 &#xff0c;来控制对其他对象的访问。在有些情况下&#xff0c;一个对象不能或者不适合直接访问另一个对象&#xff0c;而代理对象可以在这两个类之间起一个中介的作用。 举例&#xf…

Docker容器基础知识,即linux日常运维命令

Docker 是一个流行的用 Go 语言开发的开源项目&#xff0c;基于Linux内核的cgroup、namespace及 AUFS 等技术&#xff0c;对进程进行封装隔离&#xff0c;由 Dotcloud 公司开发。Docker已成为容器行业的事实标准。 小陈发现一个有趣的事情&#xff0c;容器的英文是Container&am…

甘肃的千层烤馍:传统面点的魅力绽放

千层烤馍&#xff0c;作为甘肃美食文化的重要象征&#xff0c;以其独特的外形和丰富的口感&#xff0c;吸引着众多食客。它的外观犹如一件精美的艺术品&#xff0c;层层叠叠&#xff0c;金黄酥脆&#xff0c;散发着诱人的香气。 在甘肃平凉地区制作千层烤馍&#xff0c…

国内大模型/智能体盘点丨16家公司,13款大模型,19个智能体

在当今这个智能化风起云涌的时代&#xff0c;随着人工智能技术的飞速发展&#xff0c;大模型&#xff08;Large Language Models&#xff09;作为推动行业变革的关键力量&#xff0c;正逐步渗透到社会经济的各个角落。 从科技创新的最前沿到日常生活应用的细微之处&#xff0c…

相交链表--力扣160

相交链表 题目思路C代码 题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 思路 求两个链表的相交结点&#xff0c;使用…