书接上回,为了实现更复杂的需求,如多层嵌套映射,我们可以通过在 MyBatis 中嵌套多个 resultMap 来实现。例如,假设我们现在有三个表,它们之间存在多层嵌套关系:Category(类别)、Product(产品)和 Review(评论)。一个 Category 有多个 Product,每个 Product 又有多个 Review,这样就形成了多层嵌套的结构。

示例场景

假设有以下三个表:

category 表:

CREATE TABLE category (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255)
);

product 表:

CREATE TABLE product (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255),
    price DECIMAL(10, 2),
    category_id INT,
    FOREIGN KEY (category_id) REFERENCES category(id)
);

review 表:

CREATE TABLE review (
    id INT PRIMARY KEY AUTO_INCREMENT,
    content VARCHAR(255),
    rating INT,
    product_id INT,
    FOREIGN KEY (product_id) REFERENCES product(id)
);

1. 实体类设计

Category 实体类

package com.example.entity;

import lombok.Data;
import java.util.List;

@Data
public class Category {
    private Integer id;
    private String name;
    private List<Product> products; // 一对多关系,产品列表
}

Product 实体类

package com.example.entity;

import lombok.Data;
import java.util.List;

@Data
public class Product {
    private Integer id;
    private String name;
    private Double price;
    private Integer categoryId;
    private List<Review> reviews; // 一对多关系,评论列表
}

Review 实体类

package com.example.entity;

import lombok.Data;

@Data
public class Review {
    private Integer id;
    private String content;
    private Integer rating;
    private Integer productId;
}

2. Mapper 接口设计

CategoryMapper.java

package com.example.mapper;

import com.example.entity.Category;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface CategoryMapper {

    // 查询所有类别,包括其产品和产品的评论信息(多层嵌套)
    @Select("SELECT * FROM category")
    List<Category> findAllCategoriesWithProductsAndReviews();
}

3. MyBatis 配置文件

我们需要在 CategoryMapper.xml 中定义嵌套的 resultMap 来处理多层嵌套的关系。

CategoryMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.CategoryMapper">

    <!-- 定义嵌套映射:Category 包含 Product,Product 包含 Review -->
    <resultMap id="CategoryProductReviewMap" type="com.example.entity.Category">
        <!-- Category 映射 -->
        <id column="category_id" property="id"/>
        <result column="category_name" property="name"/>

        <!-- 嵌套映射 Product -->
        <collection property="products" ofType="com.example.entity.Product">
            <id column="product_id" property="id"/>
            <result column="product_name" property="name"/>
            <result column="price" property="price"/>
            <result column="category_id" property="categoryId"/>

            <!-- 嵌套映射 Review -->
            <collection property="reviews" ofType="com.example.entity.Review">
                <id column="review_id" property="id"/>
                <result column="content" property="content"/>
                <result column="rating" property="rating"/>
                <result column="product_id" property="productId"/>
            </collection>
        </collection>
    </resultMap>

    <!-- 查询所有类别及其产品及评论 -->
    <select id="findAllCategoriesWithProductsAndReviews" resultMap="CategoryProductReviewMap">
        SELECT 
            c.id AS category_id, 
            c.name AS category_name, 
            p.id AS product_id, 
            p.name AS product_name, 
            p.price AS product_price, 
            p.category_id AS product_category_id,
            r.id AS review_id, 
            r.content AS review_content, 
            r.rating AS review_rating, 
            r.product_id AS review_product_id
        FROM 
            category c
        LEFT JOIN 
            product p 
        ON 
            c.id = p.category_id
        LEFT JOIN 
            review r 
        ON 
            p.id = r.product_id;
    </select>
</mapper>

4. Service 层与 Controller 实现

CategoryService.java

package com.example.service;

import com.example.entity.Category;
import com.example.mapper.CategoryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    public List<Category> findAllCategoriesWithProductsAndReviews() {
        return categoryMapper.findAllCategoriesWithProductsAndReviews();
    }
}

CategoryController.java

package com.example.controller;

import com.example.entity.Category;
import com.example.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class CategoryController {

    @Autowired
    private CategoryService categoryService;

    @GetMapping("/categories")
    public List<Category> getCategoriesWithProductsAndReviews() {
        return categoryService.findAllCategoriesWithProductsAndReviews();
    }
}

5. 测试

启动应用后,访问 http://localhost:8080/categories,你将会得到包含 CategoryProductReview 的完整多层嵌套的 JSON 结果。例如,返回的数据格式如下:

[
  {
    "id": 1,
    "name": "Electronics",
    "products": [
      {
        "id": 1,
        "name": "Laptop",
        "price": 1000.00,
        "categoryId": 1,
        "reviews": [
          {
            "id": 1,
            "content": "Great laptop!",
            "rating": 5,
            "productId": 1
          },
          {
            "id": 2,
            "content": "Not bad.",
            "rating": 4,
            "productId": 1
          }
        ]
      }
    ]
  }
]

总结

通过这个复杂的多层嵌套映射示例,可以看到如何在 MyBatis 中实现一个 Category -> Product -> Review 的多对多层级关系。我们使用了 resultMapcollection 来处理每个层级的映射,确保每个实体类的关系能够被准确解析。通过这种方式,MyBatis 可以灵活地处理多层嵌套的查询和数据映射。