Sử dụng MyBatis

MyBatis

Câu hỏi phỏng vấn: Khác biệt giữa MyBatis và Hibernate?

  • Hibernate và MyBatis đều là các khung làm việc ORM phổ biến trong ngành công nghiệp hiện nay

  • Về tối ưu hóa SQL

  • Hibernate sử dụng ngôn ngữ truy vấn HQL (Hibernate Query Language), độc lập với cơ sở dữ liệu. Không cần viết nhiều SQL để ánh xạ hoàn toàn, nhưng sẽ tiêu tốn hiệu năng và lập trình viên không thể tối ưu hóa hiệu suất SQL. Cung cấp các tính năng như nhật ký, bộ nhớ cache, liên kết (liên kết của Hibernate mạnh hơn MyBatis).

  • MyBatis yêu cầu viết SQL thủ công, vì vậy linh hoạt hơn. Hỗ trợ SQL động, xử lý danh sách, tạo tên bảng động, hỗ trợ thủ tục lưu. Khối lượng công việc tương đối lớn.

  • Về phát triển

  • MyBatis là một khung làm việc ánh xạ bán tự động, vì MyBatis cần khớp thủ công POJO và ánh xạ SQL.

Hibernate là một khung làm việc ánh xạ toàn bộ, chỉ cần cung cấp POJO và quan hệ ánh xạ.

  • Đối với logic đơn giản, cả Hibernate và MyBatis đều có công cụ tạo mã tương ứng để tạo các phương thức DAO cơ bản.

Đối với truy vấn nâng cao, MyBatis cần viết thủ công câu lệnh SQL và ResultMap. Trong khi đó, Hibernate có cơ chế ánh xạ tốt, lập trình viên không cần quan tâm đến việc tạo SQL và ánh xạ kết quả, có thể tập trung vào quy trình kinh doanh.

  • Hibernate có cơ chế bộ nhớ cache cấp hai tốt hơn, có thể sử dụng cache bên thứ ba. MyBatis không cung cấp cơ chế cache hiệu quả.
  • MyBatis phù hợp cho các dự án internet có yêu cầu thay đổi thường xuyên, trong khi Hibernate phù hợp cho các dự án có yêu cầu rõ ràng và kinh doanh cố định.

1. Tổng quan về MyBatis

MyBatis là một khung làm việc lớp bền vững xuất sắc, được sử dụng để đơn giản hóa phát triển JDBC, bọc nội bộ JDBC.

  • MyBatis ban đầu là một dự án mã nguồn mở của Apache gọi là iBatis, năm 2010 dự án này được di chuyển từ Apache Software Foundation sang Google Code và đổi tên thành MyBatis. Năm 2013, được di chuyển sang GitHub.
  • Trang web chính thức: https://mybatis.org/mybatis-3/zh/index.html

Câu hỏi đặt ra:

(1) Lớp bền vững là gì?

  • Kiến trúc JavaEE ba lớp: Lớp giao diện, lớp nghiệp vụ, lớp bền vững.
  • Lớp bền vững là lớp code chịu trách nhiệm thao tác cơ sở dữ liệu. Trong quá trình phát triển sau này, chúng ta sẽ xem các đoạn code Java thao tác cơ sở dữ liệu là lớp bền vững, và MyBatis là khung bọc lại code JDBC.

(2) Khung làm việc là gì?

  • Khung làm việc là một phần mềm bán thành phẩm, là một tập hợp các mã cơ bản có thể tái sử dụng, chung và phổ dụng.
  • Dựa trên khung làm việc để xây dựng phần mềm giúp tăng hiệu suất, tính quy chuẩn, tính phổ dụng và khả năng mở rộng.
1.1. Nhược điểm của JDBC

Dưới đây là đoạn mã JDBC, chúng ta phân tích xem có những nhược điểm nào:

  • Mã cứng
  • Đăng ký driver, lấy kết nối có vấn đề mã cứng, nếu đổi sang cơ sở dữ liệu quan hệ khác, cần thay đổi tham số trong mã nguồn.
  • Mã dư thừa
  • Hoạt động nghiệp vụ và thao tác cơ sở dữ liệu bị trộn lẫn, hiệu suất phát triển thấp.
  • Quy trình phức tạp
  • Phải thiết lập tham số thủ công.
  • ResultSet không hỗ trợ chuyển đổi tự động giữa cơ sở dữ liệu và đối tượng thực thể, vẫn phải gán giá trị thủ công.
1.2. Ưu điểm của MyBatis
  • MyBatis bọc lại JDBC, lập trình viên chỉ cần tập trung vào câu lệnh SQL, không cần xử lý các quy trình phức tạp như tải driver, tạo kết nối (cấu hình file XML, giao MyBatis xử lý).
  • MyBatis cấu hình câu lệnh SQL thông qua XML hoặc chú thích, sau đó thực thi SQL và ánh xạ kết quả thành đối tượng Java rồi trả về.

2. Phát triển đại lý Mapper (Tập trung)

Yêu cầu: Truy vấn tất cả dữ liệu trong bảng user

Bước 1: Tạo bảng user và thêm dữ liệu
create database mybatisTest collate utf8_general_ci character set utf8;
use mybatisTest;

drop table if exists tb_user;

create table tb_user(
	id int primary key auto_increment,
	username varchar(20),
	password varchar(20),
	gender char(1),
	addr varchar(30)
);

INSERT INTO tb_user VALUES (1, 'zhangsan', '123', 'nam', 'Hà Nội');
INSERT INTO tb_user VALUES (2, 'Lý Tứ', '234', 'nữ', 'Tianjin');
INSERT INTO tb_user VALUES (3, 'Vương Ngũ', '11', 'nam', 'Xian');

Bước 2: Tạo lớp thực thể User

— Thông thường được tạo theo cơ sở dữ liệu

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String username;
    private String password;
    private String gender;
    private String addr;
}

Bước 3: Tạo giao diện UserMapper và viết phương thức
  • Tạo giao diện UserMapper trong gói com.itheima.mapper, mã như sau:
public interface UserMapper {
    //Truy vấn tất cả dữ liệu người dùng
    List<User> selectAll();
}

Bước 4: Tạo mô-đun, nhập tọa độ
<dependencies>
    <!--phụ thuộc MyBatis-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>

    <!--trình điều khiển MySQL-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.46</version>
    </dependency>

    <!--kiểm tra đơn vị-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
 
    <!--plugin phân trang MyBatis-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
    </dependency>

    <!--giải quyết lỗi mã hóa màn hình-->
    <dependency>
        <groupId>org.jboss</groupId>
        <artifactId>jboss-vfs</artifactId>
        <version>3.2.16.Final</version>
    </dependency>

    <!--thêm phụ thuộc này để in thông tin câu lệnh SQL trên màn hình-->
    <!--API nhật ký slf4j-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.20</version>
    </dependency>
    <!--phụ thuộc logback-classic-->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <!--phụ thuộc logback-core-->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.2.3</version>
    </dependency>
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
</dependencies>

