一个完整的一对一、一对多、多对多演示
一、前期准备
1、数据库表的准备
2、对应的类准备
Role和User的类对应好字段名创建
public class User {
private Integer id;
private String userName;
private Role role;
//getter&setter方法
//toString重写
3、Utils工具类,用于获取sqlSession
这个比较简单不多赘述,通过mybatisUtils.getSqlSession()来调用,后面我们会用到
二、实现一对一查询
1、创建userMapper接口,用于查询,接口的抽象方法是通过id查用户,用来进行一对一查询的演示
public interface userMapper {
public User selectByIdOneToOne(Integer id);
}
2、创建roleMapper接口,在resources/mapper/中创建roleMapper.xml,用于被主查询的association中的select属性调用
此时我们可以先通过接口中添加selectRoleById方法来测试查询是否正确
<resultMap id="roleMap" type="com.wangCool.pojo.Role">
<id property="id" column="id"/>
<result property="roleName" column="role_name"/>
<result property="enabled" column="enabled"/>
</resultMap>
<select id="selectRoleById" resultMap="roleMap">
SELECT *FROM role WHERE id = #{id0}
</select>
3、在resources/mapper/中写对应userMapper接口的UserMapper.xml
<resultMap id="userMap" type="com.wangCool.pojo.User">
<id property="id" column="user_id"/>
<result property="userName" column="user_name"/>
</resultMap>
<resultMap id="userUnite" type="com.wangCool.pojo.User" extends="userMap">
<association property="role"
select="com.wangCool.Dao.roleMapper.selectRoleById"
column="{id0=role_id}" fetchType="lazy"/>
</resultMap>
<select id="selectByIdOneToOne" resultMap="userUnite">
SELECT
u.id AS user_id,
u.user_name,
ur.role_id
FROM user u
INNER JOIN user_role ur ON ur.user_id = u.id
WHERE u.id = #{id}
</select>
1. extend让我们可以省去别名
2. association中
①property对应user类中对应role属性的属性名,因此可以省掉查询时role表的别名
②select让我们可以省去INNER JOIN role表的语句(新增一个user_role表字段方便column中对应{id0=role_id})
③column={id0=role_id}中,id0为子查询的查询用的参数(id=# {这里写的});role_id为主查询中的关联表的role_id字段
④select调用了另一个同目录下的roleMapper
⑤不用写columnPrefix
⑥使用fetchType="lazy"来延迟加载
3. 主查询没有role表的数据,只有一个user_role关联表的role_id字段
4、记得在mybatis主配置文件中注册两个mapper
<mappers>
<mapper resource="mapper/userMapper.xml"/>
<mapper resource="mapper/roleMapper.xml"/>
</mappers>
5、测试类
SqlSession sqlSession = mybatisUtils.getSqlSession();
userMapper dao = sqlSession.getMapper(userMapper.class);
User user = dao.selectByIdOneToOne(3);
System.out.println("开启了延迟加载");
System.out.println(user);
sqlSession.close();
以下是运行结果
可以看到,结果输出成功,并且延迟加载也实现了,解决了N+1问题,谈到延迟加载,如果不成功可能是因为主配置文件中没有添加以下配置
<setting name="aggressiveLazyLoading" value="false"/>
三、实现一对多查询
在实现一对多的过程中,发现似乎也实现了多对多查询,我们后面会提及,对于一对多查询,我建议是从最底级的表对应的mapper开始配置嵌套查询,否则可能会出现错误
以下我们要实现的是一个两层嵌套的一对多查询,即通过用户id查询用户,该用户拥有多个角色,以及每个角色有多个权限
1、前期准备
此处我们在一对一的基础上准备
- 将user类中的role属性改为装有role的集合类roleList
public class User {
private Integer id;
private String userName;
private List<Role> roleList;
- 数据库user_role表中增加关联,1个user_id对应多个role_id
- 数据库中添加privilege表以及role_privilege关联表(一个role对应多个权限)
如图,role1为班长,班长三个权限都有,role2学习委员只有两个权限
- role类中添加privilegeList的List集合属性
public class Role {
private Integer id;
private String roleName;
private Integer enabled;
private List<Privilege> privilegeList;
- 新增接口privilegeMapper,以及以该接口的权限的名称为命名空间的privilegeMapper.xml
在xml中添加以下privilegeMap的映射
<resultMap id="privilegeMap" type="com.wangCool.pojo.Privilege">
<id property="id" column="id"/>
<result property="privilegeName" column="privilege_name"/>
</resultMap>
**记得将该xml注册到mybatis中**
2、开始实现一对多
1、写一对多多嵌套查询应该从最底层开始写起,此处我们从privilegeMapper.xml开始配置
<select id="selectPrivilegeListByRoleId" resultMap="privilegeMap">
SELECT
p.id,
p.privilege_name
FROM privilege p
INNER JOIN role_privilege rp ON rp.privilege_id=p.id
WHERE rp.role_id=#{roleId}
</select>
①在privilegeMapper.xml中添加第三级查询:selectPrivilegeListByRoleId,通过roleId查询privilege(通过角色id查询对应权限)
②where条件中# {roleId}的roleId为上一级(roleMapper)查询出的角色id
③写完可以在对应接口中进行测试,因为每一个子查询都是可以独立存在的
2、权限往上一级为角色,我们到roleMapper.xml中来配置
- 添加第二级查询:selectRoleListByUserId,通过用户id查询对应角色
<select id="selectRoleListByUserId" resultMap="rolePrivilegeList">
SELECT
r.id,
r.role_name,
r.enabled
FROM role r
INNER JOIN user_role ur ON ur.role_id = r.id
WHERE ur.user_id = #{userId}
</select>
查询用的参数userId是最顶级(第一级)查询中查询到的
- 添加resultMap:rolePrivilegeList
<resultMap id="rolePrivilegeList" extends="roleMap" type="com.wangCool.pojo.Role">
<collection property="privilegeList"
select="com.wangCool.Dao.privilegeMapper.selectPrivilegeListByRoleId"
column="{roleId=id}" fetchType="lazy"/>
</resultMap>
①因为是用于查角色,所以此处继承roleMap
②嵌套collection
property为role类中的权限集合的属性名
select调用privilegeMapper.xml中的第三级查询
column中等号右边的值id为第二级查询中查到的角色id,要③对应selectRoleListByUserId中查询到的字段名!,,等号右边赋值给等号左边,即id赋值给roleId,roleId为第三级查询selectPrivilegeListByRoleId中的查询参数,名字也要对应一致
④开启延迟加载fetchType="lazy"
⑤resultMap给第二级查询调用
⑥依旧可以在roleMapper接口中进行测试
3、配置完了第二级,我们就到了最顶级userMapper了
- 添加主查询语句selectUserRolePrivilegeByIdMulti,通过用户id查找对应的角色及其所拥有的权限
<select id="selectUserRolePrivilegeByIdMulti" resultMap="userRoleListPrivilegeList">
SELECT
u.id user_id,
u.user_name
FROM user u
WHERE u.id = #{uid}
</select>
- 添加resultMap:userRoleListPrivilegeList
<resultMap id="userRoleListPrivilegeList" extends="userMap" type="com.wangCool.pojo.User">
<collection property="roleList"
select="com.wangCool.Dao.roleMapper.selectRoleListByUserId"
column="{userId=user_id}"
fetchType="lazy"/>
</resultMap>
到这里时,配置已经和上面类似了不再赘述
4、全部配置完成,进行测试
如图,为方便看运行结果,我们输出的时候用foreach分级输出一下,并且在获取到user集合后添加延迟查询的标记语句
运行结果,可以看到,①延迟查询实现,并且嵌套部分也合并为了一条数据
四、多对多
①写完多对多发现,我所实现的一对多涉及了一点了多对多
②似乎真正的一对多就是,比如一个动画片分为3d动画片,2d动画片,cg动画片等这就是一个一对多,又有
一个游戏包含3d游戏,2d游戏,等等,也是一个一对多,而其中,3d游戏并不能归到动画片的版块,即不能
指向动画片,而3d动画片也不能指向游戏,故只能为一对多;
③而多对多是,学生A可以选很多课,如语文课,数学课,英语课,,,学生B也可以选这些课,即语文课可以
同时被多个学生选择,这些课程是可以指向多个人的,故为多对多
以上是我对三种查询的个人见解与实践经验