1. Giới thiệu
Bài viết này sẽ giải thích cách ánh xạ các mối quan hệ cơ bản trong MyBatis: một-đối-một (1-1), một-đối-nhiều (1-N) và nhiều-đối-nhiều (N-N). Chúng ta sẽ sử dụng các bảng người dùng (User), địa chỉ (Address) và xe hơi (Car) để minh họa.
2. Mối quan hệ 1-1 và 1-N
Mối quan hệ 1-1 và 1-N khá đơn giản và có thể được trình bày cùng nhau. Một ví dụ điển hình là mỗi người dùng có một địa chỉ duy nhất (1-1), và mỗi người dùng có thể sở hữu nhiều chiếc xe (1-N).
2.1. Thiết lập dự án Spring Boot
Dưới đây là cấu hình cơ bản cho dự án Spring Boot với MyBatis:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
Và cấu hình `application.yml`:
server:
port: 8080
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapping/*.xml
type-aliases-package: com.example.entity
logging:
level:
com.example.mapper: debug
2.2. Cấu trúc dự án
Các lớp chính bao gồm: controller, service, repository, entity và các file XML mapping. Dưới đây là các lớp thực thể:
// Người dùng
public class User {
private Long id;
private String name;
private Location location; // Quan hệ 1-1
private List<Vehicle> vehicles; // Quan hệ 1-N
}
// Địa chỉ
public class Location implements Serializable {
private Long id;
private String province;
private String city;
}
// Xe cộ
public class Vehicle implements Serializable {
private Long id;
private String color;
private String model;
private User owner; // Liên kết ngược lại
}
2.3. Ánh xạ XML cho MyBatis
Trong file `UserMapper.xml`, chúng ta định nghĩa cách ánh xạ dữ liệu từ SQL sang các đối tượng Java:
<resultMap id="userResult" type="com.example.entity.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<association property="location" javaType="com.example.entity.Location">
<id property="id" column="loc_id"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
</association>
<collection property="vehicles" ofType="com.example.entity.Vehicle">
<id property="id" column="veh_id"/>
<result property="color" column="color"/>
<result property="model" column="model"/>
</collection>
</resultMap>
<select id="findUserById" resultMap="userResult">
SELECT u.id AS user_id, u.name AS user_name,
l.id AS loc_id, l.province, l.city,
v.id AS veh_id, v.color, v.model
FROM users u
LEFT JOIN locations l ON u.location_id = l.id
LEFT JOIN vehicles v ON u.id = v.user_id
WHERE u.id = #{id}
</select>
3. Mối quan hệ N-N
Để minh họa quan hệ N-N, chúng ta sử dụng hai bảng bài viết (`Post`) và danh mục (`Category`). Mỗi bài viết có thể thuộc nhiều danh mục khác nhau, và mỗi danh mục có thể chứa nhiều bài viết.
3.1. Cấu trúc cơ sở dữ liệu
Tạo ba bảng: `posts`, `categories` và bảng trung gian `post_categories`:
CREATE TABLE posts (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255),
content TEXT
);
CREATE TABLE categories (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255)
);
CREATE TABLE post_categories (
post_id INT,
category_id INT,
FOREIGN KEY (post_id) REFERENCES posts(id),
FOREIGN KEY (category_id) REFERENCES categories(id)
);
3.2. Các lớp thực thể
// Bài viết
public class Post implements Serializable {
private Integer id;
private String title;
private String content;
private List<Category> categories;
}
// Danh mục
public class Category implements Serializable {
private Integer id;
private String name;
private List<Post> posts;
}
3.3. Ánh xạ XML
Trong file `PostMapper.xml`, chúng ta sử dụng liên kết để lấy danh sách danh mục của bài viết:
<resultMap id="postResult" type="com.example.entity.Post">
<id property="id" column="post_id"/>
<result property="title" column="title"/>
<result property="content" column="content"/>
<collection property="categories" ofType="com.example.entity.Category">
<id property="id" column="cat_id"/>
<result property="name" column="cat_name"/>
</collection>
</resultMap>
<select id="getPostById" resultMap="postResult">
SELECT p.id AS post_id, p.title, p.content,
c.id AS cat_id, c.name AS cat_name
FROM posts p
LEFT JOIN post_categories pc ON p.id = pc.post_id
LEFT JOIN categories c ON pc.category_id = c.id
WHERE p.id = #{id}
</select>