resultMap
元素是 MyBatis 中最重要最强大的元素。它可让你从 90% 的 JDBCResultSets
数据提取代码中解放出来,并在一些情形下容许你进行一些 JDBC 不支持的操做。实际上,在为一些好比链接的复杂语句编写映射代码的时候,一份resultMap
可以代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句作到零配置,对于复杂一点的语句,只须要描述语句之间的关系就好了。
resultMap
能够将查询到的复杂数据,好比多张表的数据、一对一映射、一对多映射等复杂关系聚合到一个结果集当中。平常的业务开发一般都会和它打交道,今天就对 resultMap
进行一个详细讲解。java
接下来咱们来看看 resultMap
是如何进行映射的。数据库
咱们声明一个数据库对应的实体类:mybatis
/** * @author felord.cn * @since 16:50 **/ @Data public class Employee implements Serializable { private static final long serialVersionUID = -7145891282327539285L; private String employeeId; private String employeeName; private Integer employeeType; }
那么它对应的 resultMap
为:app
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper"> <resultMap id="EmployeeMap" type="cn.felord.mybatis.entity.Employee"> <id column="employee_id" property="employeeId"/> <result column="employee_name" property="employeeName"/> <result column="employee_type" property="employeeType"/> </resultMap> </mapper>
咱们来解释这些配置的属性:性能
<mapper namespace="全局惟一的名称空间"> <resultMap id="本namespace下惟一" type="对应映射的实体"> <id column="数据库主键字段名或者别名,使用它提升总体性能" property="对应实体属性"/> <result column="数据库字段名或者别名" property="对应实体属性"/> </resultMap> </mapper>
以上方式是经过 Getter 和 Setter 方法进行注入,也就是实体类必须有无参构造,对应属性必须有Getter 和 Setter 方法。this
Getter 和 Setter 方法进行注入是咱们最经常使用的方式。可是 Mybatis 一样支持构造注入,若是 Employee
存在以下构造方法:spa
public Employee(String employeeId, String employeeName, Integer employeeType) { this.employeeId = employeeId; this.employeeName = employeeName; this.employeeType = employeeType; }
那么对应的 resultMap
能够这样写:设计
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper"> <resultMap id="EmployeeMap" type="cn.felord.mybatis.entity.Employee"> <constructor> <idArg column="employee_id" javaType="String"/> <arg column="employee_name" javaType="String"/> <arg column="employee_type" javaType="String"/> </constructor> </resultMap> </mapper>
细心的同窗发现这里并无 property
属性,其实当你不声明property
属性时会按照构造方法的参数列表顺序进行注入。code
在 Mybatis 3.4.3 引入了 name
属性后咱们就能够打乱 constructor
标签内的 arg
元素的顺序了。xml
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper"> <resultMap id="EmployeeConstructorMap" type="cn.felord.mybatis.entity.Employee"> <constructor> <idArg column="employee_id" javaType="String" name="employeeId"/> <!-- 你能够不按参数列表顺序添加--> <arg column="employee_type" javaType="Integer" name="employeeType"/> <arg column="employee_name" javaType="String" name="employeeName"/> </constructor> </resultMap> </mapper>
像 Java 中的类同样,resultMap
也是能够继承的。下面是两个有继承关系的 Java 类:
那么 RegularEmployee
的 resultMap
就能够这么写:
<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee"> <result column="level" property="level"/> <result column="job_number" property="jobNumber"/> <association property="department" javaType="cn.felord.mybatis.entity.Department"> <id column="department_id" property="departmentId"/> <result column="department_name" property="departmentName"/> <result column="department_level" property="departmentLevel"/> </association> </resultMap>
跟 Java 的继承关键字同样使用 extends
来进行继承。
明眼人会看出来 2.3 最后一个 resultMap
示例中有一个 association
标签。这个用来作什么用呢?打个比方,每个正式员工 RegularEmployee
会对应一个部门 Department
,业务中会有把这种 一对一 关系查询出来的需求。因此 association
就派上了用场。
<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee"> <result column="level" property="level"/> <result column="job_number" property="jobNumber"/> <association property="属性名称" javaType="对应的Java类型"> <id column="department_id" property="departmentId"/> <result column="department_name" property="departmentName"/> <result column="department_level" property="departmentLevel"/> </association> </resultMap>
association
能够继续嵌套下去,有可能关联的对象中还有一对一关系。
有一对一关联,天然会有一对多关联。咱们反客为主,一个部门有多个员工,咱们可能须要查询一个部门的信息以及全部员工的信息装载到 DepartmentAndEmployeeList
中去。
/** * @author felord.cn * @since 15:33 **/ public class DepartmentAndEmployeeList extends Department { private static final long serialVersionUID = -2503893191396554581L; private List<Employee> employees; public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } }
咱们能够在 resultMap
中使用 collection
关键字来处理一对多映射关系:
<resultMap id="DepartmentAndEmployeeListMap" extends="DepartmentMap" type="cn.felord.mybatis.entity.DepartmentAndEmployeeList"> <collection property="employees" ofType="cn.felord.mybatis.entity.RegularEmployee"> <id column="employee_id" property="employeeId"/> <result column="employee_name" property="employeeName"/> <result column="level" property="level"/> <result column="job_number" property="jobNumber"/> </collection> </resultMap>
你们都知道,员工并不都是正式工,还有临时工。有时候咱们也指望可以将这两种区分开来,至于缘由你懂的。不深刻讨论这个问题了。就这个需求而言咱们的映射关系又复杂了,咱们须要根据某个条件来判断哪条数据是正式工,哪条数据是临时工,而后分别装入下面这个实体类的 regularEmployees
、temporaryEmployees
中。
/** * @author felord.cn * @since 15:33 **/ public class DepartmentAndTypeEmployees extends Department { private static final long serialVersionUID = -2503893191396554581L; private List<RegularEmployee> regularEmployees; private List<TemporaryEmployee> temporaryEmployees; // getter setter }
鉴别器(discriminator
)元素就是被设计来应对这种状况的,另外也能处理其它状况,例如类的继承层次结构。 鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。
为此咱们须要在 Employee
类中增长一个 int
类型的 employeeType
属性来区分正式工和临时工,其中 1
表明正式工,而 0
表明临时工。而后咱们来编写查询 DepartmentAndTypeEmployees
的 resultMap
:
<resultMap id="DepartmentAndTypeEmployeesMap" extends="DepartmentMap" type="cn.felord.mybatis.entity.DepartmentAndTypeEmployees"> <collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee"> <discriminator javaType="int" column="employee_type"> <case value="1"> <id column="employee_id" property="employeeId"/> <result column="employee_name" property="employeeName"/> <result column="employee_type" property="employeeType"/> <result column="level" property="level"/> <result column="job_number" property="jobNumber"/> </case> </discriminator> </collection> <collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee"> <discriminator javaType="int" column="employee_type"> <case value="0"> <id column="employee_id" property="employeeId"/> <result column="employee_name" property="employeeName"/> <result column="employee_type" property="employeeType"/> <result column="company_no" property="companyNo"/> </case> </discriminator> </collection> </resultMap>
切记必定是先声明 DepartmentAndTypeEmployees
的两个 List
,而后在 collection
标签内部使用 discriminator
标签。
这里很容易犯如下错误,下面的写法虽然能够查询出数据可是知足不了上述需求:
<resultMap id="DepartmentAndTypeEmployeesMap" extends="DepartmentMap" type="cn.felord.mybatis.entity.DepartmentAndTypeEmployees"> <discriminator javaType="int" column="employee_type"> <case value="1"> <collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee"> <!--省略--> </collection> </case> <case value="0"> <collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee"> <!--省略--> </collection> </case> </discriminator> </resultMap>
这种写法的意思是:当发现该条数据中 employee_type=1
时,就新建一个 List<RegularEmployee>
并把该条数据放进去,每次都会新建一个 List<RegularEmployee>
;当employee_type=0
时也同样。这样的话最终就会返回一个 List<DepartmentAndTypeEmployees>
。
resultMap
可以知足大部分业务场景对于数据映射的需求,今天咱们对 Mybatis 中 resultMap
的一些用法进行了讲解,其实 resultMap
还有一些有用的属性,基于篇幅的缘由这里再也不讲解,可阅读 Mybatis 官方文档。可是请注意虽然 resultMap
功能强大,必定要合理使用,级联过于复杂会影响后期维护和性能。好比当一对多映射时,多的一方若是数据条数过大,会增长内存消耗和读写性能。但愿今天的文章对你使用 resultMap
有所帮助,更及时的技术资讯请多多关注:码农小胖哥。
本次文章的 DEMO ,可关注公众号:Felordcn 回复 resultMap 获取。
关注公众号:Felordcn 获取更多资讯