mybtis中的级联关系(关联关系、集合关系)association、collection(一对一关系、一对多关系、多对多关系)

本文详细解析MyBatis中如何处理关联(association)与集合(collection)关系,包括一对一、一对多和多对多关系的懒加载实现,通过具体案例如丈夫与妻子、老师与学生、玩家与游戏的映射配置,阐述如何避免循环调用及正确使用<association>和<collection>标签。
摘要由CSDN通过智能技术生成

 

  • 当我们在做查询时,多个表之间可能存在着级联关系

比如丈夫表和妻子表,丈夫表里面含有妻子的外键,那么我们在做查询的时候,我们就在想我们只需要丈夫信息不需要妻子信息的时候,就只查询丈夫的基本信息,而不用连妻子信息一起查询出来,当我们需要的时候,再查询丈夫的时候一起把妻子的信息一起查出来。

那么这里我们就需要用到懒加载,去解决这种一对一,多对多等这种关系。不过在这种关系中需要注意的是:不能循环调用彼此的方法,意思就是我在结果集中调用另外一个方法,那么这个方法的结果集不能反过来再调用我,说起来很抽象。

举个简单例子:假设丈夫有妻子外键,妻子有丈夫外键,查找丈夫信息时把妻子信息查出来,查妻子时把丈夫信息查出来(下面就进行了相互调用,是错误的方式

    <!-- 丈夫结果集映射 -->
	<resultMap type="HusbandBean" id="husMap">
		<id property="id" column="id" javaType="long"></id>
		<result property="husbandName" column="husband_name"
			javaType="string" />
		<!-- association 关联、关系 用于在映射复合对象属性 的值的时候,去调用select属性对应的方法查询结果 -->
		<!-- select 属性,代表在映射复合属性时,需要调用的查询方法,语法:namespace.meth名 -->
		<!-- column="fk_wife_id" 代表需要向select调用的方法中,传入的值 -->

		<!-- fetchType="lazy" 需要对wife对象进行懒加载(延迟加载) ,调用下面妻子查询方法-->
		<association property="wifeBean" javaType="WifeBean"
			fetchType="lazy" column="fk_wife_id" select="getWifeBeanById"></association>
	</resultMap>

	<!-- 查询丈夫信息方法 -->
	<select id="getHusbandBeanById" resultMap="husMap">
		select
		id,husband_name,fk_wife_id from t_husband where id = #{id}
	</select>

	<!-- 妻子结果集映射 -->
	<resultMap type="WifeBean" id="wifMap">
		<id property="id" column="id" javaType="long"></id>
		<result property="wifeName" column="wife_name"
			javaType="string" />
		
		<!--调用上面的getHusbandBeanById,这里不允许使用,因为丈夫查询过程中调用了妻子的查询方法,如果调用就形成了循环-->
		<association property="husbandBean" javaType="HusbandBean"
			fetchType="lazy" column="fk_husband_id" select="getHusbandBeanById"></association>
	</resultMap>

	<!-- 查询妻子信息方法 -->
	<select id="getWifeBeanById" resultMap="wifMap">
		select
		id,wife_name,fk_husband_id from t_wife where id = #{id}
	</select>

解决办法就是去掉妻子中的<association></association>标签避免循环,然后重新写以上的两个方法及映射结果集,妻子中含有association,丈夫结果集中去掉association,只不过这时就有四个方法四个结果映射集。这种双方包含对方外键的情况基本不会这样设计。

        <!-- 丈夫结果集映射 -->
	<resultMap type="HusbandBean" id="husMap">
		<id property="id" column="id" javaType="long"></id>
		<result property="husbandName" column="husband_name"
			javaType="string" />
		<!-- association 关联、关系 用于在映射复合对象属性 的值的时候,去调用select属性对应的方法查询结果 -->
		<!-- select 属性,代表在映射复合属性时,需要调用的查询方法,语法:namespace.meth名 -->
		<!-- column="fk_wife_id" 代表需要向select调用的方法中,传入的值 -->

		<!-- fetchType="lazy" 需要对wife对象进行懒加载(延迟加载) ,调用下面妻子查询方法 -->
		<association property="wifeBean" javaType="WifeBean"
			fetchType="lazy" column="fk_wife_id" select="getWifeBeanById"></association>
	</resultMap>

	<!-- 查询丈夫信息方法 -->
	<select id="getHusbandBeanById" resultMap="husMap">
		select
		id,husband_name,fk_wife_id from t_husband where id = #{id}
	</select>

	<!-- 妻子结果集映射 -->
	<resultMap type="WifeBean" id="wifMap">
		<id property="id" column="id" javaType="long"></id>
		<result property="wifeName" column="wife_name"
			javaType="string" />
	</resultMap>

	<!-- 查询妻子信息方法 -->
	<select id="getWifeBeanById" resultMap="wifMap">
		select
		id,wife_name,fk_husband_id from t_wife where id = #{id}
	</select>
        <!-- 丈夫结果集映射2 -->
	<resultMap type="HusbandBean" id="husMap2">
		<id property="id" column="id" javaType="long"></id>
		<result property="husbandName" column="husband_name"
			javaType="string" />
	</resultMap>

	<!-- 查询丈夫信息方法2 -->
	<select id="getHusbandBeanById2" resultMap="husMap2">
		select
		id,husband_name from t_husband where id = #{id}
	</select>
	
	<!-- 妻子结果集映射2 -->
	<resultMap type="WifeBean" id="wifMap2">
		<id property="id" column="id" javaType="long"></id>
		<result property="wifeName" column="wife_name"
			javaType="string" />

		<!--调用上面的getHusbandBeanById,可以调用,因为丈夫没有用association来调该方法 -->
		<association property="husbandBean" javaType="HusbandBean"
			fetchType="lazy" column="fk_husband_id" select="getHusbandBeanById2"></association>
	</resultMap>

	<!-- 查询妻子信息方法2 -->
	<select id="getWifeBeanById2" resultMap="wifMap2">
		select
		id,wife_name,fk_husband_id from t_wife where id = #{id}
	</select>

查询丈夫就用getHusbandBeanById,查询妻子就用getWifeBeanById2,这样就分离了,避免循环套用

 

1)关联关系(就是一个对象中含有另外一个对象)association

