9.4.5 ResultMaps resultMap
元素是MyBatis
中最重要最强大的元素。它的作用是告诉MyBatis
将从结果集中取出的数据转换成开发者所需要的对象。
每条记录转换成一个Map 下面是最简单的映射语句示例:
1 2 3 <select id="selectUser" resultType="map" > select * from tb_user </select>
selectUser
的select
元素执行一条查询语句,查询tb_user
表的所有数据。
resultType="map"
表示返回的每条数据是都是一个Map
集合(使用这条记录的列名作为key
,列值作为value
)。
示例: 测试ResultMaps 简单映射 一条记录封装成map SelectMapTest.java 1 2 3 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 public class SelectMapTest { public static void main (String[] args) { SqlSession sqlSession = null ; try { sqlSession = FKSqlSessionFactory.getSqlSession(); List<Map<String, Object>> list = sqlSession .selectList("org.fkit.mapper.UserMapper.selectUser" ); list.forEach(row -> System.out.println(row)); sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); e.printStackTrace(); } finally { if (sqlSession != null ) sqlSession.close(); } } }
对应的select标签 1 2 3 4 5 6 7 <select id ="selectUser" resultType ="map" > SELECT * FROM TB_USER</select >
数据库表 此时tb_user
表中的数据如下:
1 2 3 4 5 6 7 8 9 10 mysql> select * from tb_user; +----+------+-----+-----+ | id | name | sex | age | +----+------+-----+-----+ | 2 | 小丽 | 男 | 22 | | 3 | 小李 | 男 | 22 | | 4 | 小王 | 女 | 20 | | 5 | 小明 | 女 | 20 | +----+------+-----+-----+ 4 rows in set
运行结果 运行SelectMapTest
类的main
方法,控制台显示如下:
1 2 3 4 5 6 7 DEBUG [main] ==> Preparing: SELECT * FROM TB_USER DEBUG [main] ==> Parameters: DEBUG [main] <== Total: 4 {sex=男, name=小丽, id=2 , age=22 } {sex=男, name=小李, id=3 , age=22 } {sex=女, name=小王, id=4 , age=20 } {sex=女, name=小明, id=5 , age=20 }
可以看到,査询语句返回的每一条记录都被封装成一个Map
集合,这条记录的列名作为Map
集合的key
,而列的值作为Map
的value
.
简单映射 每条记录封装成一个持久化对象 虽然数据被封装成Map
集合返回,但是Map
集合并不能很好地描述一个领域模型。在实际项目开发中更加建议使用JavaBean
或POJO(Plain Old Java Object
,普通Java
对象)作为领域模型描述数据。例如:
1 2 3 <select id ="selectUser" resultType ="org.fkit.domain.User" > select * from tb_user </select >
ResultMap标签 数据表列名和持久化对象属性名不一致的情况 默认情况下,MyBatis
会将査询到的记录的列和需要返回的对象(User
)的属性逐一进行匹配赋值,但是如果查询到的记录的列和需要返回的对象(User
)的属性不一致则MyBatis
就不会自动赋值了,这时,可以使用ResultMap
标签进行处理.
创建数据库表 进入mybatis
数据库,创建一个表tb_user2
,并插入几条测试数据: 在cmd
命令行中输入下面的sql
语句进行创建,注意是在cmd
命令行,如果使用Navicat
的话先保存成sql
文件,然后通过导入SQL
文件的方式创建,不要使用Navicat
的命令行界面,因为直接复制粘贴下面代码插入数据的时候,会出现乱码.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 set names "gbk";use mybatis; DROP TABLE IF EXISTS `tb_user2`; CREATE TABLE `tb_user2` ( `user_id` int(11 ) NOT NULL AUTO_INCREMENT, `user_name` varchar(18 ) NOT NULL, `user_sex` varchar(18 ) NOT NULL, `user_age` int(11 ) NOT NULL, PRIMARY KEY (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO tb_user2 VALUES (null, '小明', '男', 24 ); INSERT INTO tb_user2 VALUES (null, '小王', '男', 25 ); INSERT INTO tb_user2 VALUES (null, '小丽', '女', 22 ); INSERT INTO tb_user2 VALUES (null, '小花', '女', 25 );
创建持久化对象 接下来创建一个User
对象映射tb_user2
表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class User implements Serializable { private static final long serialVersionUID = 1L ; private Integer id; private String name; private String sex; private Integer age; public User () { super (); } public User (String name, String sex, Integer age) { super (); this .name = name; this .sex = sex; this .age = age; } @Override public String toString () { return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]" ; } }
创建resultMap标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <resultMap id ="userResultMap" type ="org.fkit.domain.User" > <id property ="id" column ="user_id" /> <result property ="name" column ="user_name" /> <result property ="sex" column ="user_sex" /> <result property ="age" column ="user_age" /> </resultMap > <select id ="selectUser2" resultMap ="userResultMap" > SELECT * FROM TB_USER2</select >
resultMap标签详解 上面使用了一个新的元素resultMap
,该元素常用属性如下
resultMap
元素的id
属性,是resultMap
的唯一标识符。
resultMap
元素的type
属性,表示resultMap
实际返回的类型。
上面使用了resultMap
的两个子元素id
和result
。
resultMap标签的id子标签
id
,表示数据库表的主键列,其中,
column
属性表示数据库表的列名,
property
表示数据库列映射到返回类型的属性。
resultMap标签的result子标签
result
,表示数据库表的普通列,其中,
column
属性表示数据库表的列名 ,
property
表示这个数据库列名要映射到的持久化对象的属性名。
ResultMapTest.java 1 2 3 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 public class ResultMapTest { public static void main (String[] args) { SqlSession sqlSession = null ; try { sqlSession = FKSqlSessionFactory.getSqlSession(); List<User> user_list = sqlSession.selectList("org.fkit.mapper.UserMapper.selectUser2" ); user_list.forEach(user -> System.out.println(user)); sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); e.printStackTrace(); } finally { if (sqlSession != null ) sqlSession.close(); } } }
运行ResultMapTest
类的main
方法,控制台显示如下:
1 2 3 4 5 6 7 DEBUG [main] ==> Preparing: SELECT * FROM TB_USER2 DEBUG [main] ==> Parameters: DEBUG [main] <== Total: 4 User [id=1 , name=小明, sex=男, age=24 ] User [id=2 , name=小王, sex=男, age=25 ] User [id=3 , name=小丽, sex=女, age=22 ] User [id=4 , name=小花, sex=女, age=25 ]
可以看到,TB_USER
的列名虽然和User
对象的属性名不一致,数据依然被正确封装到User
对象当中。
关联映射 在实际项目开发中,还有更加复杂的情况,例如执行的是一个多表查询语句,而返回的对象关联到另一个对象,此时简单地映射已经无法解决问题,必须使用resultMap
元素来完成关联映射 进入mybatis
数据库,创建两个表tb_cazz
和tb_student
,并分别插入几条测试数据: 此处代码后面补上… 以上SQL
语句插入了两个班级记录和4个学生记录,两个学生分配在1班,两个学生分配在2班。需要指出的是,tb_student
表中的clazz_id列作为外键引用tb_clazz
表的id
列,表示学生对应的班级。 接下来我们要做的是查询出所有的学生信息,同时关联查询出学生对应的班级信息。 创建一个Clazz
对象和Student
对象并分别映射tb_clazz
表和tb_student
表。
Clazz.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Clazz implements Serializable { private static final long serialVersionUID = 1L ; private Integer id; private String code; private List<Student> students; public Clazz () { super (); } public List<Student> getStudents () { return students; } public void setStudents (List<Student> students) { this .students = students; } @Override public String toString () { return "Clazz [id=" + id + ", code=" + code + "]" ; } }
Student.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Student implements Serializable { private static final long serialVersionUID = 1L ; private Integer id; private String name; private String sex; private Integer age; private Clazz clazz; public Student () { super (); } @Override public String toString () { return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", clazz=" + clazz + "]" ; } }
需要注意的是,Student
中的属性Clazz
是一个对象,该对象包括Clazz
的id
和code
这是现代开发中最常用的对象关联方式。
映射配置 1 2 3 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 <select id ="selectStudent" resultMap ="studentResultMap" > SELECT * FROM TB_STUDENT</select > <resultMap id ="studentResultMap" type ="org.fkit.domain.Student" > <id property ="id" column ="id" /> <result property ="name" column ="name" /> <result property ="sex" column ="sex" /> <result property ="age" column ="age" /> <association property ="clazz" column ="clazz_id" javaType ="org.fkit.domain.Clazz" select ="selectClazzWithId" /> </resultMap > <select id ="selectClazzWithId" resultType ="org.fkit.domain.Clazz" > SELECT * FROM TB_CLAZZ where id = #{id}</select >
上面的映射相对之前复杂了一些,具体解释如下: ①首先执行id
为selectstudent
的select
元素,查询所有的学生数据,此时返回的不是简单的Student
对象,因为Student
对象中还包含了Clazz
对象,所以使用resultMap
去映射返回类型。 ②id
为studentResultMap
的resultMap
元素返回类型为org.fkit.domain.Student
,其中,id
、name
、sex
和age
都是简单的属性映射,而查询的班级id
列clazz
则使用了关联映射association
assoclation
元素的解释如下
column
。表示数据库表的列名
property
。表示返回类型Student
的属性名clazz
。
javaType
。表示该clazz
属性对应的类型名称,本示例是一个Clazz
类型。
select
。表示执行一条査询语句,将查询到的记录封装到property
所代表的类型对象当中。上面的selectClazzWithId
执行一条SQL
语句,将学生的clazz_id
作为参数查询对应的班级信息
SelectStudentTest.java 1 2 3 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 public class SelectStudentTest { public static void main (String[] args) { SqlSession sqlSession = null ; try { sqlSession = FKSqlSessionFactory.getSqlSession(); List<Student> student_list = sqlSession .selectList("org.fkit.mapper.UserMapper.selectStudent" ); student_list.forEach(stu -> System.out.println(stu)); sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); e.printStackTrace(); } finally { if (sqlSession != null ) sqlSession.close(); } } }
运行结果 运行SelectStudentTest
类的main
方法,控制台显示如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 DEBUG [main] ==> Preparing: SELECT * FROM TB_STUDENT DEBUG [main] ==> Parameters: DEBUG [main] ====> Preparing: SELECT * FROM TB_CLAZZ where id = ? DEBUG [main] ====> Parameters: 1 (Integer) DEBUG [main] <==== Total: 1 DEBUG [main] ====> Preparing: SELECT * FROM TB_CLAZZ where id = ? DEBUG [main] ====> Parameters: 2 (Integer) DEBUG [main] <==== Total: 1 DEBUG [main] <== Total: 4 Student [id=1 , name=小明, sex=男, age=22 , clazz=Clazz [id=1 , code=B180102]] Student [id=2 , name=小王, sex=男, age=22 , clazz=Clazz [id=2 , code=B180203]] Student [id=3 , name=小丽, sex=男, age=21 , clazz=Clazz [id=2 , code=B180203]] Student [id=4 , name=小芳, sex=男, age=23 , clazz=Clazz [id=1 , code=B180102]]
可以看到,因为使用了关联映射 ,查询学生信息时学生对应的班级对象也被查询出来了。
集合映射 现在査询所有学生时可以关联查询出班级信息了,那如果反过来,查询所有班级时需要查询出班级中的所有学生对象,应该如何映射呢? 学生通常只对应一个班级,但是班级中会有多个学生存在,所以首先在Clazz.java
类中增加一个字段students
,该字段是一个List
集合,表示班级的多个学生。
1 2 3 4 5 6 7 public class Clazz implements Serializable { private List<Student> students; }
映射配置 1 2 3 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 <select id ="selectClazz" resultMap ="clazzResultMap" > SELECT * FROM TB_CLAZZ</select > <resultMap id ="clazzResultMap" type ="org.fkit.domain.Clazz" > <id property ="id" column ="id" /> <result property ="code" column ="code" /> <collection property ="students" javaType ="ArrayList" column ="id" ofType ="org.fkit.domain.Student" select ="selectStudentWithId" /> </resultMap > <select id ="selectStudentWithId" resultType ="org.fkit.domain.Student" > SELECT * FROM TB_STUDENT where clazz_id = #{id}</select >
上面的映射和查询学生关联班级类似,具体解释如下: ①首先执行id
为selectClazz
的select
元素,查询所有的班级数据,此时返回的不是简单的Clazz
对象,因为Clazz
对象中还包含了学生的集合对象,所以使用resultMap
去映射返回类型。id
为clazzResultMap
素返回类型为org.fkit.domain.Clazz
其中,id
和code
都是简单的属性映射,而査询班级所有学生时则使用了集合映射 collection
collection标签详解
collection
元素的解释如下:
property
。表示返回类型Clazz
的属性名students
javaType
。表示该属性对应的类型名称,本示例中是一个Arraylist
集合。
ofType
。表示集合当中的类型,本示例中是Student
类型。
column
。表示使用id
作为参数进行之后的select
语句查询。
select
。表示执行一条查询语句,将查询到的数据封装到property
所代表的类型对象当中。上面的selectStudentWithId
执行一条SQL
语句,将班级的id
作为参数查询班级对应的所有学生信息。
SelectClazzTest.java 1 2 3 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 public class SelectClazzTest { public static void main (String[] args) { SqlSession sqlSession = null ; try { sqlSession = FKSqlSessionFactory.getSqlSession(); List<Clazz> clazz_list = sqlSession .selectList("org.fkit.mapper.UserMapper.selectClazz" ); for (Clazz clazz : clazz_list) { System.out.println(clazz); List<Student> student_list = clazz.getStudents(); for (Student stu : student_list) { System.out.println(stu.getId() + " " + stu.getName() + " " + stu.getSex() + " " + stu.getAge()); } } sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); e.printStackTrace(); } finally { if (sqlSession != null ) sqlSession.close(); } } }
行SelectClazzTest
类的main
方法,控制台显示如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 DEBUG [main] ==> Preparing: SELECT * FROM TB_CLAZZ DEBUG [main] ==> Parameters: DEBUG [main] ====> Preparing: SELECT * FROM TB_STUDENT where clazz_id = ? DEBUG [main] ====> Parameters: 1 (Integer) DEBUG [main] <==== Total: 2 DEBUG [main] ====> Preparing: SELECT * FROM TB_STUDENT where clazz_id = ? DEBUG [main] ====> Parameters: 2 (Integer) DEBUG [main] <==== Total: 2 DEBUG [main] <== Total: 2 Clazz [id=1 , code=B180102] 1 小明 男 22 4 小芳 男 23 Clazz [id=2 , code=B180203] 2 小王 男 22 3 小丽 男 21
以看到,因为使用了集合映射,所以査询班级信息时班级对应的所有学生对象也被查询出来了。
原文链接: 9.4.5 ResultMaps