多表查询准备工作,新建了三个表。之前使用的user表,新建的role表,account表,user_role表。表中具体参数如下:
user表(主要是表头,内容自行填充):
role表:
account表:
user_role表:
这些类创建好之后,记得在domain中创建其实体类,并重写set,get,toString方法,然后在dao接口中写这三个类的接口,其余步骤前面已经说过,这里不再赘述。展示一下目录结构,有的文件我已经建好了(AccountUser,User2),后面才要用,到时候用的时候再解释
表之间的关系有多种,分别有一对一,一对多,多对一,多对多。
例如:
- 人和身份证号就是一对一,一个人只能有一个身份证号,一个身份证号只能属于一个人
- 用户和订单就是一对多,订单和用户就是多对一。一个用户可以下多个订单,多个订单属于同一个用户。
- 老师和学生之间就是多对多,一个学生可以被多个老师教过,一个老师可以交多个学生
简单起见,只实现一对一,一对多(多对一和一对多原理一样,学习一个即可)和多对多的功能。
1.一对一(这里介绍的方法是配置文件的方法进行一对一,继承类一对一虽然简单,但是不常用,故跳过不提)
主要实现的SQL语句:
select a.*,u.* from account a,user u where a.uid=u.id;
首先在IAccountDao中提供方法
List <Account> findAccountAndUser();
查出来很简单,这里把查出来的结果封装起来。我们将结果封装到Account类中,因此在Account中需要加入User属性(private User user),就是多表实体类中维护的单表实体类对象。更重要的是,需要在IAccountDao.xml中添加配置文件,在mapper标签中配置,主要配置文件如下:
<mapper namespace="dao.IAccountDao">
<resultMap id="accountMap" type="account">
<!-- 配置主键-->
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<association property="user" column="uid" javaType="user">
<!-- 配置主键-->
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</association>
</resultMap>
这个我们之前配置表中名和属性名时的配置一样,不同的是,这次我们在后面加了一个association标签,这个标签主要和user产生关联,下面解释一下association中这几个属性的含义:
- property=“user” 实体类中维护的单表实体类对象
- column=“uid” 多表通过这个uid字段跟单表产生关联
- javaType=“user” 通过账户查询出信息,封装到User类中, 前面配置过别名,所有直接写user
配置完成之后,直接在下面写sql语句。需要注意的是,需要引用上面的“accountMap”,否则就白配置了
<select id="findAccountAndUser" resultMap="accountMap">
select a.*,u.* from account a,user u where a.uid=u.id;
</select>
到这里,主要工作就做完啦,最后写个测试文件测试一下就OK了。呐,这是我的测试文件
@Test
public void testFindAccountAndUser(){
List<Account> user = dao.findAccountAndUser();
for (Account account : user) {
System.out.println(account);
}
}
最终结果是正确的,这里就不进行演示了。
2.一对多
和一对一不同的是,我们在进行一对多的结果封装时,需要在单表实体类中提供一个集合,来描述这个一对多的关系,集合里面装的就是多个用户。
先看一下主要查询语句:
select u.*,a.id as aid,a.uid,a.money from user u left join account a on a.uid=u.id;
在此语句中,就是user用户要展示完,对应的账户表有则展示,没有展示null即可。当然了,这就是要把结果封装到user实体类中了,不过这次我们在user中添加的属性,是一个集合,如下:
private List<Account> accounts;
记得补全get,set,toString方法哦
下来就是在单表实体类IUserDao.xml映射文件中进行配置,和之前一样,我们也在resultMap标签中进行配置,先上代码
<resultMap id="userMap" type="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<collection property="accounts" ofType="account">
<id property="id" column="aid"/>
<!-- 在查询中,我们用a.id as aid,所以上面column中写aid-->
<result property="uid" column="uid"/>
<result property="money" column="money"/>
</collection>
</resultMap>
不同的是,这次我们在里面使用的是collection标签,下面解释一下collection中两个属性名的含义:
- property=“accounts” 主表实体类中维护的那个集合
- ofType=“account” 集合中封装的类型
然后,我们在下面接着写SQL语句就好了,如下:
<select id="findAccountAndUser" resultMap="userMap">
select u.*,a.id as aid,a.uid,a.money from user u left join account a on a.uid=u.id;
</select>
记得,一定要写resultMap。最后写一个测试文件测试一下就好了。测试代码如下:
// 一对多测试
@Test
public void testFindAccountAndUser(){
List<User> list = dao.findAccountAndUser();
for (User user : list) {
System.out.println(user);
}
}
结果就省略了,亲测没毛病。
3.多对多
先看一下最终的SQL语句:
select r.id as rid,r.role_name,r.role_desc,u.* from role r left outer join user_role ur on r.id=ur.rid left outer join user u on ur.uid=u.id;
简单解释一下,用三张表,role表,user表和user_role表,user_role表是中间表,其uid和user表产生联系,rid和role表产生联系,最终使role和user表产生联系。这个多对多分种,就是看把查询出来的结果封装到哪个实体类中,我这里封装到了role类中。其实原理都一样,有兴趣的伙伴可以试着封装到user类中。
之前的user类中已经添加属性了,我这里就不改了,免得之前的测试功能坏掉,这里我们新建一个user2类,还是要保留之前的属性。既然我们要将结果封装到role中,就要在role类中添加属性,如下:
@Getter
@Setter
@ToString
public class Role implements Serializable {
private Integer id;
private String roleName;
private String roleDesc;
// 这里的属性名和表中的字段名有点不一样,所以一会要配置映射
private List<User2> user2s;
}
我这里使用了Lombok插件,自动补全了get,set,toString方法。并且,roleName,roleDesc和表中的名字不太一样(表中是role_name,role_desc),一会配置就好了。
然后在IRoleDao接口中添加方法
// 外连接查找角色和用户表
List<Role> findByRoleAndUser();
接下来就在IRoleDao.xml配置文件中进行配置,主要配置如下:
<resultMap id="roleMap" type="Role">
<!-- 配置主键-->
<id property="id" column="id"/>
<!-- 配置其余属性-->
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
<!-- 此种配置,测出来的是每个角色对应的用户信息,换句话说,角色信息都有,用户信息可能不全-->
<collection property="user2s" ofType="user2">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</collection>
</resultMap>
我们这里使用的标签是collection,和一对多的一样,里面的属性就不解释啦,下来写SQL语句即可
<select id="findByRoleAndUser" resultMap="roleMap" resultType="role">
select r.id as rid,r.role_name,r.role_desc,u.* from role r left outer join user_role ur on r.id=ur.rid left outer join user u on ur.uid=u.id;
</select>
然后写测试代码:
@Test
public void testFindByRoleAndUser(){
List<Role> roles = dao.findByRoleAndUser();
for (Role role : roles) {
System.out.println(role);
}
}
展示一下结果吧,要不有人说我在这瞎写呢