第3章 MyBatis进阶 视频讲解 学习目标  掌握MyBatis的映射文件的使用方法和规范,能够编写映射文件。  掌握MyBatis一对一关系的映射方法,能够完成一对一应用程序。  掌握MyBatis一对多关系的映射方法,能够完成一对多应用程序。  掌握MyBatis多对多关系的映射方法,能够完成多对多应用程序。 为了灵活运用MyBatis框架,开发人员需要掌握MyBatis映射文件的结构及使用方法。通过深入理解映射文件的组成和重要参数,开发人员能够根据项目需求对映射文件进行灵活配置,以达到最佳的性能和扩展性。本章将对MyBatis的映射文件和关联映射的内容进行讲解,同时通过一个实战演练——智慧农业果蔬系统普通用户的数据管理项目巩固Mapper和XML映射文件的相关知识。 3.1映 射 文 件 在2.4.8节中提到,MyBatis配置文件中的元素用于引入映射文件。这些映射文件是MyBatis框架的核心,定义了SQL语句和数据库中的数据如何映射到Java对象,是MyBatis的“翻译器”,将数据库信息翻译成Java对象,使开发者能够更轻松地与数据库交互,而不必过分关注数据库连接和底层细节,从而大幅度降低编码的工作量。本节将对MyBatis的映射文件结构及元素用法进行讲解。 3.1.1映射文件结构 通过映射文件,我们能够将数据从应用程序的POJO对象映射到持久化存储中,实现数据的创建、读取、更新和删除操作。映射文件以XML文件的形式存在,一般采用“POJO类的名称+Mapper”的规则进行命名,例如EducationMapper.xml。MyBatis规定了其映射文件的层次结构,具体如下所示。 上述代码中列出了MyBatis映射文件的常见元素,例如等元素来实现。此外,元素还用于定义如何将数据库记录映射到Java对象的规则,通常通过元素来实现。后续将对上述元素进行详细讲解。 元素还可以通过resource、url、class等属性引入其他映射文件,这有助于将映射配置分解成多个文件,使得映射文件更易于维护和组织。具体使用规则可参考2.4.8节。 3.1.3元素是MyBatis中常用的元素之一,主要用于定义数据库查询操作,它包含了SQL语句、参数映射、结果映射以及其他与数据库操作相关的细节。 为了更加灵活地映射查询语句,元素中的属性,每个属性都有其独特的作用,开发人员根据查询的要求选择适当的属性来定制查询操作。 接下来,通过 3select * from education 4 5 上述配置定义了一个名为findAllEducation的MyBatis查询操作,它的功能是从名为education的数据库表中检索所有记录,并将结果映射为com.qfedu.pojo.Education类型的Java对象列表。元素定义的数据库查询操作进行测试,结果如图31所示。 图31测试元素成功执行查询表education中所有记录的SQL语句。 3.1.4元素、元素、元素 元素用于映射数据库插入操作的SQL语句,元素用于映射数据库更新操作的SQL语句,而元素则用于映射数据库删除操作的SQL语句。与 select from education insert into education() values(#{id}, #{name},#{price}) 上述代码中,元素用于包含元素定义的SQL代码片段,其refid属性匹配元素的id属性。 3.2关 联 映 射 MyBatis的关联映射可以帮助开发人员在数据库中进行复杂的查询操作,避免手动拼接SQL语句。它可以将多个表之间的关系映射到Java对象之间的关系,从而简化了复杂的多表查询工作。关联映射支持三种主要类型的映射: 一对一、一对多、多对多。本节将对MyBatis关联映射的功能和语法进行讲解。 3.2.1一对一关联映射 1. 设计背景 一对一关联映射是指一个表中的一条记录对应着另一个表中的一条记录。在数据库模型中,这种关系通常表示两个实体之间的一对一关系。例如,一个人只有一个身份证号,一个身份证号也只对应一个人,这就是一对一关联。 在一个OA管理系统中,涉及两个实体: 员工和工号。每个员工都会对应一个唯一的工号。在前面讲解的元素中包含了子元素,MyBatis通过该元素来处理一对一关联关系。 2. 创建实体类和数据库 在chapter03项目的com.qfedu.pojo包下新建Employee类和Card类,分别表示员工类和工号类,具体代码如例34与例35所示。 例34Employee.java。 1public class Employee { 2private int id; //主键 3private String name; //姓名 4private int cid; //工号表外键 5private int did; //部门表外键 6private Card card; //工号表实体类 7//此处省略构造方法、Getter()、Setter()和toString()方法 8} 例35Card.java。 1public class Card { 2private int id; 3private int number; 4private String introduce; 5//此处省略构造方法、Getter()、Setter()和toString()方法 6} (1) 在数据库student中创建员工表employee,SQL语句如下所示。 DROP TABLE IF EXISTS 'employee'; CREATE TABLE 'employee' ( 'id' int(0) NOT NULL AUTO_INCREMENT, 'name' varchar(255) NULL DEFAULT NULL, 'cid' int(0) NULL DEFAULT NULL, PRIMARY KEY ('id') USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; (2) 向employee表中插入数据,SQL语句如下所示。 INSERT INTO 'employee' VALUES (1, '余*兴', 1); INSERT INTO 'employee' VALUES (2, '谭*端', 2); INSERT INTO 'employee' VALUES (3, '宋*桥', 3); (3) 在数据库student中创建工号表card,SQL语句如下所示。 DROP TABLE IF EXISTS 'card'; CREATE TABLE 'card' ( 'id' int(0) NOT NULL AUTO_INCREMENT, 'number' int(0) NULL DEFAULT NULL, 'introduce' varchar(255) NULL DEFAULT NULL, PRIMARY KEY ('id') USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; (4) 向card表中插入数据,SQL语句如下所示。 INSERT INTO 'card' VALUES (1, 77000, '组长'); INSERT INTO 'card' VALUES (2, 66789, '组员'); INSERT INTO 'card' VALUES (3, 22960, '组员'); 将card表的id和employee表的cid进行关联,即每个员工记录在employee表中有一个cid字段,用于指示员工持有的工号。 3. 编写MyBatis的配置文件和映射文件 (1) 在chapter03项目下创建resource文件夹,将其标注为资源文件夹。在resource文件夹下新建MyBatis的配置文件,具体代码如例36所示。 例36mybatisconfig.xml。 1 2 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 在例36中,第6行代码表示引用外部配置文件db.properties,其具体代码如例37所示。 例37db.properties。 1jdbc.Driver = com.mysql.cj.jdbc.Driver 2jdbc.Url = jdbc:mysql://localhost:3306/textbook 3jdbc.Username = root 4 jdbc.Password = root 需要注意的是,db.properties文件必须存放于resource目录下,否则,mybatisconfig.xml文件无法读取db.properties中的内容。 (2) 在chapter03项目的com.qfedu.mapper包中新建EmployeeMapper接口,并在该接口中声明查询所有员工的方法,具体代码如例38所示。 例38EmployeeMapper.java。 1public interface EmployeeMapper { 2List findAllEmployee(); 3} (3) 在chapter03项目的com.qfedu.mapper包中新建名为EmployeeMapper的XML文件,作为EmployeeMapper接口的映射文件,具体代码如例39所示。 例39EmployeeMapper.xml。 1 2 4 5 6 7 8 9 10 11 12 13 14 15 18 在例39中,定义了一个名为employeeMap的映射规则,用元素将employee表的数据映射到employee对象和关联的card对象中。同时,本例中还定义了一个查询操作findAllEmployee,用于查询员工信息,并在查询结果中包含了与员工关联的工号信息。 4. 编写测试类 (1) 在chapter03项目的com.qfedu.test包中创建TestFindAllEmployee类,用于验证一对一关联映射,具体代码如例310所示。 例310TestFindAllEmployee.java。 1public class TestFindAllEmployee { 2public static void main(String[] args) { 3/*创建输入流*/ 4InputStream inputStream = null; 5/*将MyBatis配置文件转化为输入流*/ 6try { 7inputStream = 8Resources.getResourceAsStream("mybatis-config.xml"); 9} catch (IOException e) { 10e.printStackTrace(); 11} 12/*通过SqlSessionFactoryBuilder()创建SqlSessionFactory对象*/ 13SqlSessionFactory build = 14new SqlSessionFactoryBuilder().build(inputStream); 15/*通过SqlSessionFactory对象创建SqlSession对象*/ 16SqlSession sqlSession = build.openSession(); 17EmployeeMapper mapper = 18sqlSession.getMapper(EmployeeMapper.class); 19List allEmployee = mapper.findAllEmployee(); 20for (Employee employee : allEmployee) { 21System.out.println(employee.toString()); 22} 23/*关闭事务*/ 24sqlSession.close(); 25} 26} (2) 执行TestFindAllEmployee类的main()方法。测试一对一关联映射的结果如图32所示。 图32测试一对一关联映射的结果 从图32中可以看出,employee对象中的card属性一对一映射成功。did字段此处不做处理,3.2.3节中将使用该字段对一对多关联映射进行详细讲解。 3.2.2一对多关联映射 1. 设计背景 一对多关联映射是指一个表中的一条记录对应着另一个表中的多条记录。在本节中,员工表包含了一个部门外键did,其中每位员工归属于一个部门,但一个部门可以包含多位员工。为了实现一对多关联映射,可以借助元素来完成这样的映射。 2. 创建实体类和数据库 (1) 员工类Employee已存在,在chapter03项目的com.qfedu.pojo包中新建部门实体类Department,具体代码如例311所示。 例311Department.java。 1public class Department { 2private int id; //部门表id 3private String name; //部门名称 4private List employees; //员工集合 5//此处省略构造方法、Getter()、Setter()和toString方法 6} (2) 在数据库student中创建部门表department,SQL语句如下所示。 DROP TABLE IF EXISTS 'department'; CREATE TABLE 'department' ( 'id' int(0) NOT NULL AUTO_INCREMENT, 'name' varchar(255) NULL DEFAULT NULL, PRIMARY KEY('id') USING BTREE )ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; (3) 向部门表插入数据,SQL语句如下所示。 INSERT INTO 'department' VALUES (1, '宣传部'); INSERT INTO 'department' VALUES (2, '行政部'); 3. 编写映射文件 (1) 在chapter03项目的com.qfedu.mapper包中新建DepartmentMapper接口,具体代码如例312所示。 例312DepartmentMapper.java。 1public interface DepartmentMapper{ 2List findAllDepartment(); 3} (2) 在chapter03项目的com.qfedu.mapper包中新建名为DepartmentMapper的XML文件,作为DepartmentMapper接口的映射文件,具体代码如例313所示。 例313DepartmentMapper.xml。 1 2 4 5 6 7 8 9 10 11 12 13 17 在例313中,定义了一个名为departmentMap的映射规则,用元素将department表的数据映射到department对象和关联的employees对象中。同时,本示例中还定义了一个查询操作employees,用于查询部门信息,并在查询结果中包含了与部门关联的员工信息。 4. 编写测试类 (1) 在chapter03项目的com.qfedu.test包中创建TestFindAllDepartment类,用于验证一对多关联映射,具体代码如例314所示。 例314TestFindAllDepartment.java。 1public class TestFindAllDepartment { 2public static void main(String[] args) { 3/*创建输入流*/ 4InputStream inputStream = null; 5/*将MyBatis配置文件转化为输入流*/ 6try { 7inputStream = 8Resources.getResourceAsStream("mybatis-config.xml"); 9} catch (IOException e) { 10e.printStackTrace(); 11} 12/*通过SqlSessionFactoryBuilder()创建SqlSessionFactory对象*/ 13SqlSessionFactory build = 14new SqlSessionFactoryBuilder().build(inputStream); 15/*通过SqlSessionFactory对象创建SqlSession对象*/ 16SqlSession sqlSession = build.openSession(); 17DepartmentMapper mapper = 18sqlSession.getMapper(DepartmentMapper.class); 19List departments = mapper.findAllDepartment(); 20for (Department department : departments) { 21System.out.println(department.toString()); 22} 23/*关闭事务*/ 24sqlSession.close(); 25} 26} (2) 执行例314中的main()方法。测试一对多关联映射的结果如图33所示。 图33测试一对多关联映射的结果 从图33可以看出,Department类中的集合对象employees一对多映射成功,输出该部门员工的详细信息。 3.2.3多对多关联映射 1. 设计背景 多对多关联映射是指两个实体之间存在多对多的关系,通常需要中间表来建立它们之间的连接。 在一个OA管理系统中,涉及两个实体: 员工和技能培训课程。由于培训需求中,每个员工可以选择参加多门技能培训课程,同时每门技能培训课程也可以有多名员工参加。每个员工有对应的工号。为了有效地处理这种多对多的关系,可以通过MyBatis的元素实现这种关联映射。 2. 创建实体类和数据库 (1) 在chapter03项目的com.qfedu.pojo包中新建技能培训课程的实体类Course,具体代码如例315所示。 例315Course.java。 1public class Course { 2Integer id; 3String courseName; 4List employees; 5此处省略Getter()、Setter()、toString()和构造方法 6} (2) 员工类中需要课程类的实体类集合属性,在Employee类中添加集合对象courses,具体代码如例316所示。 例316Employee.java。 1public class Employee { 2private int id; 3private String name; 4private List courses; 5} (3) 创建技能培训课程表course,SQL语句如下所示。 DROP TABLE IF EXISTS 'course'; CREATE TABLE 'course' ( 'id' int(0) NOT NULL AUTO_INCREMENT, 'course_name' varchar(255) CHARACTER NOT NULL, PRIMARY KEY('id') USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; (4) 向技能培训课程表中插入数据,SQL语句如下所示。 INSERT INTO 'course' VALUES (1, '礼仪培训'); INSERT INTO 'course' VALUES (2, '话术培训'); (5) 多对多关联映射需要中间表做关联,创建中间表employee_course的SQL语句如下所示。 DROP TABLE IF EXISTS 'employee_course'; CREATE TABLE 'employee_course' ( 'id' int(0) NOT NULL AUTO_INCREMENT, 'e_id' int(0) NOT NULL, 'c_id' int(0) NOT NULL, PRIMARY KEY('id') USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 中间表employee_course包括3个字段: 主键id、表示员工id的e_id和表示课程id的c_id。 (6) 向中间表中插入数据,SQL语句如下所示。 INSERT INTO 'employee_course' VALUES (1, 1, 1); INSERT INTO 'employee_course' VALUES (2, 2, 1); INSERT INTO 'employee_course' VALUES (3, 3, 2); 3. 创建接口文件和映射文件 (1) 在chapter03项目的EmployeeMapper接口中新增查询员工的技能培训课程的方法,具体代码如例317所示。 例317EmployeeMapper.java。 1public interface EmployeeMapper { 2List findEmployeeCourse(); 3} (2) 在chapter03项目的EmployeeMapper.xml文件中创建多对多映射关系,具体代码如例318所示。 例318EmployeeMapper.xml。 1 2 4 5 6 7 8 9 10 11 12 13 23 在例318中,定义了一个名为courseMap的映射规则,用元素将员工与其参与的课程相关联。同时,本例中还定义了一个查询操作findEmployeeCourse,从数据库中选择员工信息以及他们参与的课程,并将结果映射到Java对象中,以便获取员工与课程的多对多关联信息。 4. 编写测试类 (1) 在chapter03项目的com.qfedu.test包中新建TestFindAEmployeeCourse类,用于验证多对多关联映射,具体代码如例319所示。 例319TestFindAEmployeeCourse.java。 1public class TestFindAEmployeeCourse { 2public static void main(String[] args) { 3/*创建输入流*/ 4InputStream inputStream = null; 5/*将MyBatis配置文件转化为输入流*/ 6try { 7inputStream = 8Resources.getResourceAsStream("mybatis-config.xml"); 9} catch (IOException e) { 10e.printStackTrace(); 11} 12/*通过SqlSessionFactoryBuilder()创建SqlSessionFactory对象*/ 13SqlSessionFactory build = 14new SqlSessionFactoryBuilder().build(inputStream); 15/*通过SqlSessionFactory对象创建SqlSession对象*/ 16SqlSession sqlSession = build.openSession(); 17EmployeeMapper mapper = sqlSession.getMapper( 18EmployeeMapper.class); 19List employeeCourse = mapper.findEmployeeCourse(); 20for (Employee employee : employeeCourse) { 21System.out.println(employee); 22} 23/*关闭事务*/ 24sqlSession.close(); 25} 26} (2) 执行例319中的main()方法,测试多对多关联映射的结果如图34所示。 图34测试多对多关联映射的结果 从图34可以看出,Employee类中的集合对象course多对多映射成功。 MyBatis的关联映射可以提高查询效率、避免冗余数据、简化代码、提高可读性、支持高级查询等,对于开发大型数据库应用程序具有重要意义。 3.3实战演练: 智慧农业果蔬系统中普通 用户的数据管理 为了加深对MyBatis核心组件相关编程知识的理解,本节以智慧农业果蔬系统为例,开发该系统中普通用户的管理模块。通过对该模块的实现,读者可以掌握Mapper和XML映射文件的相关知识。本实战演练的实战描述、实战分析和实现步骤如下所示。 【实战描述】 使用IDEA软件搭建一个Web项目chapter03,通过MyBatis框架的SqlSession语法完成对MySQL数据库chapter03下普通用户表user的增、删、改、查操作,并在控制台输出日志信息。 【实战分析】 (1) 创建一个名为chapter03的数据库,并在该数据库下创建数据表user。 (2) 向表中插入测试数据。 (3) 在IDEA软件中创建一个名为chapter03的项目,并引入MyBatis的相关JAR包。 (4) 在chapter03项目下创建对应的POJO类、接口、映射文件、配置文件和测试类。 (5) 编写和执行测试类,验证数据库中的数据表信息是否同步,并查看控制台的打印日志是否正确。 【实现步骤】 1. 搭建开发环境 (1) 在MySQL中创建数据库chapter03和数据表user,SQL语句如例320所示。 例320user.sql。 1DROP TABLE IF EXISTS 'user'; 2CREATE TABLE 'user' ( 3'id' int(0) NOT NULL AUTO_INCREMENT COMMENT '注解ID', 4'userName' varchar(255) CHARACTER SET DEFAULT NULL COMMENT '用户名', 5'passWord' varchar(255) CHARACTER SET DEFAULT NULL COMMENT '密码', 6'phone' varchar(255) CHARACTER SET DEFAULT NULL COMMENT '手机号', 7'realName' varchar(255) CHARACTER DEFAULT NULL COMMENT '真实姓名', 8'sex' varchar(255) CHARACTER SET DEFAULT NULL COMMENT '性别', 9'address' varchar(255) CHARACTER SET DEFAULT NULL COMMENT '地址', 10'email' varchar(255) CHARACTER SET DEFAULT NULL COMMENT '邮箱', 11PRIMARY KEY('id') USING BTREE 12) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = 13utf8_general_ci ROW_FORMAT = Dynamic; (2) 向数据表user中插入数据,SQL语句如下所示。 INSERT INTO 'user' VALUES (1, '曾*梁', '2', '138****6907', '曾*梁', '男', '北京市昌平区', '138****6907@163.com'); INSERT INTO 'user' VALUES (2, 'wu', 'dd', '156****1543', '吴*英', '男', '北京市海淀区', '156****1543@163.com'); INSERT INTO 'user' VALUES (3, '吴*德', '111111', '192****9012', '吴*德', '女', '北京市丰台区', '192****9012@163.com'); INSERT INTO 'user' VALUES (6, 'wang', '123456', '155****2130', '王*强', '女', '北京市房山区', '155****2130@163.com'); INSERT INTO 'user' VALUES (7, 'fang', '123456', '170****1239', '方*智', '女', '北京市通州区', '170****1239@163.com'); INSERT INTO 'user' VALUES (8, 'jian', '11', '166****8613', '*坚', '男', '北京市密云区', '166****8613@163.com'); (3) 在Windows的命令提示符窗口中输入查询user表数据的SQL语句,具体语句如下所示。 select * from user; (4) 执行查询user表数据的SQL语句,查询结果如图35所示。 图35user表查询结果 从图35中可以看出,user表数据添加成功。 2. 创建项目 (1) 在IDEA中新建Web项目chapter03,将MyBatis的JAR包mybatis3.5.6.jar复制到WEBINF下的lib文件夹中,完成JAR包的导入。 (2) 在chapter03项目的src目录下创建com.qfedu.pojo包,并在该包下新建User类,具体代码如例321所示。 例321User.java。 1public class User { 2private int id; 3private String userName; 4private String passWord; 5private String phone; 6private String realName; 7private String sex; 8private String address; 9private String email; 10//此处省略Getter/Setter、toString()和构造方法 11} 需要注意的是,在上述代码中,User类必须提供Setter方法,这样MyBatis框架才能通过配置文件映射User类和数据表user的关系。 (3) 在resource目录下新建MyBatis的配置文件mybatisconfig.xml,具体代码如例322所示。 例322mybatisconfig.xml。 1 2 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 在例322中,第23~32行代码用于配置数据库的连接信息,其中元素的4个属性分别配置数据库的驱动、URL、用户名和密码; 第35~37行代码通知MyBatis在包com.qfedu.mapper下寻找并加载映射文件。 (4) 在src目录下创建com.qfedu.mapper包,在该包下新建UserMapper接口,用于声明查询、新增、修改和删除的方法,具体代码如例323所示。 例323UserMapper.java。 1public interface UserMapper { 2//查询 3List findAllUser(); 4//新增 5Integer insertUser(User User); 6//修改 7Integer updateUser(User User); 8//删除 9Integer deleteUser(User User); 10} (5) 在com.qfedu.mapper包下新建UserMapper接口对应的映射文件UserMapper.xml,具体代码如例324所示。 例324UserMapper.xml。 1 2 4 5 8 9insert into user(userName,passWord,phone,relName,sex,address,email) 10values(#{userName},#{passWord},#{phone},#{relName},#{sex}, 11#{address},#{email}) 12 13 14update User set passWord= #{passWord} where id =#{id} 15 16 17delete from User where id= #{id} 18 19 在例324中,第5行代码B. C. D. 2. 在MyBatis的关联关系映射中,以下哪个标签用于定义一对一关联关系?() A. B. C. D. 3. 在MyBatis的关联关系映射中,以下哪个标签用于定义一对多关联关系?() A. B. C. D. 4. 关于MyBatis的映射文件,下列描述错误的是()。 A. 一个MyBatis配置文件中可引入多个映射文件 B. 在编写MyBatis的映射文件时,开发人员无须关心元素顺序 C. 元素是MyBatis映射文件的根元素 D. 在编写MyBatis映射文件时,不是所有的元素都必须配置 5. 在下列选项中,不属于