Bước 5: Viết tệp cấu hình cốt lõi MyBatis

Trong thư mục resources của mô-đun, tạo tệp cấu hình MyBatis mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>


<configuration>
    <settings>
        <!--có bật cache toàn cục cấp hai hay không-->
        <setting name="cacheEnabled" value="true"/>
        <!-- có in nhật ký trên màn hình không -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>

    <!--đặt tên tắt cho tên đường dẫn đầy đủ, thường là tên lớp thực thể, đơn giản hóa viết resultType trong tệp ánh xạ-->
    <typeAliases>
        <package name="com.haoyu.pojo"/>
    </typeAliases>

    <plugins>
        <!--plugin phân trang-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

    <!--
    environments:cấu hình thông tin kết nối cơ sở dữ liệu. Có thể cấu hình nhiều environment, chuyển đổi environment khác nhau qua thuộc tính default
    -->
    <environments default="development">
        <!-- cấu hình môi trường mysql -->
        <environment id="development">
            <!--loại giao dịch, JDBC-->
            <transactionManager type="JDBC"/>
            <!-- cấu hình nguồn dữ liệu POOLED: sử dụng bể kết nối -->
            <dataSource type="POOLED">
                <!--thông tin kết nối cơ sở dữ liệu-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql:///mybatisTest?useSSL=false&amp;useServerPrepStmts=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="232104"/>
            </dataSource>
        </environment>
    </environments>


    <!-- chỉ định vị trí tệp cấu hình ánh xạ -->
    <mappers>
        <!--Chế độ đại lý Mapper tải tệp ánh xạ SQL-->
        <package name="com.haoyu.mapper"/>
    </mappers>


</configuration>

Bước 6: Viết tệp ánh xạ SQL (quản lý tập trung câu lệnh SQL)
  • Quy định: Tên tệp phải trùng với tên giao diện, giao diện Mapper và tệp ánh xạ SQL phải đặt cùng thư mục. (giao diện userMapper tương ứng với tệp userMapper.xml)
  • Thông thường một bảng tương ứng với một tệp xml

Trong thư mục resources của mô-đun, tạo tệp cấu hình ánh xạ UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>


<!--
    namespace:không gian tên, phải là tên đầy đủ của giao diện tương ứng
    id: định danh duy nhất, trong giao diện Mapper định nghĩa phương thức, tên phương thức chính là id của câu lệnh SQL trong tệp ánh xạ SQL, và giữ nguyên kiểu tham số và kiểu trả về
    resultType:kiểu dữ liệu kết quả trả về
 -->
<mapper namespace="com.haoyu.mapper.UserMapper">
    <select id="selectAll" resultType="User">
        select * from tb_user;
    </select>
</mapper>

Lưu ý đặc biệt:

  1. Vì tệp cấu hình cốt lõi MyBatis đã đặt tên tắt, resultType có thể viết ngắn gọn thành resultType="User"&, nếu chưa đặt tên thì nên viết resultType="com.itheima.pojo.User">

  2. resultType thường là kiểu lớp thực thể, nếu phương thức không có giá trị trả về thì có thể không cần viết. (kết quả truy vấn sẽ dựa vào điều này)

Bước 7: Tạo giao diện UserMapper và viết phương thức
  • Tạo giao diện UserMapper trong gói com.itheima.mapper, mã như sau:
public interface UserMapper {
    //Truy vấn tất cả dữ liệu người dùng
    List<User> selectAll();
}

Bước 8: Tạo lớp kiểm tra
/**
 * Phát triển đại lý MyBatis
 */
public class MyBatisDemo{
    public static void main(String[] args) throws IOException {
        //1. Tải tệp cấu hình cốt lõi MyBatis, lấy SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2. Lấy đối tượng SqlSession, dùng để thực thi SQL
        SqlSession sqlSession = sqlSessionFactory.openSession();
        
        //3. Thực thi SQL
        //3.1 Lấy đối tượng đại lý của giao diện UserMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = userMapper.selectAll();
        for (User user : users) {
            System.out.println(user.toString());
        }
        
        //4. Giải phóng tài nguyên
        sqlSession.close();
    }
}

Tối ưu mã: Trích xuất thành lớp công cụ

Vì mã lấy SqlSessionFactory có thể tái sử dụng, trích xuất ra thành lớp công cụ

public class SqlSessionFactoryUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        //khối tĩnh sẽ tự động chạy khi tải lớp, chỉ chạy một lần
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSessionFactory getSqlSessionFactory(){
        return sqlSessionFactory;
    }
}

Tối ưu bước 8

public class MyBatisDemo {
    //Lấy tệp cấu hình cốt lõi MyBatis qua phương thức công cụ, lấy SqlSessionFactory
    public static SqlSessionFactory factory= SqlSessionFactoryUtils.getSqlSessionFactory();

    public static void main(String[] args) throws IOException {

        //Lấy đối tượng SqlSession, dùng để thực thi SQL
        SqlSession sqlSession = factory.openSession();
        
        //Lấy đối tượng đại lý của giao diện UserMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //Thực thi SQL
        List<User> users = userMapper.selectAll();

        for (User user : users) {
            System.out.println(user.toString());
        }
        
        //Giải phóng tài nguyên
        sqlSession.close();
    }
}

Mở rộng: Vai trò của SqlSessionFactory (trọng tâm)

SqlSessionFactory là lớp nhà máy trong MyBatis, dùng để tạo đối tượng SqlSession. Đây là một trong những đối tượng quan trọng nhất của khung MyBatis, quản lý cấu hình và ánh xạ của MyBatis, và tạo đối tượng SqlSession dựa trên thông tin này để tương tác với cơ sở dữ liệu.