这里用妻子对象和丈夫对象(分别对应表妻子表,丈夫表,丈夫表中有妻子的外键)

HusbandBean:(对应表属性:id,hus_name,age,fk_wife_id)

private Integer id;
private String husbandName;
private Integer age;
private WifeBean wife;

WifeBean:(对应表属性:id,wife_name,age)

private Integer id;
private String wifeName;
private Integer age;

 

丈夫接口的方法:

HusbandBean getHusbandBeanById(@Param("id")int id);

丈夫xml映射对应的查询方法:

<select id="getHusbandBeanById" resultMap="husMap2">
	select id,hus_name,age,fk_wife_id from t_hus where id = #{id}
</select>

对于表中我们需要查询的列,需要和对象的属性进行映射,在xml的映射配置中的结果集映射中我们就该使用<association></association>标签去解决关联关系。

<resultMap type="HusbandBean" id="husMap2">
		<id property="id" column="id" javaType="int"></id>
		<result property="husbandName" column="hus_name" javaType="string"/>
		<result property="age" column="age" javaType="int"/>
		<!-- association 关联、关系  用于在映射复合对象属性 的值的时候,去调用select属性对应的方法查询结果 -->
		<!-- select 属性,代表在映射复合属性时,需要调用的查询方法,语法:namespace.meth名 -->
		<!-- column="fk_wife_id" 代表需要向select调用的方法中,传入的值 -->
		
		<!-- fetchType="lazy" 需要对wife对象进行懒加载(延迟加载)传下去的是妻子外键值,也就是妻子的id -->
		<association property="wife" javaType="WifeBean" fetchType="lazy" column="fk_wife_id" select="com.ali.mybatis01.o2omag.mapper.WifeMapper.getWifeBeanById"></association>	
	</resultMap>

<association></association>标签中select中的属性(调用关联对象的查询方法):比如这里,就是去调用了妻子中的查询方式,如果在同一篇配置文件中,可以直接写调用的方法名,也就是该方法的id,如果调用的是不同的配置中的方法,那么就要写全路径;fetchType就是加载类型,这里用懒加载才实现用时才去查询。

 

妻子接口方法:

WifeBean getWifeBeanById(@Param("id") int id);

妻子xml映射对应的查询方法:

妻子的通过id查询的方法:(返回用resultType的话,并且用对象去接收返回结果那么必须取别名,和类的属性一致,才能接受到数据)resultType接受数据可以是对象、Map、基本数据类型,引用数据类型。

<select id="getWifeBeanById" resultType="WifeBean">
		select id,wife_name as wifeName,age from t_wife where id = #{id};
</select>

 

2)集合关系(就是一个对象中含有其他对象的集合)collection

 

  • 一对多的关系

      比如老师和学生的关系,一个老师可以有多个学生,那么老师就含有学生的一个集合,那么情况与上述情况类似,那么这些学生中就会含有这个老师的外键,那么我们在查询老师信息的时候也要实现懒加载,当我们需要学生信息才查询,不需要学生信息就只去查询老师自身的信息。

 

