MyBatis级联查询(一对多)复习巩固练习

MyBatis级联查询(一对多)复习巩固练习

为了帮助巩固MyBatis级联查询的知识点,设计了一个完整的练习方案,包含理论复习和实践操作。

一、理论知识点复习

1. 级联查询的核心概念

  • 目的:在一个查询中获取关联表的数据
  • 类型:一对一、一对多、多对多
  • 实现方式:使用<association><collection>标签

2. 核心标签解析

  • <association>:用于处理一对一关系

    • property:Java对象中的属性名
    • javaType:关联对象的Java类型
    • 包含<id><result>子标签
  • <collection>:用于处理一对多关系

    • property:集合属性名
    • ofType:集合中元素的Java类型

3. SQL编写要点

  • 必须使用别名(AS)区分不同表的相同列名
  • JOIN查询比笛卡尔积更高效
  • SELECT子句应包含所有需要映射的字段

二、实践练习任务

任务1:基础级联查询(学生→班级)

1. 数据库准备
CREATE DATABASE IF NOT EXISTS test11;
USE test11;

-- 班级表
CREATE TABLE class (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

-- 学生表
CREATE TABLE student (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    cid INT,
    FOREIGN KEY (cid) REFERENCES class(id)
);

-- 插入测试数据
INSERT INTO class (name) VALUES 
    ('计算机科学与技术1班'),
    ('软件工程2班'),
    ('人工智能3班');

INSERT INTO student (name, cid) VALUES
    ('张三', 1),
    ('李四', 1),
    ('王五', 2),
    ('赵六', 3),
    ('钱七', 3);
2. 实体类实现
// Student.java
package com.test.entity;

public class Student {
    private Integer id;
    private String name;
    private Class clazz; // 关联的班级对象

    // 构造函数、getter/setter、toString
}

// Class.java
package com.test.entity;

public class Class {
    private Integer id;
    private String name;
    
    // 构造函数、getter/setter、toString
}
3. Mapper接口
package com.test.repository;

import com.test.entity.Student;

public interface StudentRepository {
    Student getStudentWithClass(Integer id);
}
4. Mapper XML配置
<!-- StudentRepository.xml -->
<resultMap id="studentClassMap" type="com.test.entity.Student">
    <id property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="clazz" javaType="com.test.entity.Class">
        <id property="id" column="cid"/>
        <result property="name" column="cname"/>
    </association>
</resultMap>

<select id="getStudentWithClass" parameterType="int" resultMap="studentClassMap">
    SELECT 
        s.id AS sid, 
        s.name AS sname, 
        c.id AS cid, 
        c.name AS cname
    FROM student s
    JOIN class c ON s.cid = c.id
    WHERE s.id = #{id}
</select>
5. 测试代码
public class StudentTest {
    public static void main(String[] args) {
        try (SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
            StudentRepository mapper = sqlSession.getMapper(StudentRepository.class);
            Student student = mapper.getStudentWithClass(1);
            System.out.println(student);
            System.out.println("班级信息: " + student.getClazz().getName());
        }
    }
}

任务2:反向级联查询(班级→学生)

1. 修改实体类
// Class.java
package com.test.entity;

import java.util.List;

public class Class {
    private Integer id;
    private String name;
    private List<Student> students; // 班级下的学生集合
    
    // 添加getter/setter
}
2. 新增Mapper接口
package com.test.repository;

import com.test.entity.Class;

public interface ClassRepository {
    Class getClassWithStudents(Integer id);
}
3. Mapper XML配置
<!-- ClassRepository.xml -->
<resultMap id="classStudentMap" type="com.test.entity.Class">
    <id property="id" column="cid"/>
    <result property="name" column="cname"/>
    <collection property="students" ofType="com.test.entity.Student">
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
    </collection>
</resultMap>

<select id="getClassWithStudents" parameterType="int" resultMap="classStudentMap">
    SELECT 
        c.id AS cid, 
        c.name AS cname,
        s.id AS sid,
        s.name AS sname
    FROM class c
    LEFT JOIN student s ON c.id = s.cid
    WHERE c.id = #{id}
</select>
4. 测试代码
public class ClassTest {
    public static void main(String[] args) {
        try (SqlSession sqlSession = MyBatisUtil.getSqlSession()) {
            ClassRepository mapper = sqlSession.getMapper(ClassRepository.class);
            Class cls = mapper.getClassWithStudents(1);
            System.out.println("班级: " + cls.getName());
            System.out.println("学生列表:");
            for (Student student : cls.getStudents()) {
                System.out.println(" - " + student.getName());
            }
        }
    }
}

任务3:扩展练习(课程-学生多对多关系)

1. 新增数据库表
-- 课程表
CREATE TABLE course (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

-- 学生选课表(多对多关系)
CREATE TABLE student_course (
    student_id INT,
    course_id INT,
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id) REFERENCES student(id),
    FOREIGN KEY (course_id) REFERENCES course(id)
);

-- 插入课程数据
INSERT INTO course (name) VALUES 
    ('Java程序设计'),
    ('数据库原理'),
    ('Web前端开发'),
    ('算法与数据结构');

-- 插入选课数据
INSERT INTO student_course (student_id, course_id) VALUES
    (1, 1), (1, 2),
    (2, 1), (2, 3),
    (3, 2), (3, 4),
    (4, 3), (4, 4),
    (5, 1), (5, 4);
2. 实体类扩展
// Student.java 添加
private List<Course> courses;

// Course.java
package com.test.entity;

import java.util.List;

public class Course {
    private Integer id;
    private String name;
    private List<Student> students;
    
    // 构造函数、getter/setter、toString
}
3. 练习要求
  1. 实现查询学生时获取所选课程
  2. 实现查询课程时获取选课学生
  3. 使用<collection>处理多对多关系
4. 提示
<!-- 学生包含课程 -->
<collection property="courses" ofType="com.test.entity.Course">
    <id property="id" column="course_id"/>
    <result property="name" column="course_name"/>
</collection>

<!-- SQL示例 -->
SELECT 
    s.id, s.name,
    c.id AS course_id, c.name AS course_name
FROM student s
JOIN student_course sc ON s.id = sc.student_id
JOIN course c ON sc.course_id = c.id
WHERE s.id = #{id}

三、调试技巧与常见问题

1. 常见错误排查

  1. 空指针异常

    • 检查getter/setter是否完整
    • 确保SQL别名与resultMap中的column一致
  2. 映射失败

    • 使用MyBatis日志功能
    • 在config.xml中添加:
      <settings>
          <setting name="logImpl" value="STDOUT_LOGGING"/>
      </settings>
      
  3. SQL错误

    • 先在数据库客户端执行SQL验证正确性
    • 检查表名、字段名大小写

2. 性能优化建议

  1. 延迟加载

    <setting name="lazyLoadingEnabled" value="true"/>
    
  2. 分页查询

    • 使用PageHelper插件
    • 避免一次性加载大量数据
  3. 缓存策略

    <cache/>
    

四、综合练习

场景:学生管理系统

实现以下功能:

  1. 查询学生详情(包含班级信息)
  2. 查询班级详情(包含学生列表)
  3. 查询学生所选课程
  4. 按课程查询选课学生

要求:

  1. 使用MyBatis实现所有数据访问
  2. 合理设计实体类和Mapper
  3. 编写测试代码验证所有功能
  4. 使用日志记录SQL执行情况

扩展挑战:

  1. 添加分页功能
  2. 实现条件查询(如按班级查询学生)
  3. 添加事务管理
  4. 实现缓存优化

通过以上练习,您将能够:

  1. 熟练掌握MyBatis级联查询的配置
  2. 理解一对多、多对多关系的处理方式
  3. 掌握复杂SQL的编写技巧
  4. 提升MyBatis调试和优化能力

建议按照任务顺序逐步完成,遇到问题时回顾相关知识点,并使用调试技巧解决问题。完成基础练习后,尝试挑战综合练习以巩固所学知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值