Quy trình tạo và sử dụng SqlSessionFactory thường như sau:

  • Đọc tệp cấu hình MyBatis: SqlSessionFactory đọc thông tin cấu hình từ tệp cấu hình MyBatis.
  • Tạo bể kết nối cơ sở dữ liệu: SqlSessionFactory tạo bể kết nối cơ sở dữ liệu dựa trên thông tin cấu hình, để tăng hiệu suất thao tác cơ sở dữ liệu.
  • Tạo đối tượng SqlSessionFactory: SqlSessionFactory tạo đối tượng SqlSessionFactory dựa trên thông tin cấu hình, dùng để tạo đối tượng SqlSession.
  • Tạo đối tượng SqlSession: Ứng dụng tạo đối tượng SqlSession qua SqlSessionFactory, dùng để thực thi câu lệnh SQL, cam kết giao dịch v.v.
  • Thao tác cơ sở dữ liệu: Thực hiện các thao tác cơ sở dữ liệu như truy vấn, chèn, cập nhật, xóa thông qua đối tượng SqlSession.
  • Cam kết giao dịch hoặc hoàn tác giao dịch: Nếu ứng dụng cần cam kết hoặc hoàn tác giao dịch, gọi phương thức commit hoặc rollback của SqlSession.
  • Đóng SqlSession: Sau khi thao tác xong, ứng dụng cần gọi phương thức close của SqlSession để đóng đối tượng SqlSession, giải phóng tài nguyên kết nối cơ sở dữ liệu.

Mở rộng: Kiểm soát giao dịch trong MyBatis

SqlSession sqlSession = sqlSessionFactory.openSession(false);  //tắt cam kết giao dịch tự động
try {
    // Thực thi thao tác cơ sở dữ liệu
    sqlSession.insert("insertUser", user);
    sqlSession.update("updateUser", user);
    
    // Cam kết giao dịch
    sqlSession.commit();
} catch (Exception e) {
    // Hoàn tác giao dịch
    sqlSession.rollback();
} finally {
    // Đóng SqlSession
    sqlSession.close();
}

3. Yêu cầu khi sử dụng đại lý Mapper

  • Định nghĩa giao diện Mapper cùng tên với tệp ánh xạ SQL, và đặt giao diện Mapper cùng tệp ánh xạ SQL vào cùng một thư mục.
  • Thiết lập thuộc tính namespace của tệp ánh xạ SQL thành tên đầy đủ của giao diện Mapper
  • Trong giao diện Mapper định nghĩa phương thức, tên phương thức chính là id của câu lệnh SQL trong tệp ánh xạ SQL, và giữ nguyên kiểu tham số và kiểu trả về.

4. Nâng cao hiểu biết về MyBatis

Mở rộng: Cài đặt plugin MyBatisX

Tính năng:

  • Dây đỏ biểu thị tệp cấu hình ánh xạ, dây xanh biểu thị giao diện mapper, nhấp vào chim sẻ để chuyển đổi qua lại.
  • Tự động tạo statement

4.1. Thực hiện thao tác CRUD (trọng tâm)

—Lưu ý đặc biệt: Khi thực hiện thao tác thêm/xóa/sửa, phải cam kết giao dịch.

4.1.1. Chuẩn bị dữ liệu

Bảng thương hiệu:

-- Xóa bảng tb_brand
drop table if exists tb_brand;
-- Tạo bảng tb_brand
create table tb_brand
(
    -- id khóa chính
    id           int primary key auto_increment,
    -- tên thương hiệu
    brand_name   varchar(20),
    -- tên công ty
    company_name varchar(20),
    -- trường sắp xếp
    ordered      int,
    -- thông tin mô tả
    description  varchar(100),
    -- trạng thái: 0: vô hiệu hóa  1: kích hoạt
    status       int
);
-- Thêm dữ liệu
insert into tb_brand (brand_name, company_name, ordered, description, status)
values ('Ba con sóc', 'Công ty cổ phần ba con sóc', 5, 'ngon miệng không gây nóng', 0),
       ('Huawei', 'Công ty công nghệ Huawei', 100, 'Huawei cam kết đưa thế giới số đến từng cá nhân, từng gia đình, từng tổ chức, xây dựng thế giới thông minh kết nối vạn vật', 1),
       ('Xiaomi', 'Công ty công nghệ Xiaomi', 50, 'are you ok', 1);

Lớp thực thể Brand

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Brand {
    // id khóa chính
    private Integer id;
    // tên thương hiệu
    private String brandName;
    // tên công ty
    private String companyName;
    // trường sắp xếp
    private Integer ordered;
    // thông tin mô tả
    private String description;
    // trạng thái: 0: vô hiệu hóa  1: kích hoạt
    private Integer status;
}

4.1.2. Vấn đề tên trường cơ sở dữ liệu và lớp thực thể không khớp

Vì MyBatis ở cấp dưới lấy dữ liệu, sẽ tạo đối tượng tương ứng (như Brand) qua phản chiếu và đóng gói dữ liệu, nếu tên trường cơ sở dữ liệu và lớp thực thể không khớp sẽ dẫn đến đóng gói thất bại, khiến dữ liệu không khớp trở thành null.

Cách 1: Sử dụng đoạn SQL
  • Trích xuất đoạn SQL cần lặp lại vào thẻ sql
<sql id="brand_column">
	id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>

Giá trị id là định danh duy nhất, khi tham chiếu cũng sử dụng giá trị này.

  • Trong câu SQL gốc tiến hành tham chiếu

Sử dụng thẻ include để tham chiếu đoạn SQL trên, và refid chỉ định id của đoạn SQL.

<select id="selectAll" resultType="brand">
    select
    <include refid="brand_column" />
    from tb_brand;
</select>

Cách 2: Sử dụng resultMap để giải quyết (khuyến nghị sử dụng)
  • Trong tệp cấu hình ánh xạ dùng resultMap định nghĩa quan hệ ánh xạ giữa trường và thuộc tính

Ví dụ:

<mapper namespace="com.haoyu.mapper.BrandMapper">
    <!-- type biểu thị loại giá trị trả về -->
    <resultMap id="brandResultMap" type="brand">
        <!--
                id:hoàn thành ánh xạ trường khóa chính
                    column:tên cột bảng
                    property:tên thuộc tính lớp thực thể
                result:hoàn thành ánh xạ trường thông thường
                    column:tên cột bảng
                    property:tên thuộc tính lớp thực thể
         -->  
    <!--Nói cho MyBatis rằng giá trị truy vấn được của brand_name sẽ được gán cho thuộc tính brandName của đối tượng Brand được tạo phản chiếu -->
        <result column="brand_name" property="brandName"/>  
        <result column="company_name" property="companyName"/>
    </resultMap>

    <select id="selectAll" resultMap="brandResultMap"> //Lưu ý đặc biệt: Lúc này nên dùng resultMap
        select * from tb_brand;
    </select>
</mapper>

  • Sau khi cấu hình, câu SQL có thể viết bình thường
  • Lưu ý đặc biệt: Lúc này resultType="Brand" cần thay thành resultMap="tên id của resultMap">

4.1.3. Cung cấp id để truy vấn dữ liệu

1. Viết phương thức giao diện
//Truy vấn theo Id
Brand selectById(int id);

