注意事项
- 在使用一对多关系时,不要重写toString(),因为User类中有属性Pet,在Pet类中有属性User,会造成两个entity来回调用,造成栈溢出。尤其使用lombok时要注意,它的@Data注解是默认重写了toString()的,出错在个这个不起眼的注解上,难以排错。
- 正确的使用方式是通过getXX()的方式来获得返回值。
一对多的映射
推荐的XXMapper.xml方式
一个人,可以有多个宠物。这是主人的entity。注意此处属性Pet是用的List<>集合,表示会对应多个。
public class User {
private Integer user_id;
private String name;
//这儿到底该是pet还是List<Pet>?是List,一对多
private List<Pet> pets;
}
映射语句UserMapper.xml的写法:
<mapper namespace="com.stein.mapper.UserMapper">
<resultMap id="userResultType" type="User">
<!--1-2句,封装entity的属性值-->
<id property="user_id" column="user_id"/>
<result property="name" column="name"/>
<!--column="user_id"这儿是入参。ofType是集合<>括号里面的类型,写不写没看出差异-->
<!--这是第3句,利用另一句sql查询语句,查询另一张表,由指向的方法来封装子类型的属性值-->
<collection property="pets" column="user_id" ofType="Pet"
select="com.stein.mapper.PetMapper.queryPetById"/>
</resultMap>
<!--sql语句的主体,返回类型嵌套其他类型,使用了resultMap-->
<select id="queryUserById" parameterType="Integer" resultMap="userResultType">
select * from user where user_id=#{userid}
</select>
@注解方式
@Select("Select * from user where user_id=#{userid}")
@Results({
@Result(id = true,property = "user_id",column = "user_id"),
@Result(property = "name",column = "name"),
@Result(property="pets",column="user_id",
many=@Many(select="com.stein.mapper.PetMapperAnnotation.queryPetById"))
})
User queryUserById(Integer userid);
一只宠物,对应一个主人。这是宠物的entity。注意此处属性User只有一个。
public class Pet {
private Integer pet_id;
private String name;
private User user;
}
映射语句PetMapper.xml的写法:
此处宠物与主人一对一,所以用的<association>。但是宠物有多只啊,这儿只写了单个的情况,是如何实现多个呢,个人猜测是在UserMapper的<collection>进行了遍历,在此处执行了多次来实现的。
<mapper namespace="com.stein.mapper.PetMapper">
<resultMap id="petResultType" type="Pet">
<id property="pet_id" column="pet_id"/>
<result property="name" column="pet_name"/>
<association property="user" column="user_id" select="com.stein.mapper.UserMapper.queryUserById"/>
</resultMap>
<select id="queryPetById" parameterType="Integer" resultMap="petResultType">
select * from pet where user_id=#{userId}
</select>
</mapper>
@注解方式
为了便于复用,添加了属性id,给该ResultMap取一个名字。此时value不可省略,后面跟的是Result[ ]数组。
@Select("select * from pet where user_id=#{userId}")
@Results(id="petResultMap",value={
@Result(id = true,property = "pet_id",column = "pet_id"),
@Result(property = "name",column = "pet_name"),
@Result(property="user",column="user_id",
one=@One(select="com.stein.mapper.UserMapperAnnotation.queryUserById"))
})
List<Pet> queryPetById(Integer userId);
resultMap的复用
- 现在要在宠物表中,先使用petId查询pet信息表,封装宠物属性;再利用查询得到的主人id,在主人表中,查询主人信息。
- 而上面PetMapper.xml已经在<resultMap id=“petResultType”> 中,用宠物表中,用主人id查询宠物信息,先封装宠物属性;再在主人表中,用主人id,查询主人信息。
- 此时resultMap的作用就是封装宠物信息,查询主人信息,所用到的操作完全一模一样。此时便可进行复用。
<select id="queryPetByPetId" parameterType="Integer" resultMap="petResultType">
select * from pet where pet_id=#{petId} //不同的sql语句
</select>
@注解方式
可以使用@ResultMap引用id来复用,也可以直接复制要引用的部分。
@Select("select * from pet where pet_id=#{petId}")
@ResultMap("petResultMap")
Pet queryPetByPetId(Integer petId);
使用方法总结:
- 可以看出,XXMapper.xml的映射方法,在使用<resultMap>时,column都是指的DB表的列,且作为入参,封装entity的属性。
- 其中的1-2句是建立封装映射;第3句是通过级联查询或者说二次查询进行封装,column同样是作为入参,实际封装的属性名映射,在select指向的方法语句中体现。
- @注解方式和xml方式基本相同,但是@注解方式是和接口写在一起的,解耦性不如xml方式,所以不是很推荐。
- one是一对一
- many是一对多
关于<association>的两种映射方式:
- 级联查询(详见上一篇一对一关系):使用join或者cross join进行的级联查询,需要的内容都全部已经返回,此时封装子类型的语句:
<association property="idCard" javaType="IdCard">
<result property="id" column="card_id"/>
<result property="card_num" column="card_num"/>
</association>
参考sql语句:SELECT * FROM person JOIN idcard on person.card_Id = idcard.id and person.id=#{id}
- 分表查询:利用第一次查询的结果,再查另一张表,得到的结果再封装子类型的语句:
<association property="user" column="user_id" select="com.stein.mapper.UserMapper.queryUserById"/>