使用MyBatis的動態(tài)SQL注解實(shí)現(xiàn)實(shí)體的CRUD操作代碼
1. 引言
在使用MyBatis進(jìn)行數(shù)據(jù)庫操作時,動態(tài)SQL注解提供了一種優(yōu)雅的方式來編寫動態(tài)SQL語句。MyBatis 3.x 版本提供了以下四個CRUD的高級注解:
@SelectProvider
:用于構(gòu)建動態(tài)查詢SQL。@InsertProvider
:用于構(gòu)建動態(tài)新增SQL。@UpdateProvider
:用于構(gòu)建動態(tài)更新SQL。@DeleteProvider
:用于構(gòu)建動態(tài)刪除SQL。
這些注解可以幫助開發(fā)者在Mapper接口中動態(tài)地構(gòu)建SQL語句,避免了在XML配置文件中編寫大量的SQL代碼,使代碼更加簡潔和易于維護(hù)。本文將詳細(xì)介紹如何使用這些動態(tài)SQL注解來實(shí)現(xiàn)書籍信息的查詢、新增、修改和刪除操作。
此外,我們將與MyBatis Plus中的@Select注解進(jìn)行對比,展示MyBatis動態(tài)SQL注解的優(yōu)勢和靈活性。
2. 準(zhǔn)備工作
2.1 創(chuàng)建數(shù)據(jù)表
首先,需要創(chuàng)建一個代表書籍信息的表 tb_book
。
-- 判斷數(shù)據(jù)表是否存在,存在則刪除 DROP TABLE IF EXISTS tb_book; -- 創(chuàng)建“書籍信息”數(shù)據(jù)表 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 = '書籍信息表'; -- 添加數(shù)據(jù) INSERT INTO tb_book(book_name, author, publisher, publish_date) VALUES ('書籍1', '作者1', '出版社1', '2023-01-01');
2.2 創(chuàng)建實(shí)體類 Book
在 com.zhouquan.entity
包中創(chuàng)建 Book
類,使用 @Data
注解來自動生成getter和setter方法。
package com.zhouquan.entity; import lombok.Data; import java.util.Date; /** * 書籍信息實(shí)體類 */ @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
包中創(chuàng)建 BookMapper
接口,并使用動態(tài)SQL注解來實(shí)現(xiàn)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進(jìn)行單元測試,并通過日志記錄操作的結(jié)果。
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; /** * 根據(jù)書籍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語句,而無需單獨(dú)定義SQL構(gòu)建器類。
例如,使用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動態(tài)SQL注解而言,更加直接和易讀。但是,MyBatis動態(tài)SQL注解在處理復(fù)雜SQL語句時具有更高的靈活性和可維護(hù)性,特別是當(dāng)需要根據(jù)不同條件動態(tài)生成SQL時,MyBatis動態(tài)SQL注解顯得更為強(qiáng)大和適用
5. 動態(tài)SQL注解的適用場景
5.1 動態(tài)查詢條件
在一些應(yīng)用中,查詢條件是動態(tài)的,可能根據(jù)不同的用戶輸入或業(yè)務(wù)邏輯生成不同的查詢條件。使用動態(tài)SQL注解可以在運(yùn)行時根據(jù)這些條件動態(tài)構(gòu)建查詢語句,避免了硬編碼查詢條件的問題。
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 動態(tài)插入和更新
在一些情況下,插入或更新的數(shù)據(jù)字段可能不是固定的,而是根據(jù)業(yè)務(wù)需求動態(tài)變化的。使用動態(tài)SQL注解可以根據(jù)傳入的實(shí)體對象構(gòu)建動態(tài)插入或更新語句。
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 復(fù)雜的業(yè)務(wù)邏輯
在一些復(fù)雜的業(yè)務(wù)場景中,SQL語句的構(gòu)建邏輯可能非常復(fù)雜,涉及多個表的聯(lián)接、多種條件的判斷等。使用動態(tài)SQL注解可以在Java代碼中使用條件邏輯來構(gòu)建復(fù)雜的SQL語句,使代碼更加清晰和易于維護(hù)。
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 查詢結(jié)果動態(tài)列的聚合
需求類似于下,根據(jù)專題聚合出不同的資料類型,select中的列需要動態(tài)的拼接,顯然MP的mapper對于此種需求的支持并不友好
/** * 資源量統(tǒng)計(jì)-根據(jù)專題 * 之所以使用此種方式是因?yàn)榻y(tǒng)計(jì)的列是不固定的,所以需要動態(tài)拼接select * * @param resourceTypeNameList 資源名稱列表 * @param resourceTypeSidList 資源sid列表 * @param start 加工開始時間 * @param end 加工結(jié)束時間 * @return */ public String groupTopicDynamicSQL(final List<String> resourceTypeNameList, final List<String> resourceTypeSidList, final LocalDate start, final LocalDate end) { // 構(gòu)建外部查詢 SQL outerQuery = new SQL(); outerQuery.SELECT("COALESCE(topic_name, '總計(jì)') 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)); } } // 構(gòu)建子查詢 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 '總計(jì)'") .FROM("(" + subQuery + ") AS subQuery") .GROUP_BY("topic_name WITH ROLLUP"); return outerQuery.toString(); }
/** * 統(tǒng)計(jì)分析-資源量統(tǒng)計(jì) * * @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. 小結(jié)
MyBatis動態(tài)SQL注解通過提供更加靈活和動態(tài)的方式來構(gòu)建SQL語句,使得代碼更加簡潔和易于維護(hù)。而MyBatis Plus通過簡化SQL編寫過程,提供了一種更加便捷的方式來進(jìn)行數(shù)據(jù)庫操作。根據(jù)具體項(xiàng)目的需求和復(fù)雜度,可以選擇適合的工具來實(shí)現(xiàn)數(shù)據(jù)庫操作。
通過上述介紹,可以看出動態(tài)SQL注解在處理動態(tài)查詢條件、動態(tài)插入和更新以及復(fù)雜的業(yè)務(wù)邏輯時具有明顯的優(yōu)勢和適用性。在實(shí)際開發(fā)中,合理使用動態(tài)SQL注解可以提高代碼的靈活性和可維護(hù)性。
以上就是使用MyBatis的動態(tài)SQL注解實(shí)現(xiàn)實(shí)體的CRUD操作代碼的詳細(xì)內(nèi)容,更多關(guān)于MyBatis動態(tài)SQLCRUD操作的資料請關(guān)注腳本之家其它相關(guān)文章!
- MyBatis中動態(tài)SQL的使用指南
- MyBatis中實(shí)現(xiàn)動態(tài)SQL標(biāo)簽
- MyBatis實(shí)現(xiàn)動態(tài)SQL的方法
- Mybatis之動態(tài)SQL使用小結(jié)(全網(wǎng)最新)
- Mybatis動態(tài)Sql標(biāo)簽使用小結(jié)
- MyBatis中的XML實(shí)現(xiàn)和動態(tài)SQL實(shí)現(xiàn)示例詳解
- MyBatis映射文件中的動態(tài)SQL實(shí)例詳解
- 詳解MyBatis特性之動態(tài)SQL
- Mybatis使用注解實(shí)現(xiàn)復(fù)雜動態(tài)SQL的方法詳解
- MyBatis的動態(tài)攔截sql并修改
- mybatis動態(tài)生成sql語句的實(shí)現(xiàn)示例
相關(guān)文章
關(guān)于Spring源碼深度解析(AOP功能源碼解析)
這篇文章主要介紹了關(guān)于Spring源碼深度解析(AOP功能源碼解析),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07Java多線程面試題之交替輸出問題的實(shí)現(xiàn)
本文主要介紹了Java多線程面試題之交替輸出問題的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01dubbo如何實(shí)現(xiàn)consumer從多個group中調(diào)用指定group的provider
這篇文章主要介紹了dubbo如何實(shí)現(xiàn)consumer從多個group中調(diào)用指定group的provider問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03詳解SpringBoot整合RabbitMQ如何實(shí)現(xiàn)消息確認(rèn)
這篇文章主要介紹了SpringBoot整合RabbitMQ是如何實(shí)現(xiàn)消息確認(rèn)的,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05詳解java并發(fā)編程(2) --Synchronized與Volatile區(qū)別
這篇文章主要介紹了Synchronized與Volatile區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Java語言實(shí)現(xiàn)簡單FTP軟件 FTP軟件效果圖預(yù)覽之上傳功能(3)
這篇文章主要為大家詳細(xì)介紹了Java語言實(shí)現(xiàn)簡單FTP軟件,F(xiàn)TP軟件效果圖預(yù)覽之上傳功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03