2. Viết câu lệnh SQL
<select id="selectById"  resultMap="brandResultMap">
    select *
    from tb_brand where id = #{id};
</select>
//Lưu ý: Vì sử dụng quan hệ ánh xạ resultMap, nên dùng 'resultMap' thay vì 'resultType'

4.1.3.1. Dấu chấm hỏi tham số

MyBatis cung cấp hai loại dấu chấm hỏi tham số:

  • #{}` : Khi thực thi SQL, sẽ thay thế #{} bằng ?, tự động thiết lập giá trị tham số, sử dụng PreparedStatement ở cấp độ thấp.
  • ${}: Nối trực tiếp dữ liệu vào câu lệnh SQL, có rủi ro tiêm nhiễm, không khuyến nghị sử dụng.
4.1.3.2. Sử dụng parameterType

Đối với phương thức giao diện Mapper có tham số, chúng ta nên cấu hình ParameterType trong tệp cấu hình ánh xạ để chỉ định kiểu tham số. Tuy nhiên, thuộc tính này có thể bỏ qua.

<select id="selectById" parameterType="int" resultMap="brandResultMap">
    select *
    from tb_brand where id = ${id};
</select>

4.1.3.3. Xử lý trường đặc biệt trong câu lệnh SQL

4.1.4. Truy vấn đa điều kiện (trọng tâm)

Bước 1: Viết phương thức giao diện

Chức năng này có ba tham số, cần xem xét cách định nghĩa tham số khi thiết kế giao diện. MyBatis có nhiều cách thực hiện cho tham số đa dạng:

  • Sử dụng @Param("tên tham số") đánh dấu từng tham số, trong tệp ánh xạ cần dùng #{tên tham số} để thay thế.
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);

  • Đóng gói nhiều tham số thành một đối tượng thực thể, dùng đối tượng này làm tham số phương thức giao diện. Cách này yêu cầu trong SQL của tệp cấu hình ánh xạ khi dùng #{nội dung}, nội dung bên trong phải trùng với tên thuộc tính của lớp thực thể.
List<Brand> selectByCondition(Brand brand);

  • Đóng gói nhiều tham số vào tập hợp map, dùng tập hợp map làm tham số phương thức giao diện. Cách này yêu cầu trong SQL của tệp cấu hình ánh xạ khi dùng #{nội dung}, nội dung bên trong phải trùng với tên khóa trong tập hợp map.
List<Brand> selectByCondition(Map map);

Bước 2: Viết câu lệnh SQL
<select id="selectByCondition" resultMap="brandResultMap">
    select *
    from tb_brand
    where status = #{status}
    and company_name like #{companyName}
    and brand_name like #{brandName}
</select>

Bước 3: Viết phương thức kiểm tra

//Thông qua phương thức công cụ tải tệp cấu hình cốt lõi MyBatis, lấy SqlSessionFactory
public static SqlSessionFactory factory= SqlSessionFactoryUtils.getSqlSessionFactory();

//Truy vấn đa điều kiện
@Test
public void testSelectByCondition() throws IOException {
    //Nhận tham số
    int status = 1;
    String companyName = "Huawei";
    String brandName = "Huawei";

    //Xử lý tham số
    companyName = "%" + companyName + "%";  //Vì là truy vấn mờ, nên cần xử lý
    brandName = "%" + brandName + "%";

    //Lấy đối tượng SqlSession
    SqlSession sqlSession = factory.openSession();
    //Lấy đối tượng đại lý giao diện Mapper
    BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

    //Thực thi phương thức
    Cách 1: Gọi phương thức có tham số @Param
    List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);
    
    Cách 2: Gọi phương thức có tham số là đối tượng thực thể
    //Đóng gói đối tượng
    Brand brand = new Brand();
        brand.setStatus(status);
        brand.setCompanyName(companyName);
        brand.setBrandName(brandName);
    List<Brand> brands = brandMapper.selectByCondition(brand);

    Cách 3: Gọi phương thức có tham số là tập hợp map
    Map map = new HashMap();
    map.put("status" , status);
    map.put("companyName", companyName);
    map.put("brandName" , brandName);
    List<Brand> brands = brandMapper.selectByCondition(map);
    System.out.println(brands);

    //Giải phóng tài nguyên
    sqlSession.close();
}

Mở rộng: Vai trò của chú thích @Param

—Đặt tên tham số.

Ví dụ:

Phương thức trong mapper

public User selectUser(@Param("userName") String name,@Param("password") String pwd);

Ánh xạ sang thẻ XML

<select id="selectUser" resultMap="User">  
   select * from user  where user_name = #{userName} and user_password=#{password}  
</select>

  • Trong đó where user_name = #{userName} and user_password = #{password} giá trị userName và password được lấy từ chú thích @Param(), chính là giá trị của tham số hình thức String name và String pwd trong phương thức.
  • Nói cách khác, tên trong placeholder SQL cần khớp với tên được đặt bởi chú thích @Param.
4.1.4.1. SQL động (trọng tâm)
Trường hợp 1: Nhiều điều kiện

Cần sử dụng thẻ if và where

Cách thực hiện chức năng trên có vấn đề lớn: Khi người dùng nhập điều kiện, có thể không điền đầy đủ các điều kiện, lúc này câu lệnh SQL không thể viết như vậy:

Người dùng chỉ nhập trạng thái hiện tại, câu lệnh SQL sẽ là:

select * from tb_brand where status = #{status}

Nếu người dùng chỉ nhập tên công ty, câu lệnh SQL sẽ là:

select * from tb_brand where company_name like #{companName}

Nếu người dùng nhập cả trạng thái hiện tại và tên công ty, câu lệnh SQL lại khác:

select * from tb_brand where status = #{status} and company_name like #{companName}

Đối với yêu cầu trên, MyBatis hỗ trợ mạnh mẽ SQL động:

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
  • Thẻ if: Điều kiện kiểm tra

  • thuộc tính test: Biểu thức logic

  • Thẻ where

  • Tác dụng:

  • Thay thế từ khóa where

  • Tự động loại bỏ từ khóa and đầu tiên

  • Nếu tất cả tham số không có giá trị thì không thêm từ khóa where

<select id="selectByCondition" resultMap="brandResultMap">
    select *
    from tb_brand
    <where>
        <if test="status != null">
            and status = #{status}
        </if>
        <if test="companyName != null and companyName != '' ">
            and company_name like #{companyName}
        </if>
        <if test="brandName != null and brandName != '' ">
            and brand_name like #{brandName}
        </if>
    </where>
</select>

Lưu ý: Khi kết hợp thẻ if và where, cần thêm từ khóa and trước mỗi điều kiện.

