Khám Phá Và Cấu Hình MyBatis-Plus Hiệu Quả Với Spring Boot

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>
Lưu ý: Nếu gặp xung đột phiên bản MyBatis, hãy xem xét khai báo loại trừ (exclusion) hoặc chỉ định rõ phiên bản tương thích.

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.

  1. Thêm cột version vào bảng database, giá trị mặc định là 1.
  2. Thêm thuộc tính version vào Class Entity và chú giải @Version.
  3. 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();
    }
}

Thẻ: Java Spring Boot MyBatis-Plus mysql orm

Đăng vào ngày 28 tháng 6 lúc 07:45