Giới thiệu về MyBatis-Plus
MyBatis-Plus (viết tắt là MP) là một công cụ nâng cao được xây dựng dựa trên framework MyBatis truyền thống. Mục tiêu chính của nó là đơn giản hóa quy trình phát triển và tăng tốc độ thực thi mà không làm thay đổi kiến trúc hiện có của dự án.
Lợi ích cốt lõi bao gồm:
- Không xâm nhập: Chỉ đóng vai trò mở rộng chức năng, không can thiệp vào cấu hình gốc của MyBatis.
- Thao tác CRUD mạnh mẽ: Tích hợp sẵn các phương thức thao tác dữ liệu phổ biến cho bảng đơn, giảm thiểu mã SQL thủ công.
- Hỗ trợ Lambda: Xây dựng điều kiện truy vấn an toàn hơn bằng cách sử dụng tham chiếu hàm, tránh lỗi sai tên trường.
- Tự động tạo khóa chính: Cung cấp nhiều chiến lược sinh ID khác nhau.
- Công cụ phân trang tích hợp: Đơn giản hóa việc lấy dữ liệu theo trang.
1. Thiết lập môi trường và cấu hình ban đầu
Để bắt đầu với MyBatis-Plus trong một dự án Spring Boot, cần thực hiện các bước chuẩn bị sau đây.
Tạo CSDL và bảng mẫu
CREATE DATABASE IF NOT EXISTS demo_db CHARACTER SET utf8mb4;
USE demo_db;
CREATE TABLE accounts (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'Mã định danh',
username VARCHAR(50) NOT NULL COMMENT 'Tên đăng nhập',
email_code VARCHAR(32) NOT NULL COMMENT 'Mã bí mật',
user_age INT(3) NOT NULL COMMENT 'Tuổi',
contact_info VARCHAR(32) NOT NULL COMMENT 'Thông tin liên lạc'
);
-- Chèn dữ liệu thử nghiệm
INSERT INTO accounts VALUES (1, 'JohnDoe', 'pass123', 25, '0912345678');
INSERT INTO accounts VALUES (2, 'JaneSmith', 'secure99', 28, '0987654321');
Kích hoạt thư viện cần thiết trong Maven
Vì MyBatis-Plus không có sẵn trong các template mặc định của IDE, bạn cần thêm thủ công vào file pom.xml.
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- Sử dụng Druid nếu cần quản lý kết nối nâng cao -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.16</version>
</dependency>
Cấu hình kết nối dữ liệu
Trong file application.yml hoặc application.properties, cấu hình đường dẫn kết nối và bật nhật ký SQL để debug.
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/demo_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Ho_Chi_Minh
username: root
password: admin_password
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # Ghi lại câu lệnh SQL ra console
global-config:
banner: false # Tắt logo khi khởi động
Xây dựng lớp thực thể (Entity)
Tạo class Java khớp với cấu trúc bảng trong cơ sở dữ liệu. Tên lớp nên tuân thủ quy tắc đặt tên PascalCase.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Accounts {
private Long id;
private String username;
private String emailCode;
private Integer userAge;
private String contactInfo;
}
Tạo tầng truy xuất dữ liệu (DAO/Mapper)
Giao diện này thừa kế từ BaseMapper để thừa hưởng các phương thức CRUD mặc định.
@Mapper // Hoặc dùng @MapperScan ở lớp khởi chạy
public interface AccountMapper extends BaseMapper<Accounts> {}
// Service layer
public interface AccountService extends IService<Accounts> {}
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Accounts> implements AccountService {}
Mẹo: Để tránh phải gắn chú giải @Mapper lên từng giao diện, hãy sử dụng @MapperScan("com.example.dao") ngay tại lớp khởi động ứng dụng Spring Boot.
2. Thực hiện truy vấn nâng cao
Phân trang dữ liệu
MyBatis-Plus hỗ trợ phân trang thông qua đối tượng IPage. Cần cài đặt interceptor tương ứng.
void testPhanTrang() {
// Tạo đối tượng trang, ví dụ trang 1, mỗi trang 5 bản ghi
IPage<Accounts> trangHienTai = new Page<>(1, 5);
// Thực hiện truy vấn
accountMapper.selectPage(trangHienTai, null);
System.out.println("Tổng số trang: " + trangHienTai.getPages());
System.out.println("Danh sách dòng: " + trangHienTai.getRecords());
}
Cấu hình Interceptor Phân Trang
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor interceptor() {
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
// Thêm plugin phân trang
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mpInterceptor;
}
}
Xây dựng điều kiện truy vấn (Wrapper)
Sử dụng các Wrapper để đóng gói logic tìm kiếm phức tạp thay vì viết SQL thuần túy.
Sử dụng LambdaQueryWrapper (Khuyên dùng)
// Tìm người dùng dưới 18 tuổi
LambdaQueryWrapper<Accounts> builder = Wrappers.lambdaQuery(Accounts.class);
builder.lt(Accounts::getUserAge, 18);
List<Accounts> ketQua = accountMapper.selectList(builder);
Sử dụng phương thức tham chiếu (Class::method) giúp tránh lỗi gõ sai tên thuộc tính khi refactor.
Điều kiện phức tạp và Dynamic SQL
Hỗ trợ chuỗi kết hợp điều kiện (Chain programming) như OR, AND.
// Tìm theo tuổi nhỏ hơn 10 HOẶC lớn hơn 30
LambdaQueryWrapper<Accounts> lqw = new LambdaQueryWrapper<>();
lqw.lt(Accounts::getUserAge, 10).or().gt(Accounts::getUserAge, 30);
// Truy vấn động dựa trên tham số đầu vào
if (paramStatus != null) {
lqw.eq(Accounts::getStatus, paramStatus);
}
// Tương đương sql: WHERE status = ?
3. Các kỹ thuật xử lý dữ liệu đặc biệt
Chiếu chọn trường (Projection)
Chỉ lấy những cột cụ thể thay vì tất cả trường (SELECT * ...) để tối ưu hiệu suất.
LambdaQueryWrapper<Accounts> wq = new LambdaQueryWrapper<>();
wq.select(Accounts::getId, Accounts::getUsername); // Chỉ lấy ID và Tên
Tra cứu thống kê và nhóm
Dùng cho các báo cáo tổng hợp. Lưu ý rằng các hàm聚合 (Aggregate) thường trả về Map chứ không phải Entity trực tiếp.
QueryWrapper<Accounts> qw = new QueryWrapper<>();
qw.select("MAX(user_age)", "AVG(user_age)"); // Hàm MAX và AVG
List<Map<String, Object>> thongKe = accountMapper.selectMaps(qw);
Bảo mật khóa chính và ánh xạ
Nếu tên cột trong cơ sở dữ liệu không khớp với tên thuộc tính trong Class:
- Sai tên cột: Dùng
@TableField(value="tên_cột_db"). - Thuộc tính không tồn tại trong DB: Dùng
@TableField(exist=false)để bỏ qua khi query. - Sai tên bảng: Dùng
@TableName("ten_bang_thuc_te").
4. Chiến lược sinh tự động ID
Quy định cách hệ thống tạo giá trị cho khóa chính.
- AUTO (Cơ sở dữ liệu tự tăng): Phù hợp cho dự án đơn máy chủ. Cấu hình
@TableId(type = IdType.AUTO). - ASSIGN_ID (Snowflake): Giá trị mặc định của MP. Tạo ID duy nhất dạng số dài, phù hợp cho kiến trúc Microservices và phân tán.
@TableId(type = IdType.ASSIGN_ID) - ASSIGN_UUID: Tạo chuỗi GUID ngẫu nhiên.
@TableId(type = IdType.ASSIGN_UUID)
5. Cơ chế Khóa lạc quan (Optimistic Lock)
Ngăn chặn ghi đè dữ liệu khi nhiều người cùng sửa một bản ghi.
- Thêm cột
versionvào bảng database, giá trị mặc định là 1. - Thêm thuộc tính
versionvào Class Entity và chú giải@Version. - Kích hoạt interceptor khóa lạc quan.
@Bean
public MybatisPlusInterceptor optimisticLocker() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
// Khi update, MP sẽ tự động kiểm tra version trong mệnh đề WHERE
6. Công cụ sinh mã tự động
MyBatis-Plus cung cấp bộ gen mã nguồn để tạo nhanh Entity, Mapper, Service và Controller từ bảng dữ liệu có sẵn.
public class AutoGen {
public static void main(String[] args) {
AutoGenerator mpg = new AutoGenerator();
// Cấu hình nguồn dữ liệu
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/demo_db?serverTimezone=UTC");
dsc.setUsername("root");
dsc.setPassword("password");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
mpg.setDataSource(dsc);
// Cấu hình đầu ra
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
gc.setAuthor("Developer");
mpg.setGlobalConfig(gc);
// Gói tin và chiến lược
PackageConfig pc = new PackageConfig();
pc.setParent("com.example.generated");
mpg.setPackageInfo(pc);
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("accounts"); // Bảng nào cần sinh thì điền vào
strategy.setEntityLombokModel(true); // Tự thêm Getter Setter/Lombok
mpg.setStrategy(strategy);
mpg.execute();
}
}