Câu lệnh SQL trên sẽ tự động nối theo giá trị tham số truyền vào, nếu lúc này status và companyName có giá trị thì chỉ nối hai điều kiện này.

  • Thẻ choose
  • Tác dụng: Tương đương if, nhưng có thể kết hợp với thẻ when và otherwise (tương đương else).

Ví dụ:

<choose>
<when test="status != null">
    and status = #{status}
</when>
<otherwise></otherwise>
</choose>

//Tương đương java
if(test="status != null"){
    and status = #{status}
}else{

}

Trường hợp 2: Một điều kiện

Cần sử dụng thẻ choose (when, otherwise) để thực hiện

Như đã nói ở trên, khi truy vấn chỉ có thể chọn một trong ba điều kiện là tên thương hiệu, trạng thái hiện tại, tên công ty, nhưng người dùng chọn điều kiện nào thì chúng ta không thể xác định. Đây thuộc về câu lệnh SQL động một điều kiện.

Viết phương thức giao diện

//Truy vấn một điều kiện
List<Brand> selectByConditionSingle(Brand brand);

Viết câu lệnh SQL

<select id="selectByConditionSingle" resultMap="brandResultMap">
    select *
    from tb_brand
    <where>
        <choose><!--tương đương switch-->
            <when test="status != null"><!--tương đương case-->
                status = #{status}
            </when>
            <when test="companyName != null and companyName != '' "><!--tương đương case-->
                company_name like #{companyName}
            </when>
            <when test="brandName != null and brandName != ''"><!--tương đương case-->
                brand_name like #{brandName}
            </when>
        </choose>
    </where>
</select>

Viết phương thức kiểm tra

Trong lớp MyBatisTest trong gói com.itheima.mapper dưới thư mục test/java định nghĩa phương thức kiểm tra

//Thông qua phương thức công cụ tải tệp cấu hình cốt lõi MyBatis, lấy SqlSessionFactory
public static SqlSessionFactory factory= SqlSessionFactoryUtils.getSqlSessionFactory();

@Test
public void testSelectByConditionSingle() throws IOException {
    //Nhận tham số
    String companyName = "Huawei";

    // Xử lý tham số
    companyName = "%" + companyName + "%";

    //Đóng gói đối tượng
    Brand brand = new Brand();
    brand.setCompanyName(companyName);  //Chỉ có điều kiện đơn

    //Lấy đối tượng SqlSession
    SqlSession sqlSession = factory.openSession();
    //Lấy đối tượng đại lý giao diện Mapper
    BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
    //Thực thi phương thức
    List<Brand> brands = brandMapper.selectByConditionSingle(brand);
    System.out.println(brands);

    //Giải phóng tài nguyên
    sqlSession.close();
}

Kết quả thực thi phương thức kiểm tra như sau:

4.1.5. Thêm dữ liệu

Viết phương thức giao diện

//Thêm dữ liệu
void add(Brand brand);

Viết câu lệnh SQL

—Vì id thường tự tăng, nên không cần thêm

<insert id="add">
    insert into tb_brand (brand_name, company_name, ordered, description, status)
    values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>

Viết phương thức kiểm tra

//Thông qua phương thức công cụ tải tệp cấu hình cốt lõi MyBatis, lấy SqlSessionFactory
public static SqlSessionFactory factory= SqlSessionFactoryUtils.getSqlSessionFactory();

@Test
public void testAdd() throws IOException {
    //Nhận dữ liệu và đóng gói đối tượng
    Brand brand = new Brand("Hao Yu She","Hao Yu Company",20,"Đây là một công ty tuyệt vời",1);
    
    //Lấy đối tượng SqlSession
    SqlSession sqlSession = factory.openSession(true);  //Bật cam kết giao dịch tự động, trường hợp này không cần cam kết thủ công
    //Lấy đối tượng đại lý giao diện Mapper
    BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
    //Thực thi phương thức
    brandMapper.add(brand);
    
    //Giải phóng tài nguyên
    sqlSession.close();
    }
}

4.1.5.1. Trả về khóa chính

—Đôi khi sau khi thêm dữ liệu, chúng ta sẽ sử dụng khóa chính của nó.

Viết phương thức giao diện

//Thêm dữ liệu
int add(Brand brand);

Viết câu lệnh SQL

Thêm thuộc tính sau vào thẻ insert:

  • useGeneratedKeys: có lấy giá trị khóa chính tự tăng không. true là có
  • keyProperty: chỉ định gói giá trị khóa chính vào thuộc tính nào
<insert id="add"  useGeneratedKeys="true" keyProperty="id">
    insert into tb_brand (brand_name, company_name, ordered, description, status)
    values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>

Lúc này sau khi thực thi phương thức, đối tượng brand sẽ có giá trị id

brandMapper.addData(brand);
int id=brand.getId();

4.1.6. Sửa dữ liệu

Khi sửa dữ liệu trên trang web, nếu ô nhập không có nội dung, nên giữ nguyên giá trị trường cũ.

Viết phương thức giao diện

//Sửa dữ liệu
void update(Brand brand);

Viết câu lệnh SQL

<update id="update">
    update tb_brand
    <set>
        <trim suffixOverrides=",">   <!--dùng để loại bỏ "," thừa-->
        <if test="brandName != null and brandName != ''">
            brand_name = #{brandName},
        </if>
        <if test="companyName != null and companyName != ''">
            company_name = #{companyName},
        </if>
        <if test="ordered != null">
            ordered = #{ordered},
        </if>
        <if test="description != null and description != ''">
            description = #{description},
        </if>
        <if test="status != null">
            status = #{status}
        </if>
        </trim>
    </set>
    where id = #{id}
</update>

Viết phương thức kiểm tra

//Thông qua phương thức công cụ tải tệp cấu hình cốt lõi MyBatis, lấy SqlSessionFactory
public static SqlSessionFactory factory= SqlSessionFactoryUtils.getSqlSessionFactory();

@Test
public void testUpdate(){
    int id=4;

    //Đóng gói đối tượng
    Brand brand = new Brand(null,null,10,"Đây là một công ty tuyệt vời",2);
    brand.setId(id);
    //Lấy đối tượng SqlSession
    SqlSession sqlSession = factory.openSession(true);
    //Lấy đối tượng đại lý giao diện Mapper
    BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
    //Thực thi phương thức
    brandMapper.updateDnata(brand);

    //Giải phóng tài nguyên
    sqlSession.close();
}

4.1.7. Xóa dữ liệu hàng loạt

—Chủ yếu sử dụng thẻ foreach

