Mybatis (三) Mybatis映射文件

Mybatis映射文件

基本CRUD

增加

<select id="saveRole" resultType="role">
  insert into tb_role
  (
  <trim suffixOverrides=",">
    <if test="roleName!=null">
      role_name,
    </if>
    <if test="description != null">
      description,
    </if>
    <if test="status != null">
      status,
    </if>
    <if test="createTime != null">
      create_time,
    </if>
    <if test="createUser != null">
      create_user,
    </if>
    <if test="modifyTime != null">
      modify_time,
    </if>
    <if test="modifyUser != null">
      modify_user
    </if>
  </trim>
  ) VALUES
  (
  <trim suffixOverrides=",">
    <if test="roleName!=null">
      #{roleName},
    </if>
    <if test="description != null">
      #{description},
    </if>
    <if test="status != null">
      #{status},
    </if>
    <if test="createTime != null">
      #{createTime},
    </if>
    <if test="createUser != null">
      #{createUser},
    </if>
    <if test="modifyTime != null">
      #{modifyTime},
    </if>
    <if test="modifyUser != null">
      #{modifyUser}
    </if>
  </trim>
  )
</select>

这个增加方法使用if 进行判断,所以只是将不为空的字段添加。

增加并返回ID

<!--设置保存的时候需要返回插入时的id-->
<insert id="saveUser" useGeneratedKeys="true" keyProperty="id" parameterType="User" >
    insert into tb_user (user_id,
    	username,password,email,phone,gender,birthday,create_time,
    	create_user,modify_time,modify_user)
    values (
    	#{user.userId},#{user.username},#{user.password},#{user.email},#{user.phone},#{user.gender},
    	#{user.birthday},#{user.createTime},#{user.createUser},#{user.modifyTime},#{user.modifyUser})
</insert>

删除

<delete id="deleteRoleById">
  delete from tb_role where id = #{id}
</delete>

这是一个非常简单的删除语句,这里不赘述。

更新

<update id="updateRole">
  update tb_role
  <set>
    <if test="roleName!=null">
      role_name = #{roleName},
    </if>
    <if test="description != null">
      description = #{description},
    </if>
    <if test="status != null">
      status = #{status},
    </if>
    <if test="createTime != null">
      create_time = #{createTime},
    </if>
    <if test="createUser != null">
      create_user = #{createUser},
    </if>
    <if test="modifyTime != null">
      modify_time = #{modifyTime},
    </if>
    <if test="modifyUser != null">
      modify_user = #{modifyUser}
    </if>
  </set>
  where id = #{id}
</update>

提示:这里采用了选择性更新,即只有字段不为空的时候,才会进行更新操作。

查询

<select id="selectRolesByStatus" resultType="role">
  select * from tb_role where status = #{status}
</select>

提示:这里仅仅介绍一下最简单的查询,后面重点介绍与查询相关的。

增删改查标签相关属性请参考:

https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#insert_update_and_delete

增删改查标签属性

Select 元素的属性

属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。
resultType 从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
resultMap 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
fetchSize 这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为未设置(unset)(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 中的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖驱动)。
databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。
resultSets 这个设置仅对多结果集的情况适用。它将列出语句执行后返回的结果集并给每个结果集一个名称,名称是逗号分隔的。

Insert, Update, Delete 元素的属性

属性 描述
id 命名空间中的唯一标识符,可被用来代表这条语句。
parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认值:未设置(unset)。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望使用多个生成的列,也可以设置为逗号分隔的属性名称列表。
databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。

结果映射

结果映射(resultMap)

  • constructor - 用于在实例化类时,注入结果到构造方法中idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂类型的关联;许多结果将包装成这种类型嵌套结果映射 – 关联本身可以是一个 resultMap 元素,或者从别处引用一个
  • collection – 一个复杂类型的集合嵌套结果映射 – 集合本身可以是一个 resultMap 元素,或者从别处引用一个
  • discriminator – 使用结果值来决定使用哪个 resultMapcase – 基于某些值的结果映射嵌套结果映射 – case 本身可以是一个 resultMap 元素,因此可以具有相同的结构和元素,或者从别处引用一个

Id 和 Result 的属性

属性 描述
property 映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。
column 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcType JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。
typeHandler 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。

支持的 JDBC 类型

为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。

BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED
TINYINT REAL VARCHAR BINARY BLOB NVARCHAR
SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR
INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB
BIGINT DECIMAL TIME NULL CURSOR ARRAY

单表基本结果映射

属性设值

<resultMap id="base_map" type="role">
  <id column="id" jdbcType="INTEGER" property="id" />
  <result column="role_name" jdbcType="VARCHAR" property="roleName"/>
  <result column="description" jdbcType="VARCHAR" property="description"/>
  <result column="status" jdbcType="INTEGER" property="status"/>
  <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
  <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
  <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
  <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
</resultMap>

<select id="selectAllRoles" resultMap="base_map">
  select * from tb_role
</select>

java实体:

//空参数,全参数,get,set,toString
public class Role implements Serializable {

    private Integer id;
    private String roleName;
    private String description;
    private Integer status;
    private Date createTime;
    private String createUser;
    private Date modifyTime;
    private String modifyUser;
}

提示:可以看出,这里我们不再直接使用了user作为返回类型(resultType),而是使用了结果映射(resultMap)来映射实体。以上方式使用的实体映射是使用的setter进行设置参数的。我们在setId方法上加一段输入语句之后,再次运行,结果如下:

public void setId(Integer id) {
  System.out.println("setId of role is running");
  this.id = id;
}

那么我们如何使用构造函数来进行属性设置赋值呢?

<resultMap id="base_map" type="role">
  <constructor>
    <idArg column="id" javaType="Integer"/>
    <arg column="role_name" javaType="String"/>
    <arg column="description" javaType="String"/>
    <arg column="status" javaType="Integer"/>
    <arg column="create_time" javaType="Date"/>
    <arg column="create_user" javaType="String"/>
    <arg column="modify_time" javaType="Date" />
    <arg column="modify_user" javaType="String" />
  </constructor>
</resultMap>

构造方法设值

public Role(Integer id, String roleName, String description, Integer status, Date createTime, String createUser, Date modifyTime, String modifyUser) {
  System.out.println("all args' constructor of role is running ");
  this.id = id;
  this.roleName = roleName;
  this.description = description;
  this.status = status;
  this.createTime = createTime;
  this.createUser = createUser;
  this.modifyTime = modifyTime;
  this.modifyUser = modifyUser;
}

测试结果:

关联关系一对一

​ 前面我们看到了单表操作,我们通过直接返回对象或是使用resultMap的方式来映射实体。这里我们看一下实体一对一的关系如何映射。

一对一关系的映射:association标签的属性元素

property CDATA  			属性,必须
column CDATA  				数据库表的字段名
javaType CDATA  			属性的java类型
jdbcType CDATA  			数据库表的字段的类型
select CDATA  				嵌套查询时使用。
resultMap CDATA  			关联外部的手动映射关系时使用
typeHandler CDATA  			类型处理器
notNullColumn CDATA  	
columnPrefix CDATA  		字段前缀,一对一中有涉及到
resultSet CDATA  		
foreignColumn CDATA  	
autoMapping (true|false)  	是否自定映射
fetchType (lazy|eager)  	是否懒加载

业务:用户与身份证是一对一的关系。

User类:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class User implements Serializable {

    private Integer id;
    private String userId;
    private String username;
    private String password;
    private String email;
    private String phone;
    private Integer gender;
    private Date birthday;
    private Integer status;
    private Date createTime;
    private String createUser;
    private Date modifyTime;
    private String modifyUser;
    private Card card;
}

提示:这里使用了lombok 简化实例开发,依赖如下:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>

Card类:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Card implements Serializable {

    private Integer id;
    private Integer uid;
    private String cardId;
    private String address;
    private Date createTime;
    private Date modifyTime;
}

一对一自动映射

​ 通过上面的实例类关系我们可以发现,card是User类中的一个属性,但是如何通过Mybatis的自动映射机制将card表中的数据之间填充到User属性中。我们可以在查询字段上使用别名来映射,如下,uid字段是user的card属性的属性,则可以通过card.uid 来映射到User类的card属性的uid字段。

<select id="findUserWithCardByUserId" resultType="user">
    SELECT
      a.id,a.user_id,a.username,a.password,a.email,a.phone,
      a.gender,a.birthday,a.status,a.create_time,a.create_user,
      a.modify_time,a.modify_user,
      b.id as "card.id" ,
      b.uid as "card.uid",
      b.card_id as "card.cardId",
      b.address as "card.address",
      b.create_time as "card.createTime" ,
      b.modify_time as "card.modifyTime"
    FROM
    	tb_user a
    INNER JOIN tb_card b on a.id = b.uid where a.id = #{id};
</select>

一对一手动映射

手动映射就是通过结果集的方式列进行映射,如下:

<resultMap id="BaseResultMapWithCard" type="user">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <result column="user_id" jdbcType="VARCHAR" property="userId"/>
    <result column="username" jdbcType="VARCHAR" property="username"/>
    <result column="password" jdbcType="VARCHAR" property="password"/>
    <result column="email" jdbcType="VARCHAR" property="email"/>
    <result column="phone" jdbcType="VARCHAR" property="phone"/>
    <result column="gender" jdbcType="INTEGER" property="gender"/>
    <result column="birthday" jdbcType="DATE" property="birthday"/>
    <result column="status" jdbcType="INTEGER" property="status"/>
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
    <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
    <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
    <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
    <association property="card" javaType="card">
        <id column="bid" jdbcType="INTEGER" property="id"/>
        <result column="uid" jdbcType="INTEGER" property="uid"/>
        <result column="card_id" jdbcType="VARCHAR" property="cardId"/>
        <result column="address" jdbcType="VARCHAR" property="address"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
    </association>
</resultMap>


<select id="findUserWithCardByUserId" resultMap="BaseResultMapWithCard">
        select
            a.id,a.user_id,a.username,a.password,a.email,a.phone,a.gender,
            a.birthday,a.status,a.create_time,a.create_user,a.modify_time,a.modify_user,
            b.id bid,
            b.uid,
            b.card_id,
            b.address,
            b.create_time,
            b.modify_time
        FROM
            tb_user a
        INNER JOIN tb_card b on a.id = b.uid where a.id = #{id}
</select>

通过上述的SQL查询和结果集的手动映射关系,这样就可以实现字段和实体的查询了。

当然,Mybatis还提供下面这种方式来实现:

<resultMap id="BaseResultMapWithCard" type="user">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <result column="user_id" jdbcType="VARCHAR" property="userId"/>
    <result column="username" jdbcType="VARCHAR" property="username"/>
    <result column="password" jdbcType="VARCHAR" property="password"/>
    <result column="email" jdbcType="VARCHAR" property="email"/>
    <result column="phone" jdbcType="VARCHAR" property="phone"/>
    <result column="gender" jdbcType="INTEGER" property="gender"/>
    <result column="birthday" jdbcType="DATE" property="birthday"/>
    <result column="status" jdbcType="INTEGER" property="status"/>
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
    <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
    <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
    <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
    <association property="card" javaType="card" column="id" select="selectCard"></association>
</resultMap>

<select id="findUserWithCardByUserId" resultMap="BaseResultMapWithCard">
    select
      a.id,a.user_id,a.username,a.password,a.email,a.phone,a.gender,
      a.birthday,a.status,a.create_time,a.create_user,a.modify_time,a.modify_user
    FROM
    	tb_user a
    where a.id = #{id}
</select>

<select id="selectCard" resultType="card">
  	select * from tb_card where uid = #{id}
</select>

这样的操作其实就是先查询出User的信息,然后在取user表的id字段来查询card表的数据,这样就相当于向MySQL发送了两条SQL,我们通过日志可以观察到现象:

​ 两种方式都可以实现同样地结果,如果需要控制SQL发送条数的话,可以使用关联方式,如果需要在一定程度上降低SQL的查询时间,则可以将其拆分为多个查询SQL。

拓展:

下面我们看一下下面这个SQL如何进行映射,我们假设,一个User有一个主身份证和一个副身份证,那么我们需要如何查询映射:

User:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class User implements Serializable {

    private Integer id;
    private String userId;
    private String username;
    private String password;
    private String email;
    private String phone;
    private Integer gender;
    private Date birthday;
    private Integer status;
    private Date createTime;
    private String createUser;
    private Date modifyTime;
    private String modifyUser;

    private Card card;//主
    private Card card1;//副
}

对应的SQL映射如下:

<resultMap id="BaseResultMapWithCard" type="user">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <result column="user_id" jdbcType="VARCHAR" property="userId"/>
    <result column="username" jdbcType="VARCHAR" property="username"/>
    <result column="password" jdbcType="VARCHAR" property="password"/>
    <result column="email" jdbcType="VARCHAR" property="email"/>
    <result column="phone" jdbcType="VARCHAR" property="phone"/>
    <result column="gender" jdbcType="INTEGER" property="gender"/>
    <result column="birthday" jdbcType="DATE" property="birthday"/>
    <result column="status" jdbcType="INTEGER" property="status"/>
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
    <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
    <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
    <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
    <association property="card" javaType="card" resultMap="cardResultMap" columnPrefix="b1_" />
    <association property="card1" javaType="card" resultMap="cardResultMap" columnPrefix="b2_" />
</resultMap>

<resultMap id="cardResultMap" type="card">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <result column="uid" jdbcType="INTEGER" property="uid"/>
    <result column="card_id" jdbcType="VARCHAR" property="cardId"/>
    <result column="address" jdbcType="VARCHAR" property="address"/>
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
    <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
</resultMap>


<select id="findUserWithCardByUserId" resultMap="BaseResultMapWithCard" >
    select
      a.id,a.user_id,a.username,a.password,a.email,a.phone,a.gender,
      a.birthday,a.status,a.create_time,a.create_user,a.modify_time,a.modify_user,
      b1.id b1_id,
      b1.uid b1_uid,
      b1.card_id b1_card_id,
      b1.address b1_address,
      b1.create_time b1_create_time,
      b1.modify_time b1_modify_time,
      b2.id b2_id,
      b2.uid b2_uid,
      b2.card_id b2_card_id,
      b2.address b2_address,
      b2.create_time b2_create_time,
      b2.modify_time b2_modify_time
    from tb_user a
    inner join tb_card b1 on a.id = b1.uid
    inner join tb_card b2 on a.id = b2.uid
    where a.id = 1
</select>

​ 当连接多个表时,我们可能会不得不使用列别名来避免在 ResultSet 中产生重复的列名。指定 association标签的columnPrefix 列名前缀允许你将带有这些前缀的列映射到一个外部的结果映射中。

关联关系一对多

// 下面是collection标签的各个属性元素

property CDATA #REQUIRED 	必须,指定的是java实体类的属性名
column CDATA 				指定的是表的字段名(查询出来的,有可能是字段别名)
javaType CDATA  			集合一般不使用这个
ofType CDATA 				属性的java类型,例如:string,integer
jdbcType CDATA  			数据库的字段类型
select CDATA 				当我们需要进行嵌套查询的时候,执行另外一个查询语句
resultMap CDATA 			返回值的映射关
typeHandler CDATA 			类型处理
notNullColumn CDATA 		
columnPrefix CDATA 			当我们需要进行区分的时候,可以指定前缀,后面有案例
resultSet CDATA 			
foreignColumn CDATA			外键
autoMapping (true|false) 	是否需要自动映射
fetchType (lazy|eager) 		是否进行懒加载

​ 在实际开发中,权限往往是无关业务,但是又是至关重要的一环。在设计权限的时候,我们往往不会讲权限之间与用户之前关联,为了更加好的管理,我们会在用户与权限之间引入一个角色,用角色来统一管理具有相同权限的用户,一般一个用户存在于多个角色,比如即使CEO又是系统开发人员(哈哈),这里就是一个一对多的关系,下面我们看看,在Mybatis中一对多的关系如何映射:

User:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class User implements Serializable {

    private Integer id;
    private String userId;
    private String username;
    private String password;
    private String email;
    private String phone;
    private Integer gender;
    private Date birthday;
    private Integer status;
    private Date createTime;
    private String createUser;
    private Date modifyTime;
    private String modifyUser;
    private List<Role> roles;
}

Role:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Role implements Serializable {

    private Integer id;
    private String roleName;
    private String description;
    private Integer status;
    private Date createTime;
    private String createUser;
    private Date modifyTime;
    private String modifyUser;
}

根据id查询的SQLxml文件:

这里与前面的一对多的关联关系有所不同 ,一对多关联关系使用的是association,java类型使用的是javaType。而多对多关联关系使用的标签是collection,java类型属性使用的是ofType。

<resultMap id="BaseResultWithRole" type="com.ooyhao.mybatis.bean.User">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <result column="user_id" jdbcType="VARCHAR" property="userId"/>
    <result column="username" jdbcType="VARCHAR" property="username"/>
    <result column="password" jdbcType="VARCHAR" property="password"/>
    <result column="email" jdbcType="VARCHAR" property="email"/>
    <result column="phone" jdbcType="VARCHAR" property="phone"/>
    <result column="gender" jdbcType="INTEGER" property="gender"/>
    <result column="birthday" jdbcType="DATE" property="birthday"/>
    <result column="status" jdbcType="INTEGER" property="status"/>
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
    <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
    <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
    <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
    <collection property="roles" ofType="role">
        <id column="cid" jdbcType="INTEGER" property="id"/>
        <result column="role_name" jdbcType="VARCHAR" property="roleName"/>
        <result column="description" jdbcType="VARCHAR" property="description"/>
        <result column="status" jdbcType="INTEGER" property="status"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
        <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
        <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
    </collection>
</resultMap>


<select id="findUserWithRolesByUserId" resultMap="BaseResultWithRole">
    select
      a.id,a.user_id,a.username,a.password,a.email,a.phone,a.gender,
      a.birthday,a.status,a.create_time,a.create_user,a.modify_time,a.create_user,
      c.id cid,
      c.role_name,
      c.description,
      c.status,
      c.create_time,
      c.create_user,
      c.modify_time,
      c.create_user
    from tb_user a
    left join tb_user_role b on a.id = b.user_id
    left join tb_role c on c.id = b.role_id
    where a.id = #{id}
</select>

测试结果如下:

{
	"birthday": 1551369600000,
	"createTime": 1551433926000,
	"createUser": "admin",
	"email": "12345678@qq.com",
	"gender": 1,
	"id": 1,
	"modifyTime": 1551679675000,
	"password": "admin123456789",
	"phone": "12345678901",
	"roles": [{
		"createTime": 1551433926000,
		"createUser": "admin",
		"description": "超级管理员",
		"id": 1,
		"modifyTime": 1551679675000,
		"roleName": "admin",
		"status": 0
	}, {
		"createTime": 1551433926000,
		"createUser": "admin",
		"description": "开发人员",
		"id": 2,
		"modifyTime": 1551679675000,
		"roleName": "develop",
		"status": 0
	}],
	"status": 0,
	"userId": "oms20190001",
	"username": "admin"
}

除了上述方式进行关联查询,我们还可以将SQL进行拆分:

关联嵌套查询:

<resultMap id="BaseResultWithRole" type="com.ooyhao.mybatis.bean.User">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <result column="user_id" jdbcType="VARCHAR" property="userId"/>
    <result column="username" jdbcType="VARCHAR" property="username"/>
    <result column="password" jdbcType="VARCHAR" property="password"/>
    <result column="email" jdbcType="VARCHAR" property="email"/>
    <result column="phone" jdbcType="VARCHAR" property="phone"/>
    <result column="gender" jdbcType="INTEGER" property="gender"/>
    <result column="birthday" jdbcType="DATE" property="birthday"/>
    <result column="status" jdbcType="INTEGER" property="status"/>
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
    <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
    <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
    <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
    <collection property="roles" ofType="role"  column="id"  select="selectRole" />
</resultMap>

<resultMap id="selectRole" type="role">
    <id column="cid" jdbcType="INTEGER" property="id"/>
    <result column="role_name" jdbcType="VARCHAR" property="roleName"/>
    <result column="description" jdbcType="VARCHAR" property="description"/>
    <result column="status" jdbcType="INTEGER" property="status"/>
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
    <result column="create_user" jdbcType="VARCHAR" property="createUser"/>
    <result column="modify_time" jdbcType="TIMESTAMP" property="modifyTime"/>
    <result column="modify_user" jdbcType="VARCHAR" property="modifyUser"/>
</resultMap>


<select id="findUserWithRolesByUserId" resultMap="BaseResultWithRole">
    select
      a.id,a.user_id,a.username,a.password,a.email,a.phone,a.gender,
      a.birthday,a.status,a.create_time,a.create_user,a.modify_time,a.create_user
    from tb_user a
    where a.id = #{id}
</select>


<select id="selectRole" resultType="role" >
    select b.* from tb_user_role a
    left join tb_role b on a.role_id = b.id
    where a.user_id = #{id}
</select>

注:多对多其实就是双向的一对多关系,不再赘述。

鉴别器

摘自官网:

有时候,一个数据库查询可能会返回多个不同的结果集(但总体上还是有一定的联系的)。 鉴别器(discriminator)元素就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。 鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。

一个鉴别器的定义需要指定 column 和 javaType 属性。column 指定了 MyBatis 查询被比较值的地方。 而 javaType 用来确保使用正确的相等测试(虽然很多情况下字符串的相等测试都可以工作)。例如:

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
    <case value="3" resultMap="vanResult"/>
    <case value="4" resultMap="suvResult"/>
  </discriminator>
</resultMap>
---------------------------------------也可以这样-----------------------------------------

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultType="carResult">
      <result property="doorCount" column="door_count" />
    </case>
    <case value="2" resultType="truckResult">
      <result property="boxSize" column="box_size" />
      <result property="extendedCab" column="extended_cab" />
    </case>
    <case value="3" resultType="vanResult">
      <result property="powerSlidingDoor" column="power_sliding_door" />
    </case>
    <case value="4" resultType="suvResult">
      <result property="allWheelDrive" column="all_wheel_drive" />
    </case>
  </discriminator>
</resultMap>

提示:请注意,这些都是结果映射,如果你完全不设置任何的 result 元素,MyBatis 将为你自动匹配列和属性。所以上面的例子大多都要比实际的更复杂。 这也表明,大多数数据库的复杂度都比较高,我们不太可能一直依赖于这种机制。

下面通过一个案例来使用一下鉴别器:

数据如下:

我们通过接口的方式来使用鉴别器创建不同的交通工具实体:

public interface Vehicle {}

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Bus implements Vehicle {
    private Integer id;
    private String name;
}

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Car implements Vehicle{

    private Integer id;
    private String name;
}

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Subway implements Vehicle {
	
  	private Integer id;
    private String name;
}

Sql.xml文件:

<resultMap id="BaseResultMap" type="com.ooyhao.mybatis.bean.Vehicle">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <discriminator javaType="INTEGER" jdbcType="INTEGER" column="vehicle_type" >
        <case value="1" resultType="car">
         	 <result property="name" column="vehicle_name" jdbcType="VARCHAR" />
        </case>
        <case value="2" resultType="bus">
         	 <result property="name" column="vehicle_name" jdbcType="VARCHAR"/>
        </case>
        <case value="3" resultType="subway">
        	  <result property="name" column="vehicle_name" jdbcType="VARCHAR"/>
        </case>
    </discriminator>
</resultMap>

<select id="findVehicleById" resultMap="BaseResultMap">
  	select * from tb_vehicle where id = #{id}
</select>

在case以外的,就相当于在每一个case中都会存在,而在case中的则是根据条件来选择的。

id为1时:

id为3时:

源码地址:

https://gitee.com/ooyhao/JavaRepo_Public/tree/master/Mybatis

最后

如果觉得不错的话,那就关注一下小编哦!一起交流,一起学习

程序yuan
原文地址:https://www.cnblogs.com/ooyhao/p/11562060.html