双向many2one/one2many
双向many2one/one2many是比较常见的对象关系。总体的使用方式和单向的many2one基本相同。先来看看对象的关系,在这里使用Department和Employee对象来完成双向的many2one/one2many。对象设计如下:
//员工对象
public class Employee {
private Long id;
private String name;
//关联部门对象
private Department dept;
//getter & setter;
}
//部门对象
public class Department {
private Long id;
private String name;
//关联的员工对象列表
private Set<</span>Employee> emps = new HashSet<</span>Employee>();
//getter & setter
}
先看Department对象的映射文件:
<mapper namespace="cd.itcast.mybatis.department">
<insert id="save" keyProperty="id" parameterType="Department" useGeneratedKeys="true">
INSERT INTO department(name) values (#{name})
</insert>
<resultMap type="Department" id="departmentmap">
<id column="did" property="id"/>
<result column="dname" property="name"/>
<collection property="emps" column="dept_id" ofType="Employee">
<id column="id" property="id"/>
<result column="name" property="name"/>
<association property="dept" column="dept_id" resultMap="departmentmap"/>
</collection>
</resultMap>
<selectid="get" parameterType="int" resultMap="departmentmap">
SELECT e.*,d.name as dname,d.id as did FROM employee e LEFT JOINdepartment d on e.dept_id = d.id WHERE d.id = #{id}
</select>
</mapper>
在上面的映射中,需要特别注意的一点:在department的映射departmentmap中,使用了collection完成了内嵌的映射。 特别的,在内嵌的映射中,我们又使用了association来映射Employee中的Department对象。注意,在这里,我们必须使用resultMap="departmentmap"来映射 department,而不能使用resultType。在这里,很明显是一个循环引用,Mybatis会使用SqlSession的缓存来完成对象的嵌套引用。 下面来看看Employee的映射文件:<mapper namespace="cd.itcast.mybatis.employee"> <insert id="save" keyProperty="id" parameterType="Employee" useGeneratedKeys="true"> INSERT INTO employee(name) values (#{name}) </insert> <resultMap type="Employee" id="employeemap"> <id column="id" property="id"/> <result column="name" property="name"/> <association property="dept" column="dept_id" javaType="Department"> <id column="did" property="id"/> <result column="dname" property="name"/> </association> </resultMap> <select id="get" parameterType="int" resultMap="employeemap"> SELECT e.*,d.id as did,d.name as dname FROM employee e LEFT JOIN department d on e.dept_id = e.id WHERE e.id = #{id} </select> </mapper>
get方法映射还是使用内嵌的映射完成的。但是在这里,非常重要的一个问题,当我们完成了association关联完成Employee里面的Department对象的映射的时候,我们没有再次去映射这个Department对应的Employee列表对象。换句话说,当使用selectOne得到Employee对象的时候,通过这个Employee对象得到的Department对象,在从这个Department对象里面去拿Employee列表的时候,得到的是一个空的列表。要解决这个问题,不能像下面这样使用:<resultMap type="Employee" id="employeemap"> <id column="id" property="id"/> <result column="name" property="name"/> <association property="dept" column="dept_id" javaType="Department"> <id column="did" property="id"/> <result column="dname" property="name"/> <collection property="emps" column="dept_id" ofType="Employee" resultMap="employeemap" /> </association> </resultMap>
注意,如果是这样映射的话,通过employee.getDept().getEmps()得到的列表,Mybatis同样会使用SqlSession的缓存来完成这个循环引用。但是,因为这是get方法,得到的只是一个Employee对象,所以,不管Department有多少Employee和其关联,都只会只有当前的这个Employee对象。即,employee.getDept().getEmps().size()==1如果要完整的映射这个关系,就只能使用额外的select来完成了。但是这样做会导致很多额外的SQL,不建议这样做。如果需求中真需要从employee中得到department,并且再从department中得到employee,那么建议使用cd.itcast.mybatis.department.get再得一次Department对象就可以了。