Các thuộc tính chính của thẻ foreach:

  • collection:
  • collection="list" tham số truyền vào là tập hợp list
  • collection="array" tham số truyền vào là tập hợp array
  • collection="map" tham số truyền vào là tập hợp map
  • item: biến tạm thời trong vòng lặp, tương đương với tên biến tạm thời khi lặp
  • separator: phân cách sau mỗi lần lặp
  • open: chuỗi bắt đầu của nội dung lặp.
  • close: chuỗi kết thúc của nội dung lặp.

Viết phương thức giao diện

void delete(Integer[] ids);

Viết câu lệnh SQL

<delete id="delete"  >
  delete from tb_brand
  <where>
    id in
    <foreach collection="array" item="id"  separator=","  open="("   close=")">
    #{id}
    </foreach>
  </where>
</delete>

Lưu ý: Câu lệnh này tương đương với delete from tb_brand where id in( , , , , );

Viết phương thức kiểm tra

 @Test
    public void testDelete(){
        Integer[] ids={1,2,3};

        //Lấy đối tượng SqlSession
        SqlSession sqlSession = factory.openSession(true);
        //Lấy đối tượng đại lý giao diện Mapper
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //Gọi phương thức khi có dữ liệu, tránh lỗi khi không có dữ liệu
        if(ids.length>0)   brandMapper.delete(ids);
        sqlSession.close();
    }

5. Phát triển bằng chú thích

—Chú thích được dùng để thay thế cách cấu hình bằng tệp ánh xạ, vì vậy khi dùng chú thích, không cần viết statement tương ứng trong tệp ánh xạ.

MyBatis cung cấp các chú thích tương ứng cho các thao tác CRUD:

  • Truy vấn: @Select
  • Thêm: @Insert
  • Sửa: @Update
  • Xóa: @Delete

Thêm chú thích vào phương thức selectById của giao diện BrandMapper

@Select("select * from tb_brand where id=#{id}")
Brand selectById(int id);

Phương thức kiểm tra giống như trước, sử dụng trực tiếp.

Tổng kết: Chú thích dùng để hoàn thành các câu lệnh SQL đơn giản, đối với thao tác phức tạp, tốt nhất dùng XML để ánh xạ câu lệnh

6. Phân trang trong MyBatis

6.1. Sử dụng plugin phân trang MyBatis

Thêm phụ thuộc:

<!--plugin phân trang MyBatis-->
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>5.2.0</version>
</dependency>
 
<!--giải quyết lỗi mã hóa màn hình-->
<dependency>
    <groupId>org.jboss</groupId>
    <artifactId>jboss-vfs</artifactId>
    <version>3.2.16.Final</version>
</dependency>

Cấu hình plugin trong tệp cấu hình cốt lõi MyBatis:

<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper">
    </plugin>
</plugins>

Trước khi thực hiện truy vấn, sử dụng PageHelper.startPage(int pageNum,int pageSize) để bật phân trang:

pageNum: số trang hiện tại, bắt đầu từ 1

pageSize: số lượng hiển thị trên mỗi trang

@Test
public void selectAll() {
    //Lấy đối tượng SqlSession, dùng để thực thi sql
    SqlSession sqlSession = factory.openSession();
    //Lấy đối tượng đại lý giao diện BrandMapper
    BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);

    //Bật plugin phân trang, plugin sẽ tự động thêm limit vào cuối câu SQL
    PageHelper.startPage(1, 3);
    //Thực thi phương thức SQL
    List<Brand> brands = brandMapper.selectAll();
    brands.forEach(brand -> System.out.println(brand));
    
    //Lấy dữ liệu phân trang
    PageInfo<Brand> page = new PageInfo<>(brands);
    System.out.println(page.getPageNum());  //Lấy số trang hiện tại
    System.out.println(page.getSize());  //Lấy số lượng hiển thị trên mỗi trang

    //Giải phóng tài nguyên
    sqlSession.close();
}

Sau khi truy vấn và lấy được tập hợp list, sử dụng PageInfo pageInfo = new PageInfo<>(list list,int navigatePages) để lấy dữ liệu phân trang.

list: dữ liệu sau khi phân trang

navigatePages: số lượng trang điều hướng

Dữ liệu thường dùng:

pageNum: số trang hiện tại

pageSize: số lượng hiển thị trên mỗi trang

size: số lượng thực tế hiển thị trên trang hiện tại

total: tổng số bản ghi

pages: tổng số trang

prePage: số trang trước

nextPage: số trang sau

isFirstPage/isLastPage: có phải trang đầu/tận cùng không

hasPreviousPage/hasNextPage: có tồn tại trang trước/sau không

6.2. Phân trang trong SpringBoot

  • Nhập tọa độ
<!--Tọa độ PageHelper-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.5</version>
</dependency>

  • Viết mã
//Truy vấn phân trang bài viết
@Override
public Result<PageBean<Article>> list(Integer pageNum, Integer pageSize,
                                      Integer categoryId, String state) {
    //Tạo đối tượng PageBean
    PageBean<Article> pageBean=new PageBean<>();

    //Bật plugin phân trang, plugin sẽ tự động thêm limit vào cuối câu SQL
    PageHelper.startPage(pageNum,pageSize);

    //Gọi mapper
    Map<String, Object> map=ThreadLocalUtil.get();
    Integer userId = (Integer) map.get("id");
    List<Article> lists= articleMapper.list(userId,categoryId,state);
    //Page cung cấp phương thức, có thể lấy tổng số bản ghi và dữ liệu trang hiện tại sau khi phân trang
    Page<Article> page= (Page<Article>) lists;

    pageBean.setTotal(page.getTotal());
    pageBean.setItems(lists);

    return Result.success(pageBean);
}

7. Truy vấn phức tạp trong MyBatis (trọng tâm)

7.1. Chuẩn bị dữ liệu

#Tạo bảng lớp
create table class
(
class_id int not null PRIMARY KEY AUTO_INCREMENT ,
class_name VARCHAR(15) not null
);

#Tạo bảng sinh viên
create table student
(
stu_id int not null PRIMARY KEY AUTO_INCREMENT,
stu_name VARCHAR(15) not null,
class_id int not null
);
#Thiết lập một số dữ liệu
insert into class VALUES (1,'Lớp 101');
insert into class VALUES (2,'Lớp 102');
insert into student VALUES(1,'Haoyu',1);
insert into student VALUES(2,'Sijia',1);
insert into student VALUES(3,'Tanlin',2);
insert into student VALUES(4,'Xiaocui',2);

Tạo lớp thực thể

@Data
@NoArgsConstructor
@AllArgsConstructor
public class StuClass {
    private Integer id;
    private String  name;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private Integer id;
    private String name;
    private Integer classId;
}

7.2. Mối quan hệ một-nhiều (association)

