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&useServerPrepStmts=true&useUnicode=true&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:
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">
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();
}