SSM框架学习-MyBatis篇 SQL映射文件——实现高级结果映射(多表查询)
问题情境:比如有张表,用户和用户详情,这两张表里面没长表都有一个id字段,这两个表的id字段都是对应的。
实体类里面分别对应两个实体类,user和userdetail,还有一个很重要的实体类,叫做“用户+用户详情”(userwithdetail),这个实体类首先继承了user,里面的元素是前面的userdetail这个对象。
比如我想要查询一个用户,我还想知道他的详情,我就要在数据库里面select它一下,要返回userwithdetail 这个对象,因为这这个对象既继承了父类User,还包括了对应的对象userDetail。那么这么复杂的结构,肯定会使用到多表查询。
mybatis对于处理简单的单表查询,用resultType就可以解决 但是对于多表查询,都要使用resultMap进行详细的描述 告诉mybatis怎么封装。
自顶向下的先实践一下多表查询:
1、数据库定义user和userdetail的两张表。这个就不多赘述。
对应的实体类,前面已经说过。其中user和userdetail都实现serializable,UserWithDetail 继承了User
2、mybatis的配置文件先声明一下两个要用到的Mapper文件,这个也不多赘述
3、首先先写一个方法,就按照id查询一个用户吧。这个写法应该没毛病。给User和UserDetail两张表
起一个别名。然后两表联合查询。
<select id="queryById" resultMap="userWithDetailMap">
select t1.id as uid ,t1.phone,t1.password,t1.create_date,t1.status,
t2.id as udid,t2.address,t2.cid
from user t1,user_detail t2
<where>
t1.id=u_id
and t1.id =#{id}
</where>
</select>
4、由于从user t1,user_detail t2这两张表里面查询数据,单纯的语句已经不知道返回的数据的封装规则是什么了。resultType就不能解决问题了,那我们就定义一个resultMap
<resultMap id="userBaseMap" type="com.sdbit.ylh.entity.UserWithDetail">
<!--user的基本信息-->
<id property="id" column="uid"/>
<result property="phone" column="phone"/>
<result property="password" column="password"/>
<result property="createDate" column="create_date"/>
<result property="status" column="status"/>
</resultMap>
这里先定义一个基本的user信息而不是userdetail,原因是这个resultmap竟然还可以继承,为了以后写方便,就先给它写一个basemap,对表中的数据进行映射,先说说里面的属性
- property:映射数据库列(column)的实体与对象(JavaBean)的对应元素的属性。这里是User类的元素。
- column:数据库列名
- id:id算是一个比较特殊的property,用来映射数据库主键列与对象对应的元素,这里是User的uid元素
那user的对应属性如此,可是最后结果还有一个userdetail对象需要我们去封装,这个应该怎么解决呢?
那么就需要用到上面说的resultMap的继承。
5、这里在写一个resultMap,id的值为userWithDetailMap
<resultMap id="userWithDetailMap" extends="userBaseMap" type="com.sdbit.ylh.entity.UserWithDetail">
<association property="userDetail" javaType="com.sdbit.ylh.entity.UserDetail">
<id property="id" column="udid"/>
<result property="address" column="address"></result>
<result property="cid" column="cid"></result>
</association>
</resultMap>
这里就用到了association这个属性,这个属性就专门用来处理一对一的情况。这里持有的是userDetail这一个对象,然后我们需要告诉mybatis它类型是对应实体类中的 com.sdbit.ylh.entity.UserWithDetail这个类,
这个类对应的表的属性,和他们对应的类里面的元素。我们在里面继续声明即可,和上面一样id和property
完事之后在看看上面指定的resultmap,就一目了然了。
程序成功运行,查询出一条数据user+userdetail
还有一种封装resultMap更简单的方式,但是不是官方推荐的那种,也可以使用(这里没有使用继承!):
实践一下分步查询:
分步查询(如果查询太复杂,可以使用分步查询)
意思就是,当查询很复杂的时候,我们可以先写一个sql语句进行单表查询,然后再在resultMap里面再次执行一次别的sql语句
这个sql语句可以是别的mapper里面的语句。
具体怎么实现呢?下面就是实践演示。
1、首先定义一个“别的”mapper,这里叫做UserDetailMapper,顾名思义,就是专门对UserDetail的这个表进行查询。
因为最后我们想要得到的结果是User+UserDetail=UserWithDetail。
2、在sql的xml文件里写一个简单的select,把UserDetail表里面对应id的记录查找出来。
3、回到UserMapper.xml,我们首先封装一个resultMap,
<resultMap id="userWithDetailMap3" extends="userBaseMap" type="com.sdbit.ylh.entity.UserWithDetail">
<association property="userDetail"
select="com.sdbit.ylh.mapper.UserDetailMapper.queryByUserId"
column="uid">
</association>
</resultMap>
在这个语句中可以看到,association里面的property指向的就是userwithDetail里面的userDetail,那么这个userDetail对象从哪里来呢?就从select引用com.sdbit.ylh.mapper.UserDetailMapper下面的queryByUserId方法里来,但是上面这个uid是从哪里来呢??
那么我们在sql语句书写的时候,就要包含resultMap需要的列,我们现在就来写分步查询的最后一个sql
<select id="queryByIdByStep" resultMap="userWithDetailMap3">
select t1.id as uid ,t1.phone,t1.password,t1.create_date,t1.status
from user t1
<where>
and t1.id =#{id}
</where>
</select>
那么这里的uid就封装好了给resultMap。resultmap就可以执行第二步查询。完成了两次查询后UserWithDetail这个对象才完美的被数据填满。
如上就完成了使用association处理一对一的关联关系。
1对1的关联关系总结:
- 学会了使用association来实现多表查询,对实体类和数据库这两方的映射
- 处理多表查询中出现bean包含bean的两种处理方式,官方的和简单的
- resultMap的继承的写法。
- 使用定义resultMap来实现分步查询,使用association,甚至可以跨mapper调用方法查询