MyBatis-Flex 使用指南
1. 概述
MyBatis-Flex透過為常見資料存取任務提供更便捷的程式設計模型來擴展 MyBatis 生態系統。
在本教程中,我們將建立一個小型 Spring Boot 4 應用程序,以探索 MyBatis-Flex 的核心工作流程,從專案設定到資料存取程式碼。具體來說,我們將定義一個簡單的實體和映射器,執行基本的 CRUD 操作,使用QueryWrapper建立過濾查詢,並對結果進行分頁。
本文中的範例需要 Java 17 或更高版本。
2. 搭建一個最小的 Spring Boot 項目
Spring Boot 簡化了 MyBatis-Flex 的設定流程,讓我們更專注於映射器和查詢 API。
2.1. 新增依賴項
雖然 MyBatis-Flex 消除了通常與直接 JDBC 存取相關的許多樣板程式碼,我們可以將其用作 JPA 或 Hibernate 的替代方案,但它仍然依賴 Spring JDBC基礎架構進行資料來源配置和管理。
MyBatis-Flex 支援超過四十種不同的資料庫類型,包括 MySQL、PostgreSQL、Oracle、SQL Server 和 SQLite。在本例中,我們使用 H2 作為記憶體資料庫,因為它便於建立快速、隔離且可重複的測試。
為此,我們將 MyBatis-Flex、Spring JDBC、H2 和 Spring Boot Test Starter 依賴項新增至 Spring Boot 4 的pom.xml中:
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot4-starter</artifactId>
<version>1.11.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.4.240</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>4.0.3</version>
<scope>test</scope>
</dependency>
在繼續之前,建議檢查Maven 倉庫是否有更新的版本。
2.2. 配置資料來源 (H2)
接下來,讓我們在src/main/resources/application.yml中設定資料來源:
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:mybatisflex
username: sa
password:
sql:
init:
mode: always
schema-locations: classpath:db/schema-h2.sql
data-locations: classpath:db/data-h2.sql
jdbc:h2:mem:mybatisflex URL 會在應用程式上下文中建立一個記憶體資料庫。 H2 的預設使用者名稱是sa ,密碼為空。
spring.sql.init部分指示 Spring Boot 使用 SQL 腳本初始化資料來源。在本例中,它從src/main/resources/db/schema-h2.sql載入模式,從src/main/resources/db/data-h2.sql載入範例資料。
2.3. 啟用地圖掃描
最後,讓我們在主 Spring Boot 應用程式類別中啟用映射器掃描。在 MyBatis-Flex 中,映射器是將 Java 程式碼連接到實體資料庫操作的元件:
@SpringBootApplication
@MapperScan("com.baeldung.mybatisflex.mapper")
public class MyBatisFlexApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisFlexApplication.class, args);
}
}
啟用映射器掃描後,Spring 可以偵測到AccountMapper ,為其建立一個 bean,並允許我們將其註入測試中。
3. 建立實體和映射器
專案配置完成後,讓我們定義領域模型和 MyBatis-Flex 用於持久化操作的映射器。
3.1. 定義一個有註解的簡單實體
首先,我們可以從一個簡單的Account實體開始,它會對應到tb_account表。 MyBatis -Flex 使用註解將類別與表格關聯起來,並自訂各個欄位如何對應到資料庫列:
@Table("tb_account")
public class Account {
@Id(keyType = KeyType.Auto)
private Long id;
@Column("user_name")
private String userName;
private Integer age;
private String status;
@Column("created_at")
private LocalDateTime createdAt;
// getters and setters
}
這裡, @Table將類別綁定到tb_account表,而@Id標記主鍵並將其配置為自動產生的值。值得注意的是,當 Java 欄位名稱與列名不完全相符時,例如userName和createdAt ,我們也使用了@Column 。其餘欄位遵循預設命名約定,因此無需額外的註解。
3.2. 建立繼承自BaseMapper<T> Mapper 介面
接下來,我們來定義Account的映射器介面。在 MyBatis-Flex 中,映射器是公開實體資料存取操作的元件:
@Mapper
public interface AccountMapper extends BaseMapper<Account> {
}
AccountMapper透過繼承BaseMapper<Account> ,獲得了一套豐富的內建操作,包括插入、更新、刪除和查詢。這不僅保持了介面的簡潔性,也提供了本文範例所需的一切功能。
3.3 初始化模式與測試數據
在編寫任何針對資料庫的 Java 程式碼之前,我們先加入一個簡單的資料庫模式和一些範例資料行。為此,我們將這兩個腳本放在src/main/resources/db目錄下,以便 Spring Boot 可以在啟動時自動載入它們。
首先,我們建立schema-h2.sql :
CREATE TABLE IF NOT EXISTS tb_account (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(100) NOT NULL,
age INT,
status VARCHAR(20),
created_at TIMESTAMP
);
接下來,我們來執行data-h2.sql :
INSERT INTO tb_account (user_name, age, status, created_at)
VALUES ('sarah', 35, 'ACTIVE', TIMESTAMP '2024-01-15 10:00:00');
INSERT INTO tb_account (user_name, age, status, created_at)
VALUES ('mike', 17, 'INACTIVE', TIMESTAMP '2024-02-01 09:30:00');
INSERT INTO tb_account (user_name, age, status, created_at)
VALUES ('emma', 42, 'ACTIVE', TIMESTAMP '2024-03-10 14:15:00');
INSERT INTO tb_account (user_name, age, status, created_at)
VALUES ('tom', 20, 'ACTIVE', TIMESTAMP '2024-04-05 08:45:00');
範例資料為我們提供了一個可預測的整合測試資料集。由於每行數據的年齡、狀態和創建時間各不相同,我們可以在多個範例中重複使用這些數據,而無需在每個測試中添加額外的設定程式碼。
4. 基本 CRUD 操作
現在我們已經有了基本模型,接下來讓我們看看在實際應用中我們最有可能首先執行的資料操作:
- 插入
- 閱讀
- 更新
- 移除
首先,讓我們直接將映射器注入到測試類別中:
@SpringBootTest
@Transactional
public class MyBatisFlexIntegrationTest {
@Autowired
private AccountMapper accountMapper;
// tests
}
現在,我們可以開始新增測試了。
4.1. 按ID插入和檢索
讓我們來嘗試一個簡單的持久化流程:
@Test
public void whenInsertAndSelectById_thenAccountIsPersisted() {
Account account = new Account();
account.setUserName("olivia");
account.setAge(28);
account.setStatus("ACTIVE");
account.setCreatedAt(LocalDateTime.of(2024, 5, 1, 12, 0));
accountMapper.insert(account);
Account persistedAccount = accountMapper.selectOneById(account.getId());
assertNotNull(account.getId());
assertNotNull(persistedAccount);
assertEquals("olivia", persistedAccount.getUserName());
}
在這裡,我們建立了一個Account ,透過映射器插入了它,然後透過其產生的id檢索它,以驗證記錄是否已正確儲存。
4.2 更新現有記錄
更新現有記錄同樣簡單。
首先,我們載入一個實體。然後,我們修改它的一個欄位。最後,我們將其傳遞回映射器以持久化更改:
@Test
public void whenUpdatingAnAccount_thenTheNewStatusIsStored() {
Account account = accountMapper.selectOneById(1L);
account.setStatus("INACTIVE");
accountMapper.update(account);
Account updatedAccount = accountMapper.selectOneById(1L);
assertEquals("INACTIVE", updatedAccount.getStatus());
}
再次讀取同一行數據,證實新值已如預期寫入資料庫。
4.3 刪除記錄
此測試證實deleteById()會刪除記錄,且後續查找會傳回null :
@Test
public void whenDeleteById_thenAccountIsRemoved() {
accountMapper.deleteById(2L);
Account deletedAccount = accountMapper.selectOneById(2L);
assertNull(deletedAccount);
}
但實際上,許多應用程式避免對業務資料進行實體刪除,而是傾向於採用軟刪除策略,以便保留記錄以供審計或復原。
5. 使用QueryWrapper編寫查詢
目前為止,我們已經使用映射器進行直接的 CRUD 操作。為了實現更豐富的讀取功能,MyBatis-Flex 提供了QueryWrapper ,這是一個用於在 Java 程式碼中建立 SQL 條件的流暢 API 。
5.1. 多條件排序
我們先來看一個結合了篩選和排序的查詢。這是應用程式程式碼中常見的模式,即使涉及多個條件, QueryWrapper也能保持良好的可讀性:
@Test
public void whenQueryWithFilters_thenMatchingAccountsAreReturned() {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(Account::getAge).ge(18)
.and(Account::getStatus).eq("ACTIVE")
.orderBy(column("age").desc());
List<Account> accounts = accountMapper.selectListByQuery(queryWrapper);
assertEquals(3, accounts.size());
assertEquals("emma", accounts.get(0).getUserName());
assertEquals("sarah", accounts.get(1).getUserName());
assertEquals("tom", accounts.get(2).getUserName());
}
在這種情況下,我們僅選擇狀態為活躍的成年帳戶,並按年齡降序排列。由此產生的斷言明確了排序方式,並幫助我們驗證產生的查詢是否符合我們的預期。
5.2 動態查詢
在許多實際搜尋場景中,某些篩選條件是可選的。與其建立大量的查詢變體,我們可以逐步建立QueryWrapper ,並且僅在對應的參數存在時才新增條件:
@Test
public void whenBuildingADynamicQuery_thenOnlyActiveAdultAccountsAreReturned() {
Integer minAge = 18;
String status = "ACTIVE";
QueryWrapper queryWrapper = QueryWrapper.create();
if (minAge != null) {
queryWrapper.where(Account::getAge).ge(minAge);
}
if (status != null) {
queryWrapper.and(Account::getStatus).eq(status);
}
queryWrapper.orderBy(column("id").asc());
List<Account> accounts = accountMapper.selectListByQuery(queryWrapper);
assertEquals(3, accounts.size());
assertEquals("sarah", accounts.get(0).getUserName());
assertEquals("emma", accounts.get(1).getUserName());
assertEquals("tom", accounts.get(2).getUserName());
}
這種方法既能保持查詢邏輯的彈性,又不會犧牲清晰度。
5.3 運行分頁查詢
分頁是一種常見的將大型結果集拆分成較小區塊(或頁面)的方法,而不是一次載入所有符合的行。 MyBatis-Flex 透過映射器直接支援分頁,因此我們可以請求特定的頁面和頁面大小,而無需引入單獨的分頁抽象:
@Test
public void whenPaginating_thenPageMetadataAndRecordsAreReturned() {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(Account::getAge).ge(18)
.orderBy(column("id").asc());
Page<Account> page = accountMapper.paginate(1, 2, queryWrapper);
assertEquals(2, page.getRecords().size());
assertEquals(3L, page.getTotalRow());
assertEquals(2L, page.getTotalPage());
}
上面,我們對篩選後的結果進行了分頁,並驗證了傳回的記錄和頁面元資料。這很有用,因為分頁不僅僅是限制行數。通常,我們還需要匹配記錄和頁數的總數來建立後續的應用程式邏輯。
6. 結論
在本文中,我們使用 MyBatis-Flex 建立了一個小型 Spring Boot 4 項目,並用它來示範核心持久化工作流程。為此,我們配置了 H2 資料來源,定義了實體和映射器,然後使用 JUnit 5 整合測試驗證了設定。
此外,我們也了解了 MyBatis-Flex 如何支援簡單且更複雜的資料存取模式。具體來說,我們使用了BaseMapper內建的 CRUD 操作,使用QueryWrapper建立了過濾查詢,並在執行分頁查詢的同時檢查了傳回的記錄和頁面元資料。
與往常一樣,本文的完整程式碼可在 GitHub 上找到。