这里就举例老师和学生(学生表中含有老师表的外键

TeacherBean:(表属性 id,teacher_name,age)

private Integer id;
private String teacherName;
private Integer age;
	
/**
 * 站在老师的角度上来说,一个老师对应多个学生
 */
private List<StudentBean> stus;

StudentBean:(表属性 id,student_name,age,fk_teacher_id)

private Integer id;
private String studentName;
private Integer age;
	
/**
 * 一个学生只对应一个老师
 */
private TeacherBean teacher;

 

老师接口方法:

TeacherBean getTeacherBeanById(@Param("id") int id);

在xml的映射配置中的结果集映射中我们就该使用<collection></collection>标签去解决。

<!-- 查询结果集中老师对象与表的映射关系 -->
	<resultMap type="TeacherBean" id="teacherMap">
		<id property="id" column="id" javaType="int"/>
		<result property="teacherName" column="teacher_name" javaType="string"/>
		<result property="age" column="age" javaType="int"/>
		
		<!-- 查1对1的时候,使用association ,查多的时候,使用collection,内部定义查询学生的方法,把老师的id传下去 -->
		<collection property="stus" javaType="java.util.List" fetchType="lazy" column="id" select="findStudentBeanByFkTeacherId"></collection>
	</resultMap>
	
	<!-- 查询老师信息的方法-->
	<select id="getTeacherBeanById" resultMap="teacherMap">
		select id,teacher_name,age from t_teacher where id = #{id}	
	</select>
	
	<!-- 内部定义查询学生的方法-->
	<select id="findStudentBeanByFkTeacherId" resultType="StudentBean">
		select id,student_name as studentName,age from t_student where fk_teacher_id = #{id}
	</select>

在上面的关联关系中已经说了,不管是关联association还是集合collection,它们在调用方法的时候,如果在同一篇文件中,select属性可直接写方法名(方法的id名),如果调用其他xml映射配置的方法,要写全路径名。

 

对于学生而言,在我这个例子里面,一个学生对应一个老师,那么,要想在查询学生信息时,对老师的信息,也实现懒加载,就用第一种association方式,细节不多讲了,上面已经写了,直接看代码。

学生接口方法:

StudentBean getStudentBeanById(@Param("id") int id);

学生xml的映射配置:

<!-- 学生结果集映射-->
	<resultMap type="StudentBean" id="studentMap">
		<id property="id" column="id" javaType="int"/>
		<result property="studentName" column="student_name" javaType="string"/>
		<result property="age" column="age" javaType="int"/>
		
		<!-- 调用老师信息查询方式-,传入老师外键(也就是对应老师的id)-->
		<association property="teacher" javaType="TeacherBean" fetchType="lazy" column="fk_teacher_id" select="getTeacherBeanById"></association>
	</resultMap>
	
	<!-- 学生信息查询方式-->
	<select id="getStudentBeanById" resultMap="studentMap">
		select id,student_name,age,fk_teacher_id from t_student where id = #{id}
	</select>
	
	<!-- 老师信息查询方式-->
	<select id="getTeacherBeanById" resultType="TeacherBean">
		select id,teacher_name as teacherName,age from t_teacher where id = #{id}	
	</select>

 

  • 多对多关系

      在上面的collecton介绍中,它用于了一对多的关系,同时他也用于多对多的关系,存在多对多关系的话,那么双方都不可能去写具体的外键了,那么就需要一个中间表来将双方的关系进行串联起来。

这里我就用玩家和游戏的例子来进行,一个玩家可以选择多个游戏,一个游戏可以对应多个玩家。那么要实现查询玩家时同时查询出游戏,查询游戏时查出玩家,看下面代码。

PlayerBean:(表属性:id,player_name)

private long id;
private String playerName;
private List<GameBean> games;

GameBean:(表属性:id,game_name)

private long id;
private String gameName;
private List<PlayerBean> palyers;

中间表t_player_game属性:id,fk_player_id,fk_game_id

 

玩家接口方法:

List<PlayerBean> findPlayerBeanListByObject(@Param("p") PlayerBean player);

玩家xml映射配置:

    <!--玩家的结果集映射-->
	<resultMap type="PlayerBean" id="playerMap">
		<id property="id" column="id" javaType="int"/>
		<result property="playerName" column="player_name" javaType="string"/>
		
		<!-- 调用方法,传入玩家的id,通过方法中的中间表去找对应的游戏id-->
		<collection property="games" javaType="java.util.List" fetchType="lazy" column="id" select="getGameBeanById"></collection>
	
	</resultMap>
	
	<!--通过中间表去查找关联的游戏,这里用到了sql的内联方式,fk_player_id = #{id}表示了内联条件,通过这个可以条件关联到相应的数据行,不会查百度-->
	<select id="getGameBeanById" resultType="GameBean">
		select g.id,g.game_name as gameName from t_game as g,t_player_game as pg 
		where g.id = pg.fk_game_id and fk_player_id = #{id}	
	</select>
	
	
	<!-- 玩家查询方法,模糊查询-->
	<select id="getPlayerBeanById" resultType="PlayerBean">
		select id,player_name as playerName from t_player where id = #{id};
	</select>
	
	<select id="findPlayerBeanListByObject" resultMap="playerMap">
		select id,player_name from t_player 
	
		<where>
			1=1
			<if test="p.playerName != null and p.playerName !=''">
				and player_name like concat(#{p.playerName},'%')
			</if>
		</where>
	</select>

同样的,查询游戏的时候也是这种方法,只需要修改一部分代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值