前言
大家应该发现了之前我们讲到的内容都是在单张表中做操作的,但是实际开发中,我们表与表之间的关系会非常复杂,不过理清表与表之间的关系,无非也就4中:一对一,一对多,多对一和多对多,那么这一节我们主要就来讲讲这四种关系下的表与表之间的关联查询啦~
关联表查询之一对一
那在这里我们先建两张表User表和Role表,现在的要求是根据我们已知的user的Id,查询该user对应的角色
<mapper namespace="com.marco.dao.RoleDao">
<resultMap type="Role" id="roleResultMap">
<id column="id" property="id"/>
<result column="role_name" property="roleName"/>
<result column="level" property="level"/>
<result column="is_del" property="isDel"/>
</resultMap>
<sql id="allColumnWithTName">
ur.id, ur.role_name, ur.level, ur.is_del
</sql>
<select id="searchUserRole" parameterType="Integer" resultMap="roleResultMap">
select <include refid="allColumnWithTName"/> from u_role ur, u_user uu, u_user_role_rel uurr where uurr.user_id = uu.id and uurr.role_id = ur.id
and uu.id = #{value}
</select>
</mapper>
那么我们通过字段roleId将两张表关联起来,此时一个用户对应的是一个角色,显然是一对一的关系,那根据以上sql,我们来测试一下效果
public class Test {
public static void main(String[] args) {
SqlSession session = MybatisUtil.getSession();
RoleDao roleDao = session.getMapper(RoleDao.class);
Role role = roleDao.searchUserRole(85);
System.out.println(role);
}
}
很显然,结果是没有问题的,关于这里的注意点之前我们也讲到过,就是resultMap的字段别名转换,当字段名和类的成员变量名不一致时,一定要记得转换,否则值是附不上去的
关联表查询之多对一
上面的内容比较简单,大家一学就会,接下来就比较有意思啦!
什么是一对多呢?就好比,一个年级有很多个班,一个班又对应了很多个学生,那么这种关系称之为一对多
那么现在我们多做一张表出来
那么这个用户表和我们客户表的关系就是一对多啦,简单来说就是一个用户(业务员)手上会有多个用户,那么我们如何把客户的信息包含业务员的名称也全部查出来呢?
实现方法一: 使用resultType处理
这种方式其实我们之前在项目中接触过,不知道大家还有没有印象,就是通过新建一个Value Object值对象,进行赋值操作,那在这里我们创建一个值对象,叫CustomerVO,除了Customer本身的属性,还包含了它对应的销售的信息
那在配置里我们要这么写
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.marco.dao.CustomerDao">
<sql id="CustColumn">
uc.cust_id as id, uc.comp_name as custName, uc.cust_name as custName,
uc.cust_job as custJob, uc.user_id as userId
</sql>
<sql id="UserColumn">
uu.user_name as userName, uu.real_name as realName
</sql>
<select id="selectAll" parameterType="Map" resultType="com.marco.vo.CustomerVO">
select <include refid="CustColumn"/>, <include refid="UserColumn"/>
from u_customer uc join u_user uu on uc.user_id = uu.id
</select>
</mapper>
接下来我们测试一下呗
public class Test {
public static void main(String[] args) {
SqlSession session = MybatisUtil.getSession();
CustomerDao custDao = session.getMapper(CustomerDao.class);
List<CustomerVO> customerVOs = custDao.selectAll();
for (CustomerVO customerVO : customerVOs) {
System.out.println(customerVO);
}
}
}
实现方法二: 使用resultMap的结果嵌套处理
和第一种实现方法有所区别,第二种方式更为粗暴!直接将User这个类聚合到Customer中,形成一个新的CustomerVO聚合类,那我们调整过后的类如下。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.marco.dao.CustomerDao">
<resultMap type="com.marco.vo.CustomerVO" id="custVOResultMap">
<id column="cust_id" property="id"/>
<result column="comp_name" property="compName"/>
<result column="cust_name" property="custName"/>
<result column="cust_job" property="custJob"/>
<result column="user_id" property="userId"/>
<association property="user" javaType="User">
<result column="user_name" property="userName"/>
<result column="real_name" property="realName"/>
</association>
</resultMap>
<sql id="CustColumn">
uc.cust_id, uc.comp_name , uc.cust_name , uc.cust_job , uc.user_id
</sql>
<sql id="UserColumn">
uu.user_name , uu.real_name
</sql>
<select id="selectAll" parameterType="Map" resultMap="custVOResultMap">
select <include refid="CustColumn"/>, <include refid="UserColumn"/>
from u_customer uc join u_user uu on uc.user_id = uu.id
</select>
</mapper>
我们好像发现了一个陌生的玩意儿,就是这个标签<association property="user" javaType="User">
我们知道客户对与业务员是多对一的关系(多个客户可能只有一个业务员在负责),那么association 中的属性可以这么解释
javaType: 多对一关系的一(业务员)中类的类型
property : 多对一关系的一(业务员)在多(客户)中的名称
<association >
中的子标签<result column="user_name" property="userName"/>
相信大家看到应该一目了然了,这些子标签也就代表着我们User的字段和成员变量属性一一映射的关系。
实现方法三: 使用resultMap的查询嵌套处理
这种方式在上面查询的方式的基础下做了一点点改动,我就直接上配置文件啦
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.marco.dao.CustomerDao">
<resultMap type="com.marco.vo.CustomerVO" id="custVOResultMap">
<id column="cust_id" property="id"/>
<result column="comp_name" property="compName"/>
<result column="cust_name" property="custName"/>
<result column="cust_job" property="custJob"/>
<result column="user_id" property="userId"/>
<association property="user" javaType="User" select="com.marco.dao.UserDao.queryById" column="user_id"/>
</resultMap>
<select id="selectAll" parameterType="Map" resultMap="custVOResultMap">
select cust_id, comp_name , cust_name , cust_job , user_id
from u_customer
</select>
</mapper>
这种方式不太好解释,我们先上个图来说明啦 ~
首先 我们通过select Id为selectAll的选择查询语句查询customer信息,返回customer的 cust_id, comp_name , cust_name , cust_job , user_id
然后 通过association并使用完全限定名调用UserDao中的queryById方法,通过column:user_id字段关联user表中id字段,并将该字段的值以形参传入到queryById方法中,
接着 再查询出user的信息,最后由resultMap: custVOResultMap整合查询到的所有数据,并将这些字段和CustomerVO中的类的属性一一映射,注入数据,返回结果。
输出结果如下
关联查询花样真的很多,大家择一就好啦
关联表查询之一对多
实现方法一: 使用resultMap的结果嵌套处理
刚才我们讲到的是多对一,那这个一对多相信大家应该也能够理解,其实两者的关系图都是一样的,只不过我们的视角从Customer这个角色,置换成User这个角色了。
我们来转变一下角色,把自己想象成Amazon的市场部专员,那好,这个时候我们会发现,自己手上有一份客户的清单,列出了我们手上所有的客户,这个表的最上方就是我在公司的信息,下方是我的客户信息,那么如何将这张表以程序的形式展现出来呢?
通过分析大家不难发现,我们User中包含了多个客户的信息,通过树状图展现出来,是不是像一个Collection?一个可以存放各种信息的容器,那我们来模拟一下,首先创建UserVO
这里我是用的是List集合将我的客户信息存放进去,那配置文件该这么写
<mapper namespace="com.marco.dao.UserDao">
<resultMap type="com.marco.vo.UserVO" id="userVOResultMap">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="real_name" property="realName"/>
<collection property="customers" javaType="List" ofType="com.marco.vo.CustomerVO">
<id column="cust_id" property="id"/>
<result column="comp_name" property="compName"/>
<result column="cust_name" property="custName"/>
<result column="cust_job" property="custJob"/>
<result column="user_id" property="userId"/>
</collection>
</resultMap>
<sql id="CustColumn">
uc.cust_id, uc.comp_name , uc.cust_name , uc.cust_job , uc.user_id
</sql>
<sql id="UserColumn">
uu.id, uu.user_name , uu.real_name
</sql>
<select id="queryAll" parameterType="map" resultMap="userVOResultMap">
select <include refid="UserColumn"/>,<include refid="CustColumn"/>
from u_user uu left join u_customer uc on uu.id = uc.user_id
</select>
</mapper>
这里的代码是不是跟上面的实现代码二的代码跟类似呢?只不过这里我们使用的不是associate标签,而是collection标签,collection顾名思义就是Collection集合的意思。
property: 依然是这个集合在当前这个类(UserVO)中的变量名,
javaType: 就是这个变量的类或者类型
ofType: 就是这个集合中的元素的类或者类型
其他的部分就都和associate一样,我就不一一讲解啦~
实现方法二: 使用resultMap的查询嵌套处理
这种方式刚刚上面有提到过咯,流程都是一样滴,只不过这一次为了直观一点,我就没有使用完全限定名调用查询方法的方式呈现给大家
<mapper namespace="com.marco.dao.UserDao">
<resultMap type="com.marco.vo.UserVO" id="userVOResultMap">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="real_name" property="realName"/>
<collection property="customers" javaType="java.util.List" ofType="com.marco.vo.CustomerVO" column="id" select="getCustsByUId">
</collection>
</resultMap>
<select id="queryAll" parameterType="map" resultMap="userVOResultMap">
select id, user_name, real_name from u_user
</select>
<select id="getCustsByUId" parameterType="int" resultType="com.marco.vo.CustomerVO">
select cust_id as id, comp_name as compName, cust_name as custName, cust_job as custJob from u_customer where user_id = #{value}
</select>
</mapper>
后语
其实我们上面的列子都比较简单,实际开发中的项目会难得多,其实大家在使用collection的时候会发现一个问题,就是它里面可以嵌套association,而association中又包含collection,
这意味这什么呢?就像镜面反射一样,两面镜子对着看里边,永远都看不到尽头,或许一些结构非常复杂,庞大的数据或许就像这两面镜子一样,一层套一层,这就又好比程序世界的路是永无止境的,我们要学习的东西真的还有很多很多,很喜欢喜剧之王中星爷大喊的一句话
“我养你啊!”
哈哈哈,开玩笑的,是 “努力!! 奋斗!!”
不说了我要再去看一遍星爷的电影了。