- 关于spring整合mybatis的步骤applicationContext.xml的配置:https://blog.csdn.net/IT_CREATE/article/details/85329007
- mybatis框架之mapper的xml映射配置的编写(insert、delete、update、select标签使用):https://blog.csdn.net/IT_CREATE/article/details/85687293
- mybatis中动态sql的操作(where、foreach、if、set、choose、trim标签的使用):https://blog.csdn.net/IT_CREATE/article/details/86557260
- mybatis中的级联关系中的鉴别器(discriminator、case、result):https://blog.csdn.net/IT_CREATE/article/details/86561051
-
当我们在做查询时,多个表之间可能存在着级联关系
比如丈夫表和妻子表,丈夫表里面含有妻子的外键,那么我们在做查询的时候,我们就在想我们只需要丈夫信息不需要妻子信息的时候,就只查询丈夫的基本信息,而不用连妻子信息一起查询出来,当我们需要的时候,再查询丈夫的时候一起把妻子的信息一起查出来。
那么这里我们就需要用到懒加载,去解决这种一对一,多对多等这种关系。不过在这种关系中需要注意的是:不能循环调用彼此的方法,意思就是我在结果集中调用另外一个方法,那么这个方法的结果集不能反过来再调用我,说起来很抽象。
举个简单例子:假设丈夫有妻子外键,妻子有丈夫外键,查找丈夫信息时把妻子信息查出来,查妻子时把丈夫信息查出来(下面就进行了相互调用,是错误的方式)
<!-- 丈夫结果集映射 -->
<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>
同样的,查询游戏的时候也是这种方法,只需要修改一部分代码。