Nhiều sinh viên tương ứng một lớp học, trong trường hợp một-nhiều, thêm thuộc tính ClassRoom vào lớp Stdent

public class Student {
    private Integer id;
    private String name;
    private Integer classId;
    //Thêm thuộc tính đối tượng một bên
    private StuClass stuClass;
}

Định nghĩa giao diện StudentMapper

public interface StudentMapper {
    List<Student> selectAllStudent();
}

Viết phương thức XML

Cách 1:

Viết trong StudentMapper.xml

<mapper namespace="com.haoyu.mapper.StudentMapper">

    <select id="selectAllStudent" resultMap="selectAllRes">
    select * from student s left join class c on s.class_id=c.class_id
    </select>

    <resultMap id="selectAllRes" type="Student">
        <id column="stu_id" property="id"/>
        <result column="stu_name" property="name"/>
        <result column="class_id" property="classId"/>
        
        <!-- property biểu thị tên thuộc tính lớp, javaType biểu thị kiểu thuộc tính -->
        <association property="stuClass" javaType="StuClass">
            <id column="class_id" property="id"/>
            <result column="class_name" property="name"/>
        </association>
    </resultMap>
        
</mapper>

Nhược điểm: Cần cấu hình thủ công tất cả các trường ánh xạ, nếu không sẽ là null, thích hợp cho các truy vấn ít trường.

Kết quả:

Cách 2:

Viết trong StudentMapper.xml

<mapper namespace="com.haoyu.mapper.StudentMapper">
    
    <select id="selectAllStudent" resultMap="selectAllRes">
          select * from student;
    </select>

    <resultMap id="selectAllRes" type="Student">
                <id column="stu_id" property="id"/>
                <result column="stu_name" property="name"/>
                <result column="class_id" property="classId"/>
 <!-- Khi truy vấn thông tin sinh viên, sẽ dùng giá trị column truyền vào phương thức selectClass được chỉ định, kết quả truy vấn của selectClass sẽ được gán cho biến thành viên được chỉ định bởi property -->
        <association property="stuClass" column="class_id"  
        <!-- Sử dụng câu truy vấn ngoài -->
        select="com.haoyu.mapper.ClassMapper.selectClass"></association>
    </resultMap>
        
</mapper>

Viết trong ClassMapper.xml

<mapper namespace="com.haoyu.mapper.ClassMapper">
    
    <select id="selectClass" resultMap="selectClassRes">
          select * from class where class_id=#{class_id}
    </select>

    <resultMap id="selectClassRes" type="StuClass">
        <id column="class_id" property="id"/>
        <result column="class_name" property="name"/>
    </resultMap>
        
</mapper>

Kết quả:

7.2. Mối quan hệ một-nhiều (collection)

Một giáo viên có nhiều sinh viên, trong trường hợp một-nhiều, thêm thuộc tính List vào lớp StuClass

public class StuClass {
    private Integer id;
    private String  name;
    
    //Thêm thuộc tính tập hợp nhiều bên
    private List<Student> students;
}

Viết phương thức giao diện

public interface StudentMapper {
    List<StuClass> selectAllClass();
}

Viết phương thức XML

Cách 1:

Viết trong ClassMapper.xml

<mapper namespace="com.haoyu.mapper.ClassMapper">
    
<select id="selectAllClass" resultMap="selectAllClassRes">
        select * from class c left join student s on s.class_id=c.class_id
 </select>

 <resultMap id="selectAllClassRes" type="StuClass">
     <id column="class_id" property="id"/>
     <result column="class_name" property="name"/>
        <collection property="students" ofType="Student">
           <id column="stu_id" property="id"/>
           <result column="stu_name" property="name"/>
           <result column="class_id" property="classId"/>
        </collection>
 </resultMap>
            
</mapper>

Kết quả:

Cách 2:

Viết trong ClassMapper.xml

<mapper namespace="com.haoyu.mapper.ClassMapper">
    
<select id="selectAllClass" resultMap="selectAllClassRes">
     select * from class
</select>

<resultMap id="selectAllClassRes" type="StuClass">
     <id column="class_id" property="id"/>
     <result column="class_name" property="name"/>
     <collection property="students" column="class_id" select="com.haoyu.mapper.StudentMapper.selectAllStu"></collection>
</resultMap>
         
</mapper>

Viết trong StudentMapper.xml

<mapper namespace="com.haoyu.mapper.StudentMapper">
  
<select id="selectAllStu" resultMap="selectAllStuRes">
     select * from student where class_id = #{class_id}
</select>

<resultMap id="selectAllStuRes" type="Student">
      <id column="stu_id" property="id"/>
      <result column="stu_name" property="name"/>
      <result column="class_id" property="classId"/>
</resultMap>
    
</mapper>

7.3. Truy vấn mờ

Viết phương thức giao diện

List<Student> selectStu(String str);

Viết phương thức XML

