zoukankan      html  css  js  c++  java
  • Mybatis关联查询

    在数据库的增删改查操作中,用的最多的就是查询操作了,查询操作又可以分成一对一查询、一对多查询和多对多查询。一个人属于一个部门,查询人的时候要查出他的部门,这是一对一查询;一辆车有四个轱辘,查询车的时候要查出这四个轱辘,这是一对多查询;一个学生选了多门课,一门课也是被多个学生选的,学生与课程之间用一张关联表来联系,这是多对多查询。

    本文介绍 Mybatis 是如何处理这几种查询方式的,包括以下三个部分:

    1. Mybatis 一对一查询
    2. Mybatis 一对多查询
    3. Mybatis 多对多查询
    4. discriminator 鉴别器映射

    一对一查询

    从代码上来看一对一查询就是在查询 User 对象的时候,查询出他的部门 postion。在 Mybatis 中可以使用 association 来表示一对一的映射关系,并且有嵌套结果嵌套查询两种方式。

     
    public class TUser implements Serializable{
        ...
        private TPosition position;
    

    嵌套结果

    TUser 表有一个 resultMap 名为 BaseResultMap,配置了 TUser 对象与数据库中字段的映射。

     
    <resultMap id="BaseResultMap" type="TUser">
    		<id column="id" property="id" />
    		<result column="user_name" property="userName" />
    		<result column="real_name" property="realName" />
    		<result column="sex" property="sex" />
    		<result column="mobile" property="mobile" />
    		<result column="email" property="email" />
    		<result column="note" property="note" />
    </resultMap>
    

    我们现在要一对一查询 TUser 所在的部门 Postion,就新建一个 resultMap 叫做 userAndPosition1。它 extends 自 BaseResultMap,拥有其所有的变量。我们查询的是 TUser,同时一对一查询出 postion,所以 userAndPosition1 的类型是 TUser。

     
    <resultMap id="userAndPosition1" extends="BaseResultMap" type="TUser">
    		<association property="position" javaType="TPosition" columnPrefix="post_">
    			<id column="id" property="id"/>
    			<result column="name" property="postName"/>
    			<result column="note" property="note"/>
    		</association>
    </resultMap>
    

    这里 association帮助我们配置映射关系,它有这样几个常用属性:

    • property 表示实体类中的属性名,也就是我们要关联查询的 postion
    • javaType 表示这个属性对应的类型,postion 对应的类型是 TPostion
    • resultMap 可以使用现有的 resultMap 而不重新配置映射关系,这里进行手动配置
    • columnPrefix 是查询列的前缀,配置字段的时候就不需要加上前缀,只在查询语句中加入前缀即可

    关联表查询的时候加上前缀是很好的编程习惯,因为当关联的多张表中有字段重复的时候,前缀可以帮助我们区分这些字段。

     
    <select id="selectUserPosition1" resultMap="userAndPosition1">
    		select
    		    a.id, 
    		    user_name,
    			real_name,
    			sex,
    			mobile,
    			email,
    			a.note,
    			b.id  post_id,
    			b.post_name,
    			b.note post_note
    		from t_user a,
    			t_position b
    		where a.position_id = b.id
    </select>
    

    嵌套查询

    嵌套结果是一次关联查询多张变,一次性返回结果。嵌套查询则是先完成主查询,再将朱查询中列的结果作为嵌套查询的参数,通过子查询获得关联的对象。

     
    <resultMap id="userAndPosition2" extends="BaseResultMap" type="TUser">
    		<association property="position" fetchType="lazy"  column="position_id" 
    			select="com.enjoylearning.mybatis.mapper.
                  TPositionMapper.selectByPrimaryKey" />
    </resultMap>
    

    任然是使用 association 来表示关联关系:

    • property 表示实体类中的属性名,也就是我们要关联查询的 postion
    • column 是主查询中的列名,也是子查询的参数
    • fetchType 是数据加载的方式,可选值为 lazy 和 eager,分别为延迟加载和积极加载 ,这个配置会覆盖全局的 lazyLoadingEnabled 配置;
    • select 是查询语句的 id,指定 mapper 文件中的查询语句
     
    <select id="selectUserPosition2" resultMap="userAndPosition2">
    		select
    		a.id,
    		a.user_name,
    		a.real_name,
    		a.sex,
    		a.mobile,
    		a.position_id
    		from t_user a
    	</select>
    

    「N+1 查询问题」:嵌套查询中,主查询会返回 1 个结果列表,子查询根据这个列表中的关联字段进行 N 次查询,如果主查询获取的结果很多就会执行成百上千次的 SQL 语句,这就是 「N+1查询问题」。

    同时进行成百上千次的查询落在数据库上,会影响系统的性能,这是我们不希望见到的。解决办法是在 association 中使用fetchType=lazy开启懒加载,但是全局 setting 禁用懒加载<setting name="aggressiveLazyLoading" value="false"/>。懒加载即只有在使用到这个字段的时候才会执行 SQL,这样就可以减少 「N+1查询问题」。

    一对多查询

    一对一查询主要主要 association 标签实现,一对多查询则使用 collection 标签实现。

     
    public class TUser implements Serializable{
        ...
        // 一对一查询,一个人一个职位
        private TPosition position;
        // 一对多查询,一个人多次工作经历
        private List<TJobHistory> jobs ;
    

    嵌套结果

     
    <resultMap id="userAndJobs1" extends="BaseResultMap" type="TUser">
    		<collection property="jobs"
    			ofType="com.enjoylearning.mybatis.entity.TJobHistory" >
    			<result column="comp_name" property="compName" jdbcType="VARCHAR" />
    			<result column="years" property="years" jdbcType="INTEGER" />
    			<result column="title" property="title" jdbcType="VARCHAR" />
    		</collection>
    </resultMap>
    
    <select id="selectUserJobs1" resultMap="userAndJobs1">
    		select
    		a.id,
    		a.user_name,
    		a.real_name,
    		a.sex,
    		a.mobile,
    		b.comp_name,
    		b.years,
    		b.title
    		from t_user a,
    		t_job_history b
    		where a.id = b.user_id
    </select>
    

    嵌套查询

     
    <resultMap id="userAndJobs2" extends="BaseResultMap" type="TUser">
    		<collection property="jobs" fetchType="lazy" column="id"
    			select="com.enjoylearning.mybatis.mapper.TJobHistoryMapper.selectByUserId" />
    </resultMap>
    
    <select id="selectUserJobs2" resultMap="userAndJobs2">
    		select
    		a.id,
    		a.user_name,
    		a.real_name,
    		a.sex,
    		a.mobile
    		from t_user a
    </select>
    

    多对多查询

    多对多由两个一对多关系组成的,需要一种中间表建立连接关系。比如说 t_user 和 t_role 之间是通过 t_user_role 这张表来关联的。

     
    public class TUser implements Serializable {
        private List<TRole> roles;
    

    在一对多查询这一节中,t_user 与 t_job 通过用户的 id 可以直接关联起来。在多对多的场景中,用户和角色则需要通过中间表来关联,SQL 语句就变成了这样:

     
    <resultMap type="TUser" id="userRoleInfo" extends="BaseResultMap">
    		<collection property="roles" ofType="TRole" columnPrefix="role_">
    			<result column="id" property="id" />
    			<result column="Name" property="roleName" />
    			<result column="note" property="note" />
    		</collection>
    	</resultMap>
    	
    	
    	<select id="selectUserRole" resultMap="userRoleInfo">
    		select a.id, 
    		      a.user_name,
    		      a.real_name,
    		      a.sex,
    		      a.mobile,
    		      a.note,
    		      b.role_id,
    		      c.role_name,
    		      c.note role_note
    		from t_user a,
    		     t_user_role b,
    		     t_role c
    		where a.id = b.user_id AND 
    		      b.role_id = c.id
         </select>
    

    discriminator 鉴别器映射

    通过应用场景来理解:比如说有一个订单表,表中有一个字段用来标记订单的种类,1是机票订单、2是酒店订单。使用鉴别器映射可以在标记字段不同的情况下,分别执行不同的 SQL 语句,查询出不同的结果。

    在特定的情况下使用不同的pojo进行关联, 鉴别器元素就是被设计来处理这个情况的。鉴别器非常容易理解,因为它的表现很像 Java 语言中的 switch 语句;

    • discriminator 标签常用的两个属性如下:
      • column:该属性用于设置要进行鉴别比较值的列 。
      • javaType:该属性用于指定列的类型,保证使用相同的 Java 类型来比较值。
    • discriminator 标签可以有1个或多个 case 标签, case 标签包含以下三个属性 。
      • value : 该值为 discriminator 指定 column 用来匹配的值 。
      • resultMap : 当column的值和value的值匹配时,可以配置使用resultMap指定的映射,resultMap优先级高于 resultType 。
      • resultType : 当 column 的值和 value 的值匹配时,用于配置使用 resultType指定的映射。
     
    <resultMap id="userAndHealthReportMale" extends="userAndHealthReport" type="TUser">
    		<collection property="healthReports" column="id"
    			select= "com.enjoylearning.mybatis.mapper.THealthReportMaleMapper.selectByUserId"></collection>
    	</resultMap>
    	
    	<resultMap id="userAndHealthReportFemale" extends="userAndHealthReport" type="TUser">
    		<collection property="healthReports" column="id"
    			select= "com.enjoylearning.mybatis.mapper.THealthReportFemaleMapper.selectByUserId"></collection>
    	</resultMap>
    	
    	<resultMap id="userAndHealthReport" extends="BaseResultMap" type="TUser">
    				 
    		<discriminator column="sex"  javaType="int">
    			<case value="1" resultMap="userAndHealthReportMale"/>
    			<case value="2" resultMap="userAndHealthReportFemale"/>
    		</discriminator>
    	</resultMap>
    
    <select id="selectUserHealthReport" resultMap="userAndHealthReport">
    		select
    		<include refid="Base_Column_List" />
    		from t_user a
    </select>
    
  • 相关阅读:
    shell脚本
    vim使用快捷键
    logback.xml_appender配置
    vim配置文件
    sed
    使用linux服务器安装wordpress博客详细教程
    JDBC为什么要使用PreparedStatement而不是Statement
    Jsp技术介绍
    jsp的appilication.getInitParameter()方法无法获取到值的问题
    ubuntu 插网线无法上网解决方案
  • 原文地址:https://www.cnblogs.com/shuiyj/p/13185093.html
Copyright © 2011-2022 走看看