MyBatis是基于“数据库结构不可控”的思想建立的,也就是我们希望数据库遵循第三范式或BCNF,但实际事与愿违,那么结果集映射就是MyBatis为我们提供这种理想与现实间转换的手段了,而resultMap就是结果集映射的配置标签了。
1.从SQL查询结果到领域模型实体
在深入ResultMap标签前,我们需要了解从SQL查询结果集到JavaBean或POJO实体的过程。
1. 通过JDBC查询得到ResultSet对象
2. 遍历ResultSet对象并将每行数据暂存到HashMap实例中,以结果集的字段名或字段别名为键,以字段值为值
3. 根据ResultMap标签的type属性通过反射实例化领域模型
4. 根据ResultMap标签的type属性和id、result等标签信息将HashMap中的键值对,填充到领域模型实例中并返回
2.使用场景
在项目的实际开发中,有可能会遇到这样两种情况。
1. 实体类中的属性名与列名不相同,不能改但。导致不能自动装配值
2. 多表查询的返回值中可能需要其他对象,或者数组(一对一和一对多)
id属性 ,resultMap标签的标识。
type属性 ,返回值的全限定类名,或类型别名。
autoMapping属性 ,值范围true(默认值)|false, 设置是否启动自动映射功能,自动映射功能就是自动查找与字段名小写同名的属性名,并调用setter方法。而设置为false后,则需要在`resultMap`内明确注明映射关系才会调用对应的setter方法。
1).id标签 :ID 结果,将结果集标记为ID,以方便全局调用(适用于指定主键)
column 数据库的列名
Property需要装配的属性名
2).result标签:将查询到的列的值,反射到指定的JavaBean的 属性上
column 数据库的列名
Property 需要装配的属性名
3).association标签:复杂类型 , 多表查询(一对一)时,将根据外键或某列信息查询出的对象,直接装配给某个resultMap指定的属性。
column 数据库的列名
Property 需要装配的属性名
select 指定用来多表查询的sqlmapper
4).Collection标签:复杂类型,多表查询(一对多),将查询出的结果集合直接装配给某个对应的集合
column 数据库的列名
Property 需要装配的属性名
javaType 指定用什么类型接受返回的值(必要)
select 指定用来多表查询的sqlmapper
5).constructor– 用来将结果反射给一个实例化好的类的构造器
a) idArg –ID 参数;将结果集标记为ID,以方便全局调用
b) arg –反射到构造器的通常结果
6).discriminator – 使用一个结果值以决定使用哪个resultMap
a) case – 基本一些值的结果映射的case 情形
i. nestedresult mappings –一个case 情形本身就是一个结果映射,因此也可以包括一些相同的元素,也可以引用一个外部resultMap。
<resultMap type="" id=""> <id column="" property=""/> <result column="" property="" /> <association property="" column="" select="">association> <collection property="" column="" javaType="" select="">collection> <constructor>constructor> resultMap> |
1.id、result
id、result是最简单的映射,id为主键映射;result其他基本数据库表字段到实体类属性的映射。
实体字段 表的列名 sid stuid sname stuname gid gid grade grade |
<resultMap type="student" id="studentMap" autoMapping="true">
<id column="stuid" property="sid"/>
<result column="stuname" property="sname"/> resultMap> |
|
id、result语句属性配置细节:
属性 |
描述 |
|
property |
需要映射到JavaBean 的属性名称。 |
|
column |
数据表的列名或者标签别名。 |
|
javaType |
一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。然后,如果你是要映射到一个HashMap,那你需要指定javaType 要达到的目的。 |
|
jdbcType |
数据表支持的类型列表。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果你是直接针对JDBC 编码,且有允许空的列,而你要指定这项。 |
|
typeHandler |
使用这个属性可以覆写类型处理器。这项值可以是一个完整的类名,也可以是一个类型别名。 |
|
支持的JDBC类型
为了将来的引用,MyBatis支持下列JDBC 类型,通过JdbcType 枚举:
BIT,FLOAT,CHAR,TIMESTAMP,OTHER,UNDEFINED,TINYINT,REAL,VARCHAR,BINARY,BLOB,NVARCHAR,SMALLINT,DOUBLE,LONGVARCHAR,VARBINARY,CLOB,NCHAR,INTEGER,NUMERIC,DATE,LONGVARBINARY,BOOLEAN,NCLOB,BIGINT,DECIMAL,TIME,NULL,CURSOR
2.association联合
联合元素用来处理“一对一”的关系。
需要指定映射的Java实体类的属性,属性的javaType(通常MyBatis 自己会识别)。对应的数据库表的列名称。如果想覆写的话返回结果的值,需要指定typeHandler。
不同情况需要告诉MyBatis 如何加载一个联合。MyBatis 可以用两种方式加载:
1. select: 执行一个其它映射的SQL 语句返回一个Java实体类型。较灵活;
2. resultsMap: 使用一个嵌套的结果映射来处理通过join查询结果集,映射成Java实体类型。
association 标签
property=多表查询装配的属性名
column=通过那一列关联
select=指定查询语句,如果查询语句在其他的namespace中,则需要写全namespace.方法id
例如:在获取某个学生的信息的同时又想获取所属班级的所有信息
学生实体中的字段 |
package cn.et.fuqiang.resultMap.entity;
public class Student { private Integer sid;//学生id private String sname; //学生姓名 private Integer gid; //班级id
private Grade grade; //所属班级的所有信息 }
|
班级实体的字段 |
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade { private Integer id;//班级id private String gname;//班级名称 public Grade() { super(); // TODO Auto-generated constructor stub }
}
|
在Student的字段中,Grade班级实体,需要查询班级表才能获取,学生表中没有该列(所以需要使用多表查询中的 association标签)
想要使用多表查询先要定义一个resultMap,简单来说其实相当于一个工厂,可以指定某个实体中那些属性可以装哪一列的值,并且还要定义类似于子查询的方法。
<resultMap type="student" id="gradeInStudent">
<id column="stuid" property="sid"/>
<result column="stuname" property="sname"/>
<association property="grade" column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById">association> resultMap> |
在namespace="cn.et.fuqiang.resultMap.xml.GradeXmlInterface下的方法 |
<select id="queryGradeById" resultMap="gradeMap"> select * from grade where gid=#{0} select> |
在这个resultMap中
type:是上面定义的学生类(使用了别名,如果没有别名可以写全类名)
id:是这个resultMap的标识
因为表中的字段与实体中的不同,所以使用了下面两个子标签,将表的列与实体的属性做了映射。这样才能将表中的值获取到实体的属性中。
<id column="stuid" property="sid"/>
<result column="stuname" property="sname"/>
因为实体中还要一个班级实体,但表中没有该字段,所以就要用多表查询
<association property="grade"column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById">association>
(这里的property是实体中的字段,而column=gid就相当于关联的条件了
而select就是该关联条件对应的查询方法)
主sql
在这里我们使用resultMap定义返回的类型,这样就会自动将所有内容,装到对应的属性上
然后直接使用该主sql,查询,获取的值就会自动对应到其中。
<select id="quserAllStudent" resultMap="gradeInStudent"> select * from student select> |
以上代码写为标准的sql语句
select s.*,g.*from student s inner join grade g ons.gid=g.gid
聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList);列表中对象的类型ofType(Java实体类);对应的数据库表的列名称;
不同情况需要告诉MyBatis 如何加载一个聚集。MyBatis 可以用两种方式加载:
1. select:执行一个其它映射的SQL 语句返回一个Java实体类型。较灵活;
2. resultsMap: 使用一个嵌套的结果映射来处理通过join查询结果集,映射成Java实体类型。
Collection标签
property=多表查询装配的属性名
column=通过那一列关联
select=指定查询语句,如果查询语句在其他的namespace中,则需要写全namespace.方法id
javaType=java实体中接受的集合类型
例如,一个班级有多个学生。
学生实体中的字段 |
package cn.et.fuqiang.resultMap.entity;
public class Student { private Integer sid;//学生id private String sname; //学生姓名 private Integer gid; //班级id }
|
班级实体的字段 |
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade { private Integer id;//班级id private String gname;//班级名称 private List public Grade() { super(); // TODO Auto-generated constructor stub }
}
|
|
在Grade实体中有个List
<resultMap type="grade" id="studentsInGrade">
<id column="gid" property="id" />
<collection property="students" column="gid" javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid">collection> resultMap> |
在namespace="cn.et.fuqiang.resultMap.xml.StudentXmlInterface"下的查询语句和类型 |
<resultMap type="student" id="studentMap" autoMapping="true">
<id column="stuid" property="sid"/>
<result column="stuname" property="sname"/> resultMap> |
<select id="quserUserByGid" resultMap="studentMap"> select * from student where gid=#{0} select>
|
注意:因为实例中学生的字段名与表中的不同所以,又加了一个学生查询专属的resultMap |
在这个resultMap中
type:是上面定义的学生类(使用了别名,如果没有别名可以写全类名)
id:是这个resultMap的标识
因为表中的字段与实体中的不同,所以使用了下面的子标签,将表的列与实体的属性做了映射。这样才能将表中的值获取到实体的属性中。
<id column="gid" property="id"/>
使用多表查询获取某个班级下的所有学生
<collection property="students" column="gid"javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid">collection>
resultMap>
(这里的property是实体中的字段,而column=gid就相当于关联的条件了
而select就是该关联条件对应的查询方法)
主sql
<select id="queryAllGrade" resultMap="studentsInGrade"> select * from grade select> |
执行该映射时就会自动根据resultMap自动调用其中内嵌的sql语句查询所有学生
所有代码实例
Mybatis配置
mybatis.xml |
xml version="1.0" encoding="UTF-8"?> DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration>
<properties resource="cn/et/fuqiang/resultMap/jdbc.properties">properties>
<typeAliases>
<package name="cn.et.fuqiang.resultMap.entity"/> typeAliases>
<environments default="development"> <environment id="development"> <transactionManager type="JDBC" />
<dataSource type="POOLED"> <property name="driver" value="${driverClass}" /> <property name="url" value="${url}" /> <property name="username" value="${user}" /> <property name="password" value="${password}" /> dataSource> environment> environments>
<mappers>
<mapper class="cn.et.fuqiang.resultMap.xml.StudentXmlInterface"/> <mapper class="cn.et.fuqiang.resultMap.xml.GradeXmlInterface"/> mappers>
configuration>
|
Jdbc连接四要素配置
jdbc.properties |
url=jdbc:oracle:thin:@localhost:1521:orcl user=mybatis password=mybatis driverClass=oracle.jdbc.OracleDriver |
两个实体
student实体 |
package cn.et.fuqiang.resultMap.entity;
public class Student { private Integer sid;//学生id private String sname;//学生姓名 private Integer gid;//班级id private Grade grade;//所属班级的所有信息 public Student() { super(); // TODO Auto-generated constructor stub } public Student(Integer sid, String sname, Integer gid, Grade grade) { super(); this.sid = sid; this.sname = sname; this.gid = gid; this.grade = grade; } public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Integer getGid() { return gid; } public void setGid(Integer gid) { this.gid = gid; } public Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } }
|
Grade实体 |
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade { private Integer id;//班级 private String gname;//班级名称 private List public Grade() { super(); // TODO Auto-generated constructor stub } public Grade(Integer id, String gname) { super(); this.id = id; this.gname = gname; }
public String getGname() { return gname; } public void setGname(String gname) { this.gname = gname; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public List return students; } public void setStudents(List this.students = students; }
}
|
两个接口映射的接口
GradeXmlInterface.java |
package cn.et.fuqiang.resultMap.xml;
import java.util.List;
import cn.et.fuqiang.resultMap.entity.Grade;
public interface GradeXmlInterface { public Grade queryGradeById(Integer id); public List }
|
StudentXmlInterface.java |
package cn.et.fuqiang.resultMap.xml;
import java.util.List;
import cn.et.fuqiang.resultMap.entity.Student;
public interface StudentXmlInterface { public Student queryStudentById(Integer sid); public List public List }
|
两个映射的mapper.xml
GradeXmlInterface.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="cn.et.fuqiang.resultMap.xml.GradeXmlInterface">
<resultMap type="grade" id="gradeMap" autoMapping="false">
<id column="gid" property="id"/>
<result column="gname" property="gname"/> resultMap> <select id="queryGradeById" resultMap="gradeMap"> select * from grade where gid=#{0} select>
<resultMap type="grade" id="studentsInGrade">
<id column="gid" property="id" />
<collection property="students" column="gid" javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid">collection> resultMap>
<select id="queryAllGrade" resultMap="studentsInGrade"> select * from grade select>
mapper> |
StudentXmlInterface.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="cn.et.fuqiang.resultMap.xml.StudentXmlInterface">
<resultMap type="student" id="studentMap" autoMapping="true">
<id column="stuid" property="sid"/>
<result column="stuname" property="sname"/> resultMap> <select id="queryStudentById" resultMap="studentMap"> select * from student where stuid=#{0} select>
<resultMap type="student" id="gradeInStudent">
<id column="stuid" property="sid"/>
<result column="stuname" property="sname"/>
<association property="grade" column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById">association> resultMap> <select id="quserAllStudent" resultMap="gradeInStudent"> select * from student select>
<select id="quserUserByGid" resultMap="studentMap"> select * from student where gid=#{0} select>
mapper> |
测试类
package cn.et.fuqiang.resultMap.xml;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import cn.et.fuqiang.resultMap.entity.Grade;
import cn.et.fuqiang.resultMap.entity.Student;
public class ResultMapXmlTest {
private static SqlSession session;
private static GradeXmlInterface gradeInter=null;
private static StudentXmlInterface studentInter=null;
static{
//mybatis的配置文件
String resource = "mybatis.xml";
//使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
InputStream is =GradeXmlInterface.class.getResourceAsStream(resource);
//构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
//使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件)
//Readerreader = Resources.getResourceAsReader(resource);
//构建sqlSession的工厂
//SqlSessionFactorysessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中sql的sqlSession
session = sessionFactory.openSession();
gradeInter=session.getMapper(GradeXmlInterface.class);
studentInter=session.getMapper(StudentXmlInterface.class);
}
//@Test
public void queryGradeTest(){
Grade grade=gradeInter.queryGradeById(1);
System.out.println(grade.getId()+"------"+grade.getGname());
}
//@Test
public void queryStudentTest(){
Student student=studentInter.queryStudentById(1);
System.out.println(student.getSid()+"------"+student.getSname());
}
//@Test
public void queryGradeInStudent(){
List<Student> students=studentInter.quserAllStudent();
for (Student stu : students) {
System.out.println(stu.getSid()+"------"+stu.getSname()+"-------"+stu.getGrade().getGname());
}
}
@Test
public void queryStudentInGrade(){
List<Grade> students=gradeInter.queryAllGrade();
for (Grade grade : students) {
System.out.println(grade.getGname()+"班"+grade.getStudents().size()+"人");
}
}
}