转自:http://blog.csdn.net/ykzhen2015/article/details/51161141
MyBatis级联探讨第一篇——概念和模型
谈谈MyBatis级联,文章尽量具体一些。
1、概述
关于MyBatis级联有三种:
association :一对一的关联
collection : 一对多的关联
discriminator :鉴别器,
它可以用于鉴别是使用那个resultMapper,或者你暂时不能理解,可以暂时放放。后面我们会再谈。
MyBatis当前只能支持查询的级联,而不能支持新增,删除和更新这些操作。所以这里只会讨论查询的级联。当然首先谈论的是三种级联的用法,然后就是N+1问题,延迟加载的问题。
2、模型
在讨论这些关联之前,我们需要先定义一个模型。
我们先简单描述一下模型:
我们有一个员工表,一个员工卡表,显然员工和员工卡是一对一的关系。
而员工有男性员工和女性员工所以他们的健康表是不一样的。
一个员工可以参与多个工程,所以有张员工工程表把,员工和工程关联起来了,员工和工程是一对多的关系。
下面笔者给出建表语句:
- CREATE TABLE t_employee
- (
- id INT(20) NOT NULL AUTO_INCREMENT,
- emp_name VARCHAR(60) NOT NULL,
- sex TINYINT(2) NOT NULL,
- PRIMARY KEY (id)
- );
- CREATE TABLE t_employee_card
- (
- id INT(20) NOT NULL AUTO_INCREMENT,
- emp_id INT(20) NOT NULL,
- card_no VARCHAR(20) NOT NULL,
- PRIMARY KEY (id)
- );
- CREATE TABLE t_employee_project
- (
- id INT(20) NOT NULL AUTO_INCREMENT,
- emp_id INT(20),
- proj_id INT(20),
- PRIMARY KEY (id)
- );
- CREATE TABLE t_healthy_male
- (
- id INT(20) NOT NULL AUTO_INCREMENT,
- emp_id INT(20),
- prostate VARCHAR(60),
- PRIMARY KEY (id)
- );
- CREATE TABLE t_healthy_remale
- (
- id INTEGER NOT NULL AUTO_INCREMENT,
- emp_id INT(20),
- uterus VARCHAR(60),
- PRIMARY KEY (id)
- );
- CREATE TABLE t_project
- (
- id INT(20) NOT NULL AUTO_INCREMENT,
- proj_name VARCHAR(60),
- PRIMARY KEY (id)
- );
3、定义POJO
让我们先定义POJO,如下:
- public class Employee implements Serializable {
- private static final long serialVersionUID = 5892612078624312365L;
- private Integer id;
- private String empName;
- private Integer sex;
- //员工卡,一对一关联
- private EmployeeCard employeeCard;
- //项目,一对多关联
- private List<Project> projectList;
- //########setter and getter############
- }
- public class MaleEmployee extends Employee implements Serializable {
- private static final long serialVersionUID = 6597520360502006892L;
- private List<String> prostateList;
- //########setter and getter############
- }
- public class FemaleEmployee extends Employee implements Serializable {
- private static final long serialVersionUID = -7565810909492919315L7;
- private List<String> uterusList;
- //########setter and getter############
- }
- public class EmployeeCard implements Serializable {
- private static final long serialVersionUID = -7593225091990005041L;
- private Integer empId;
- private Integer cardNo;
- //########setter and getter############
- }
- public class Project implements Serializable {
- private static final long serialVersionUID = -7834541701010022018L;
- private Integer id;
- private String projName;
- //########setter and getter############
- }
除了Employee外,都是很简单的POJO。Employee其实也不复杂,Employee是基类,然后通过其派生一个男性一个女性的类,也不难理解。
下面,我们将就这三种级联展开更深一层的讨论。
MyBatis级联探讨第二篇——鉴别器(discriminator)
1、概述:
鉴别器在于确定使用那个ResultMap来映射SQL查询语句,在实现中我们往往有一个基类,然后可以派生一些类。比如我们要选择一群人可以用List<Person>,然而Person里面有个性别sex,根据它还可以分为Male或者Female。鉴别器就要根据sex决定用Male还是用Female相关的Mapper进行映射。
这些话还是很抽象,不过说起鉴别器,语言真的不好用描述,不过不要紧,我们来看一个实例就豁然开朗了,我们知道在上篇中我们已经有了一个员工的POJO,然后继承这个POJO分成一个男性,一个女性的POJO。
当我们查询一批员工的时候,我们希望的是返回一个List<Employee>,而里面的元素根据性别(sex)自动匹配是MaleEmployee或者是FemaleEmployee,于是我们需要根据sex的值去决定使用MaleEmployee或者是FemaleEmployee的resultMap去映射,这便是鉴别器。
让我们来定义employ的mapper,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="com.ykzhen2015.csdn.mapper.EmployeeMapper">
- <resultMap id="employeeMap" type="com.ykzhen2015.csdn.pojo.Employee">
- <id property="id" column="id" />
- <result property="empName" column="emp_name" />
- <result property="sex" column="sex" />
- <association property="employeeCard" column="id"
- select="com.ykzhen2015.csdn.mapper.EmployeeCardMapper.getEmployeeCardByEmpId" />
- <collection property="projectList" column="id"
- select="com.ykzhen2015.csdn.mapper.ProjectMapper.findProjectByEmpId" />
- <discriminator javaType="int" column="sex">
- <case value="1" resultMap="maleEmployeeMap" />
- <case value="2" resultMap="femaleEmployeeMap" />
- </discriminator>
- </resultMap>
- <select id="getEmployee" parameterType="int" resultMap="employeeMap">
- select id, emp_name as empName, sex from t_employee where id =#{id}
- </select>
- <resultMap id="maleEmployeeMap" type="com.ykzhen2015.csdn.pojo.MaleEmployee" extends="employeeMap">
- <collection property="prostateList" select="com.ykzhen2015.csdn.mapper.MaleEmployeeMapper.findProstateList" column="id" />
- </resultMap>
- <resultMap id="femaleEmployeeMap" type="com.ykzhen2015.csdn.pojo.FemaleEmployee" extends="employeeMap">
- <collection property="uterusList" select="com.ykzhen2015.csdn.mapper.FemaleEmployeeMapper.findUterusList" column="id" />
- </resultMap>
- </mapper>
我们这里定义了employee的resultMap,它除了级联其他的和平时我们定义的没什么不一样。这里先不看别的级联,先看看鉴别器:<discriminator>元素,我们定义了用javaType说明它用的是整数作为参数,而column指的是SQL对应的列为sex。
那么<case>定义的是你的条件分支:
当sex=1时候,采用maleEmployeeMap;
当sex=2时,采用femaleEmployeeMap。
maleEmployeeMap和femaleEmployeeMap都继承了employeeMap,并且扩展了一个属性,它们用select属性,来定义如何取对应的属性数据。要记住下面这句话,后面我们还将讨论它:
这里使用了全限定路径,其次用column="id"作为参数传递,如果是多个参数的,需要用逗号分隔。
3、关联Mapper:
上面我们看到了我们使用了select关联其他的sql语句,而select里面给的就是一个全限定的路径。分别是:
com.ykzhen2015.csdn.mapper.MaleEmployeeMapper.findProstateList
和
com.ykzhen2015.csdn.mapper.FemaleEmployeeMapper.findUterusList
现在让我们看看这两个Mapper是怎么样的:
- <?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="com.ykzhen2015.csdn.mapper.MaleEmployeeMapper">
- <select id="findProstateList" parameterType="int" resultType="string">
- select prostate from t_healthy_male where emp_id = #{emp_id}
- </select>
- </mapper>
- <?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="com.ykzhen2015.csdn.mapper.FemaleEmployeeMapper">
- <select id="findUterusList" parameterType="int" resultType="string">
- select uterus from t_healthy_female where emp_id = #{emp_id}
- </select>
- </mapper>
记得上节中标红的这句话,就是两者的关联,这里两个select的全限定id,上节的这句话select属性是保持一致,而参数都是上节column表明的id,这样便建立了关系。
好,鉴别器就是那么简单,这样便能将两者关联在一起了。
MyBatis级联探讨第三篇——一对一和一对多
除了鉴别器,显然MyBatis还有一对一,一对多的关联。而多对多的关联可以转变为两个一对多的关联,也十分简单,这里我们将讨论它们的使用。
继续我们上篇的代码,我们先看一下员工的Mapper:
- <?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="com.ykzhen2015.csdn.mapper.EmployeeMapper">
- <resultMap id="employeeMap" type="com.ykzhen2015.csdn.pojo.Employee">
- <id property="id" column="id" />
- <result property="empName" column="emp_name" />
- <result property="sex" column="sex" />
- <association property="employeeCard" column="id"
- select="com.ykzhen2015.csdn.mapper.EmployeeCardMapper.getEmployeeCardByEmpId" />
- <collection property="projectList" column="id"
- select="com.ykzhen2015.csdn.mapper.ProjectMapper.findProjectByEmpId" />
- <discriminator javaType="int" column="sex">
- <case value="1" resultMap="maleEmployeeMap" />
- <case value="2" resultMap="femaleEmployeeMap" />
- </discriminator>
- </resultMap>
- <select id="getEmployee" parameterType="int" resultMap="employeeMap">
- select id, emp_name as empName, sex from t_employee where id =#{id}
- </select>
- <resultMap id="maleEmployeeMap" type="com.ykzhen2015.csdn.pojo.MaleEmployee" extends="employeeMap">
- <collection property="prostateList" select="com.ykzhen2015.csdn.mapper.MaleEmployeeMapper.findProstateList" column="id" />
- </resultMap>
- <resultMap id="femaleEmployeeMap" type="com.ykzhen2015.csdn.pojo.FemaleEmployee" extends="employeeMap">
- <collection property="uterusList" select="com.ykzhen2015.csdn.mapper.FemaleEmployeeMapper.findUterusList" column="id" />
- </resultMap>
- </mapper>
这里我们主要看到association和collection两个标签。
(1)对于association而言,我们在Employee 定义的时候属性employeeCard 是一个POJO对象。
而对于collection而言,我们在Employee定义的时候属性projectList是一个List列表。
这两个需要我们注意。
(2)其次值得我们注意的是他们之间的关联的SQL语句,都是需要用select属性去定义,这里我给的是全限定路径名称和鉴别器是一致的。
(3)再次我们进行关联的时候,往往需要传递参数,这里的column就是一个参数定义。
1、一对一关联(association)
还是老规矩,看看一对一关联的Mapper
- <?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="com.ykzhen2015.csdn.mapper.EmployeeCardMapper">
- <select id="getEmployeeCardByEmpId" parameterType="int" resultType="com.ykzhen2015.csdn.pojo.EmployeeCard">
- SELECT id, emp_id as empId, card_no as cardNo FROM t_employee_card WHERE emp_id = #{empId}
- </select>
- </mapper>
好的,这里很明显,上面红色字体谈论的select属性对应的就是这个SQL。其中column属性定义的参数就是这里的empId。这样便完成了一对一的关联。
2、一对多关联(collection)
看代码,上面的论述也比较清晰了,我也不再累赘了。
- <?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="com.ykzhen2015.csdn.mapper.ProjectMapper">
- <select id="findProjectByEmpId" parameterType="int" resultType="com.ykzhen2015.csdn.pojo.Project">
- SELECT a.id, a.proj_name as projName FROM t_project a, t_employee_project b
- where a.id = b.proj_id and b.emp_id = #{empId}
- </select>
- </mapper>
好这里我们就讲解完了,鉴别器,一对一,一对多的关联。但是这里还有一个大的问题没有处理(延迟加载问题),留待下章分解。