欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

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

来源: javaer 分享于  点击 33084 次 点评:29

使用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);
      
          @Autowired
          private BookMapper bookMapper;
      
          /**
           * 根据书籍ID,获取书籍信息
           */
          @Test
          public 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对于此种需求的支持并不友好

       /**
           * 资源量统计-根据专题
           * 之所以使用此种方式是因为统计的列是不固定的,所以需要动态拼接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注解可以提高代码的灵活性和可维护性。

      以上就是使用MyBatis的动态SQL注解实现实体的CRUD操作代码的详细内容,更多关于MyBatis动态SQLCRUD操作的资料请关注3672js教程其它相关文章!

      您可能感兴趣的文章:
      • Spring mvc整合mybatis(crud+分页插件)操作mysql
      • MyBatis完成CRUD 详细细节内容剖析
      • SpringBoot整合MyBatis实现CRUD操作项目实践
      • Mybatis-Plus CRUD操作方法
      • mybatis项目CRUD步骤实例详解
      相关栏目:

      用户点评