<select id="selectStu" resultMap="selectAllStuRes">
    select * from t_user where username like '%${username}%';
    select * from student where stu_name like "%"#{str}"%"
    select * from student where stu_name like concat('%',concat(#{str},'%'))
</select>

Mở rộng: So sánh ngày tháng trong MyBatis

Viết phương thức giao diện

List<New> selectByDate(@Param("startTime") String startTime, @Param("endTime") String endTime);

Định dạng XML

SELECT * FROM NEWS WHERE <![CDATA[ create_time >= #{startTime} AND create_time < #{endTime} ]]>

8. Bộ nhớ cache trong MyBatis

—Bộ nhớ cache là việc lưu trữ dữ liệu trong RAM, khi đọc không cần đọc từ đĩa, vì vậy nhanh hơn.

—MyBatis để giảm áp lực cơ sở dữ liệu và nâng cao hiệu suất, cung cấp cơ chế cache hai cấp:

—https://blog.csdn.net/weixin_45486926/article/details/124883009

8.1. Cache cấp một

  • MyBatis mặc định bật, cache cấp SqlSession (chỉ hiệu lực trong cùng một SqlSession)
  • SqlSession khi được làm mới hoặc đóng, sẽ xóa cache cấp một.
  • Cơ sở hạ tầng dựa trên HashMap
  • Khi thao tác cơ sở dữ liệu cần tạo đối tượng sqlSession, trong đối tượng có cấu trúc dữ liệu HashMap bản địa để cache dữ liệu. Dữ liệu cache giữa các phiên SQL khác nhau (sqlSession) là không ảnh hưởng lẫn nhau.

Quy trình làm việc:

  • Lần đầu truy vấn thông tin người dùng có id là 1, trước tiên kiểm tra cache xem có thông tin người dùng id=1 không, nếu không có thì truy vấn từ cơ sở dữ liệu. Sau khi có thông tin người dùng, lưu trữ vào cache cấp một.
  • Lần thứ hai truy vấn thông tin người dùng có id là 1, trước tiên kiểm tra cache xem có thông tin người dùng id=1 không, nếu có thì lấy trực tiếp từ cache.

Ví dụ:

@Test
public void test(){
    SqlSession sqlSession = ssf.openSession();
    IDeptMapper mapper = sqlSession.getMapper(IDeptMapper.class);
    //Lần đầu truy vấn id=10
    Dept dept = mapper.selectById(10);
    System.out.println(dept);
    //Lần hai truy vấn id=10
    Dept dept2 = mapper.selectById(10); //Không yêu cầu gọi cùng phương thức, chỉ cần câu SQL cuối cùng giống nhau sẽ kích hoạt cache cấp hai
    System.out.println(dept2);
}

  • Lưu ý: Hai lần truy vấn phải được thực hiện trong cùng một sqlSession, nếu không sẽ không kích hoạt cache cấp một của MyBatis. Khi tích hợp MyBatis với Spring, kiểm soát giao dịch ở service, gọi service hai lần sẽ không kích hoạt cache cấp một, vì sqlSession sẽ đóng sau lần gọi thứ hai.

8.2. Cache cấp hai

  • Phải bật thủ công, cache cấp mapper (cùng namespace dùng chung một cache), chia sẻ giữa nhiều phiên SQL (sqlSession).
  • Nhiều sqlSession thực hiện cùng một câu lệnh SQL trong mapper, các sqlSession có thể dùng chung cache cấp hai, cache cấp hai vượt qua sqlSession.
  • Cơ chế cache giống cache cấp một.

Quy trình làm việc:

  • Khi một sqlseesion thực hiện một lần select, sau khi đóng session này, sẽ lưu kết quả truy vấn vào cache cấp hai.
  • Khi một sqlsession khác thực hiện cùng select, đầu tiên sẽ kiểm tra cache cấp hai, nếu không có dữ liệu tương ứng trong cache cấp hai, tiếp tục kiểm tra cache cấp một, nếu không có trong cache cấp một, cuối cùng truy vấn cơ sở dữ liệu, giảm áp lực cơ sở dữ liệu và nâng cao hiệu suất.

Quy trình bật:

  • Cấu hình bật cache cấp hai qua application.yml
# cấu hình MyBatis
mybatis:
  configuration:
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      #bật cache cấp hai của MyBatis
      cache-enabled: true

hoặc cấu hình cache cấp hai qua tệp cấu hình MyBatis

<setting name="cacheEnabled" value="true"/>

  • Thêm vào tệp xxxMapper.xml
//Thêm bên ngoài, bật toàn cục
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"/>
//Thiết lập thuộc tính useCache="true" để bật cho câu lệnh hiện tại

  • Đối tượng cần cache phải triển khai giao diện Serializable (lưu ý, tất cả các đối tượng xuất hiện trong truy vấn SQL đều phải kế thừa giao diện), cache sau khi sqlSession đóng (dễ dàng cache tất cả kết quả truy vấn)

Ví dụ:

@Test
public void test2(){
    //Mở phiên SQL
    SqlSession sqlSession1 = ssf.openSession();
    IDeptMapper mapper1 = sqlSession1.getMapper(IDeptMapper.class);
    Dept dept = mapper1.selectById(10);
    System.out.println(dept);
    sqlSession1.close();

    //Mở phiên SQL mới
    SqlSession sqlSession2 = ssf.openSession();
    IDeptMapper mapper2 = sqlSession2.getMapper(IDeptMapper.class);
    Dept dept2 = mapper2.selectById(10);  //Trong cùng một phương thức, nhiều phiên sqlSession chia sẻ dữ liệu, và phải gọi cùng phương thức để cache cấp hai có hiệu lực.
    System.out.println(dept2);
    sqlSession2.close();
}

8.3. Lưu ý

  • Nếu sqlSession thực hiện thao tác DML (insert, update, delete) và commit, sẽ xóa cache cấp một trong sqlSession, đảm bảo cache luôn lưu trữ thông tin mới nhất, tránh đọc dữ liệu lỗi.
  • Cache MyBatis dựa trên [ namespace:sql:tham số ] để cache (HashMap trong sqlSession lưu trữ cache, sử dụng

[ namespace:sql:tham số ] làm key, câu trả lời truy vấn làm value).

Quy trình làm việc:

  • Khi một sqlseesion thực hiện một lần select, sau khi đóng session này, sẽ lưu kết quả truy vấn vào cache cấp hai.
  • Khi một sqlsession khác thực hiện cùng select, đầu tiên sẽ kiểm tra cache cấp hai, nếu không có dữ liệu tương ứng trong cache cấp hai, tiếp tục kiểm tra cache cấp một, nếu không có trong cache cấp một, cuối cùng truy vấn cơ sở dữ liệu, giảm áp lực cơ sở dữ liệu và nâng cao hiệu suất.

Quy trình bật:

  • Cấu hình bật cache cấp hai qua application.yml
# cấu hình MyBatis
mybatis:
  configuration:
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      #bật cache cấp hai của MyBatis
      cache-enabled: true

hoặc cấu hình cache cấp hai qua tệp cấu hình MyBatis

<setting name="cacheEnabled" value="true"/>

  • Thêm vào tệp xxxMapper.xml
//Thêm bên ngoài, bật toàn cục
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"/>
//Thiết lập thuộc tính useCache="true" để bật cho câu lệnh hiện tại

  • Đối tượng cần cache phải triển khai giao diện Serializable (lưu ý, tất cả các đối tượng xuất hiện trong truy vấn SQL đều phải kế thừa giao diện), cache sau khi sqlSession đóng (dễ dàng cache tất cả kết quả truy vấn)

Ví dụ:

@Test
public void test2(){
    //Mở phiên SQL
    SqlSession sqlSession1 = ssf.openSession();
    IDeptMapper mapper1 = sqlSession1.getMapper(IDeptMapper.class);
    Dept dept = mapper1.selectById(10);
    System.out.println(dept);
    sqlSession1.close();

    //Mở phiên SQL mới
    SqlSession sqlSession2 = ssf.openSession();
    IDeptMapper mapper2 = sqlSession2.getMapper(IDeptMapper.class);
    Dept dept2 = mapper2.selectById(10);  //Trong cùng một phương thức, nhiều phiên sqlSession chia sẻ dữ liệu, và phải gọi cùng phương thức để cache cấp hai có hiệu lực.
    System.out.println(dept2);
    sqlSession2.close();
}

Thẻ: mybatis orm sql Java Phân trang

Đăng vào ngày 1 tháng 6 lúc 10:39