环境:springboot2.3.9.RELEASE + MyBatis + MySQL
依赖及相关配置参见:《Springboot整合MyBatis参数传值方式 》
建立复杂的关联关系
一个用户一个社保卡;一个用户多个角色;
实体类
@Entity
@Table(name = "BC_USERS")
public class Users extends BaseEntity {
private String username ;
private String password ;
private String realName ;
private String idNo ;
private String creator;
@Transient
private SinCard sinCard ;
@Transient
private List<Role> roles = new ArrayList<>() ;
}
因为这里使用jpa帮助生成表,所以这里的SinCard和List<Role>都加了@Transient注解,不然报错;(这里在表上没有建立物理的外键关系)。
@Entity
@Table(name = "BC_SIN_CARD")
public class SinCard extends BaseEntity {
private String cardNo ;
private BigDecimal money = BigDecimal.ZERO ;
private String userId ;
}
@Entity
@Table(name = "BC_ROLES")
public class Role extends BaseEntity {
private String roleName ;
private String roleDesc ;
private String userId ;
}
Mapper接口及XML
@Mapper
public interface UsersMapper {
List<Users> queryUsersAndSinCard() ;
List<Users> queryUsersAndSinCard2() ;
}
方式1:
<resultMap type="com.pack.domain.Users" id="AssignMapper">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="real_name" property="realName"/>
<result column="id_no" property="idNo"/>
<association property="sinCard" javaType="com.pack.domain.SinCard">
<id column="sid" property="id"/>
<result column="card_no" property="cardNo"/>
<result column="money" property="money"/>
</association>
<collection property="roles" javaType="com.pack.domain.Role">
<id column="rid" property="id"/>
<result column="role_name" property="roleName"/>
<result column="role_desc" property="roleDesc"/>
</collection>
</resultMap>
<select id="queryUsersAndSinCard" resultMap="AssignMapper">
SELECT u.*, s.id as sid, s.card_no, s.money,r.id as rid, r.role_name,r.role_desc FROM bc_users u, bc_sin_card s, bc_roles r where u.id=s.user_id and u.id=r.user_id
</select>
测试:
一条sql查询所有关联的对象。
方式2:
通过嵌套查询;通过另外一个Mapper中的sql进行查询关联对象。
<resultMap type="com.pack.domain.Users" id="AssignMapper">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="real_name" property="realName"/>
<result column="id_no" property="idNo"/>
<association property="sinCard" column="id" select="com.pack.mapper.SinCardMapper.queryByUserId"/>
<collection property="roles" javaType="com.pack.domain.Role">
<id column="rid" property="id"/>
<result column="role_name" property="roleName"/>
<result column="role_desc" property="roleDesc"/>
</collection>
</resultMap>
<select id="queryUsersAndSinCard2" resultMap="AssignMapper">
SELECT u.*,r.id as rid, r.role_name,r.role_desc FROM bc_users u,bc_roles r where u.id=r.user_id
</select>
SinCardMapper
<mapper namespace="com.pack.mapper.SinCardMapper">
<resultMap type="com.pack.domain.SinCard" id="sinCardMap">
<id column="id" property="id"/>
<result column="card_no" property="cardNo"/>
<result column="money" property="money"/>
<result column="create_time" property="createTime"/>
</resultMap>
<select id="queryByUserId" resultMap="sinCardMap">
SELECT * FROM bc_sin_card t WHERE t.user_id=#{userId}
</select>
</mapper>
@Mapper
public interface SinCardMapper {
SinCard queryByUserId(String userId) ;
}
测试:
延迟加载
MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载、侵入式延迟加载与深度延迟加载。
- 直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。
- 侵入式延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。
- 深度延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。
开启延迟加载功能。mybatis的延迟加载只对resultMap中的association和collection有作用
mybatis:
configuration:
lazy-loading-enabled: true
aggressive-lazy-loading: false
lazy-loading-enabled:是全局开关。当然我们也可以直接作用在association和collection上也是可以的。
aggressive-lazy-loading:这个属性如果你设置为false,这个延迟加载就不生效了。
Mapper及XML还是上面的queryUsersAndSinCard2方法。
测试:
并没有查询
com.pack.mapper.SinCardMapper.queryByUserId接口。
当把lazy-loading-enabled设置为false时,我们也可以将resultMap中的association和collection属性fetchType="lazy"也是可以的。
当访问主对象时,只是输出该对象,那么关联查询也会查询,同时返回的对象是代理对象
@Test
public void testQuery() {
System.out.println(usersMapper.queryUsersAndSinCard2().get(0)) ;
}
这里并没有访问延迟加载的对象,只是输出对象,也会查询关联对象。
完毕!!!