ResultMap
ResultMap 的设计思想是,对于简单的语句不需要配置结果映射,而对于复杂一点的语句只需要描述它们的关系。下面来看一下ResultMap下的部分标签和属性:
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
<!-- 实例化类时,注入结果到构造方法中 -->
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<!-- title属性,对应库中的blog_title列-->
<result property="title" column="blog_title"/>
<!-- 对象类型数据 Author author -->
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
</association>
<!-- 集合类型数据 List<Post> posts, Post中还包含其他复杂类型-->
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<!-- 调用上面的association -->
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<!-- 使用结果值来决定使用哪个 resultMap -->
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
看着是不是有点晕,ResultMap是这么复杂的吗?其实捋顺了ResultMap中的结构,也就不觉得它有多么复杂了,而且上面提到的所有子标签也并不是都经常遇到。只有result、collection和association使用的频率会高一些。
ResultMap标签中有三个属性:id、type、autoMapping
- id:resultMap的唯一标识
- type:类的完全限定名或类的别名
- autoMapping(慎用):设置这个属性,MyBatis将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。
下面我们就逐个的来讲解一下ResultMap中的这些子标签以及它们的强大功能。
id、result
id标签用于标识数据库表中主键列和类中属性的对应关系:
<id property="属性名" column="表中的主键列"/>
result标签配置对象中的属性和数据库表中列的对应关系
<result property="pojo中的属性名" column="对应数据库表中的column"/>
Collection:
collection在JDK中是集合接口,所有集合类型都需要实现它,所以这里也不难理解在mybatis中它的作用是用来配置对象中的集合类型数据。但是collection的使用远远不止我们上面例子中使用的那么简单。下面就来看看collection能做什么:
首先我们创建三张表:grade、student、grade_student,对应两个类Grade和Student
@Data
public class Grade {
private int id;
private String name;
private List<Student> studentList;
}
@Data
public class Student {
private String id;
private String name;
private int age;
}
1、映射简单对象
<resultMap id="grade" type="cn.zsm.mybatis.student.Grade">
<id property="id" column="id"/>
<result property="name" column="name"/>
</resultMap>
<select id="selectGrades" resultMap="grade" >
select * from grade
</select>
这种映射一般都会省略,因为即使列名与pojo中的属性名不同,也可以直接为列取别名与pojo中的属性进行映射
2、映射对象中的集合类型,结果嵌套
这次我们将使用一个查询获取班级和学生的信息。
<resultMap id="grade" type="cn.zsm.mybatis.student.Grade">
<id property="id" column="gid"/>
<result property="name" column="gname"/>
<collection property="studentList" ofType="cn.zsm.mybatis.student.Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
</collection>
</resultMap>
<select id="selectGrades" resultMap="grade" >
select g.id gid,g.name gname,s.id, s.name, s.age from grade g
inner join grade_student gs on g.id = gs.class_id
inner join student s on s.id = gs.student_id
</select>
List<Grade> selectGrades();
查询结果为:
[Grade(id=1, name=一班,
studentList=[Student(id=1, name=stu1, age=10),
Student(id=2, name=stu2, age=10),
Student(id=3, name=stu3, age=11)]),
Grade(id=2, name=二班,
studentList=[Student(id=4, name=stu4, age=10),
Student(id=1, name=stu1, age=10)])
]
3、配置对象中的集合类型,关联的查询语句嵌套
<resultMap id="grade" type="cn.zsm.mybatis.student.Grade">
<id property="id" column="gid"/>
<result property="name" column="gname"/>
<collection property="studentList" ofType="cn.zsm.mybatis.student.Student" column="gid" select="getStudents"/>
</resultMap>
<select id="selectGrades" resultMap="grade" >
select g.id gid,g.name gname from grade g
</select>
<select id="getStudents" resultType="cn.zsm.mybatis.student.Student">
select s.* from student s
inner join grade_student gs on s.id = gs.student_id
where gs.class_id = #{id}
</select>
List<Grade> selectGrades();
查询结果与上面的结果相同。
注意,这里在配置Grade的resultMap时,配置studentList属性时,与上面的配置大不相同。这里使用了collection标签的column、select属性,select用于指定要嵌套的查询方法,而column用于指定传入嵌套方法中的参数和参数名——如果嵌套方法的查询参数是简单类型,则如上面的例子中使用,多个参数使用【逗号】分隔;如果嵌套方法的查询参数是对象类型,则column取值格式为:{"paramName":"columnName","paramName":"columnName",......}。(对此可参考:Mybatis注解@ResultMap)
association :
association用于配置对象结果映射,比如上面的例子中,每个学生都有一个班级,我们可以在Student类中添加一个Grade类型的变量 Grade grade,如下所示:
@Data
public class Grade {
private int id;
private String name;
}
@Data
public class Student {
private String id;
private String name;
private int age;
private Grade grade;
}
这样我们在查询Student信息时,也可以顺带查询出该学生所属的班级信息。(注意,上面的数据中有一个学生属于两个班级,需要修改该数据)。
下面我们来实现该查询。
<resultMap id="student" type="cn.zsm.mybatis.student.Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="grade" javaType="cn.zsm.mybatis.student.Grade">
<id property="id" column="gid"/>
<result property="name" column="gname"/>
</association>
</resultMap>
<select id="selectStudents" resultMap="student">
select s.id, s.name, s.age, g.id gid, g.name gname from student s
inner join grade_student gs on s.id = gs.student_id
inner join grade g on g.id = gs.class_id
</select>
List<Student> selectStudents();
查询结果为:
association与collection的使用基本相同,只不过一个是配置对象集合类型,一个是封装单个对象类型属性。association中也有select、column等属性,用法与collection相同,这里就不再多说了。
constructor:
通过修改对象属性的方式,可以满足大多数的数据传输对象(Data Transfer Object, DTO)以及绝大部分领域模型的要求。但有些情况下你想使用不可变类。 一般来说,很少改变或基本不变的包含引用或数据的表,很适合使用不可变类。 构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBean 属性来完成注入,但有一些人更青睐于通过构造方法进行注入。 constructor 元素就是为此而生的
看看下面这个构造方法:
public class User { ........ public User(Integer id, String username, int age) { //... } }
为了将结果注入构造方法,MyBatis 需要通过某种方式定位相应的构造方法。 在下面的例子中,MyBatis 搜索一个声明了三个形参的的构造方法,参数类型以 java.lang.Integer, java.lang.String 和 int 的顺序给出。
<constructor> <idArg column="id" javaType="int"/> <arg column="username" javaType="String"/> <arg column="age" javaType="_int"/> </constructor>
constructor很少使用,用兴趣的可以去官网详细查看:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps