概述
第二讲:Mybatis关联映射
回顾:
parameterType:指定输入参数的数据类型
单个参数:(建议在语句标签上添加该属性)
简单类型数据:所有的基本数据类型(包括包装类型)和引用类型(String和其他内置API的类型如Date)
参数在语句中的使用:通过#{参数名}获取输入参数的值,当然#{}中参数名可以自定义,但是一般都是使用方法参数名
复杂类型数据:对象类和Map类型
参数在语句中的使用:通过#{属性名}或#{map的key值}获取输入参数的值
多个参数:(建议在标签中不设置属性)
不建议使用默认占位别名arg0,arg1…或 param1,param2…
建议:在每个参数前通过注解@Param定义别名,在语句中使用#{别名}方式获取参数值
本日目标
掌握基于Mybatis的连表查询结果映射(关联映射)
一对一:主表一条数据关联从表一条数据(用户和用户详细信息)(提高数据库的查询效率,主表的字段比从表的字段用得多),借助主键或外键关联
一对多(多对一):主表一条数据关联从表多条数据(订单和订单项),借助外键(一定在多方)或中间表关联
多对多:必须是通过双向说明,两边看过去都是一对多,主表一条数据关联从表多条数据,同样从表一条数据关联主表多条数据(学生和课程),只能借助中间表
Mybatis关联映射说明:
连表查询实现步骤:
1)分析关系:当前主要查询的实体和需要关联查询的实体之间关系(一对一还是一对多)
2)定义关联映射属性:一对一使用对象属性,一对多使用对象集合属性
User和UserInfo,需要查询User的同时需要获取UserInfo,那么就需要关联映射查询
分析关系:一对一
定义关联属性:在User中定义:private UserInfo userInfo;
3)定义mapper接口方法,关联xml的Statement(namespace+id)
4)在xml映射文件中给Statement编写连表查询sql
5)需要自定义ResultMap进行结果集封装
6)在ResultMap中进行本身属性封装和关联属性封装(掌握两个标签就好)
本身属性封装和关联属性封装基本一致:通过id和result标签进行属性封装(id标签是必须存在的)
嵌套查询实现步骤:
前面三步和连表查询一致
4)在xml映射文件中给Statement编写单表查询sql(前提是表之间使用外键关联,而且外键在当前表中)
5)需要自定义ResultMap进行结果集封装
6)在ResultMap中进行本身属性封装和关联属性封装
本身属性封装:通过id和result标签进行属性封装
关联属性封装是通过调用其他查询语句完成封装
嵌套查询问题:容易出现N+1问题(主表数据查询一次,从表数据查询多次),对比连表查询语句来看效率低下
1.Mybatis 多表查询之一对一(掌握)
关联描述中:是双向分别描述的,从哪方去看,哪方就是主体
User和Account的关系需要在主体是哪方:
从User方看双方关系:一对多关系
从Account方看关系:一对一关系
本次案例主要以最为简单的用户和账户的模型来分析Mybatis 多表关系。用户为User 表,账户为Account
表。
一对一关系:
一个账户(Account)只能属于一个用户(User)
一个书籍(Book)只有一个作者(Author)
基于主键设计:
基于外键:
1.1 一对一查询(多对一)
需求
查询所有账户信息,关联查询用户信息。
注意:
从账户角度看用户:一对一
从用户角度看账户:一对多
如果基于外键关联,那么外键一定在多方上面
2.1.1 方式一:通过继承方式(了解)
2.1.1.1 定义账户信息的实体类
package com.yaorange.entity;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private Double money;
private Integer userId;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", money=" + money +
", userId=" + userId +
'}';
}
}
2.1.1.2 编写Sql语句
实现查询账户信息时,也要查询账户所对应的用户信息。
SELECT a.id,a.money,a.user_id as userId, u.name, u.address FROM account a LEFT JOIN user u on a.user_id = u.id
在MySQL中测试的查询结果如下:
2.1.1.3 定义AccountUser类
为了能够封装上面SQL语句的查询结果,定义类中要包含账户信息同时还要包含用户信息,所以我们要在定义AccountUser类时可以继承Account类。
package com.yaorange.entity;
import java.io.Serializable;
public class AccountUser extends Account implements Serializable {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return super.toString()+" User{" +
"name='" + name + ''' +
", address='" + address + ''' +
'}';
}
}
2.1.1.4 定义账户的持久层Dao接口
public interface AccountDao {
/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
* @return
*/
List<AccountUser> findAll();
}
2.1.1.5 定义AccountDao.xml文件中的查询配置信息
<?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.yaorange.mapper.AccountDao">
<!-- 配置查询所有操作-->
<select id="findAll" resultType="Accountuser">
SELECT a.id,a.money,a.user_id as userId, u.name, u.address FROM account a LEFT JOIN user u on a.user_id = u.id
</select>
</mapper>
注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型returnType的值设置为AccountUser类型,这样就可以接收账户信息和用户信息了。
2.1.1.6 创建AccountTest测试类
package com.yaorange.mapper;
import com.yaorange.entity.AccountUser;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
import static org.junit.Assert.*;
public class AccountMapperTest {
private InputStream inputStream;
private SqlSession sqlSession;
private AccountMapper mapper;
@Before
public void setUp() throws Exception {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sessionFactory.openSession();
mapper = sqlSession.getMapper(AccountMapper.class);
}
@After
public void tearDown() throws Exception {
sqlSession.commit();
sqlSession.close();
inputStream.close();
}
@Test
public void findAllTest(){
List<AccountUser> users = mapper.findAll();
for (AccountUser user : users) {
System.out.println(user);
}
}
}
2.1.1.8 小结:
定义专门的po类作为输出类型,其中包含了sql查询结果集所有的字段(本次是通过继承实体)。此方法较为简单,企业中使用普遍。不建议
2.1.2 方式二
使用resultMap,定义专门的resultMap用于映射一对一查询结果。
通过面向对象的(has a)关系可以得知,我们可以在Account类中加入一个User类的对象来代表这个账户是哪个用户的。
2.1.2.1 新增User类
package com.yaorange.entity;
public class User {
private Integer id;
private String name;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + ''' +
", address='" + address + ''' +
'}';
}
}
修改Account类
在Account类中加入User类的对象作为Account类的一个属性(关联属性)。
package com.yaorange.entity;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private Double money;
//该字段在数据库中表示一个用户数据
// private Integer userId;//数据库关联映射字段
//新增关联属性:一对一,所以使用对象属性
private User user;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", money=" + money +
", user=" + user +
'}';
}
}
2.1.2.2 修改AccountDao接口中的方法
/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
* @return
*/
List<Account> getAll();
注意:第二种方式,将返回值改 为了Account类型。
因为Account类中包含了一个User类的对象,它可以封装账户所对应的用户信息。
2.1.2.3 重新定义AccountDao.xml文件
<?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.yaorange.dao.AccountDao">
<!--一次性配置方式:-->
<!--定义一对一结果映射处理Map-->
<resultMap id="acccountMap" type="Account">
<id column="id" property="id"/>
<result column="money" property="money"/>
<!--association:用于一对一映射,将结果集指定列映射为对象-->
<!--collection:用于一对多映射,将结果集指定列映射为集合对象-->
<association property="user" javaType="User">
<id column="user_id" property="id"/>
<result column="name" property="name"/>
<result property="address" column="address"/>
</association>
</resultMap>
<!--高重用方式(实际开发中的选择)-->
<!--1)定义基本map,封装当前实体对象属性封装-->
<resultMap id="baseResultMap" type="Account">
<id column="id" property="id"/>
<result column="money" property="money"/>
</resultMap>
<!--2)定义关联映射map,通过extends属性继承基本map-->
<resultMap id="accountAndUserMap" type="Account" extends="baseResultMap">
<!--在标签中通过resultMap属性引用其他配置文件中的resultMap进行结果映射-->
<association property="user" javaType="User" resultMap="com.yaorange.dao.UserDao.baseResultMap"/>
<!--自行进行结果映射-->
<!--<association property="user" javaType="User">-->
<!--<id column="user_id" property="id"/>-->
<!--<result column="name" property="name"/>-->
<!--<result property="address" column="address"/>-->
<!--</association>-->
</resultMap>
<select id="getAll" resultMap="accountAndUserMap">
SELECT a.account_id as id,a.money,a.user_id as userId, u.sex,u.name, u.address FROM account a LEFT JOIN user u on a.user_id = u.user_id
</select>
</mapper>
UserDao.xml
<?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.yaorange.dao.UserDao">
<resultMap id="baseResultMap" type="User">
<id column="user_id" property="id"/>
<result column="name" property="name"/>
<result property="birthday" column="birthday"/>
<result property="address" column="address"/>
</resultMap>
</mapper>
2.1.2.4 在AccountTest类中加入测试方法
package com.yaorange.service;
import com.yaorange.dao.AccountDao;
import com.yaorange.entity.Account;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class AccountTest {
private InputStream inputStream;
private SqlSession sqlSession;
private AccountDao accountDao;
@Before
public void setUp() throws Exception {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sessionFactory.openSession();
accountDao = sqlSession.getMapper(AccountDao.class);
}
@After
public void tearDown() throws Exception {
sqlSession.commit();
sqlSession.close();
inputStream.close();
}
@Test
public void getAllTest(){
List<Account> accountList = accountDao.selectAll();
for (Account account : accountList) {
System.out.println(account);
}
}
}
1.2 懒加载(延迟加载,了解原理和意义)
在上面的使用方式中,都是一次性将所有数据都进行查询获取,然后在ResultMap中进行映射,如果一对一查询中,关联属性user,使用的不是很频繁,而是偶尔使用,那么可以进行延迟加载配置
懒加载(延迟加载):也就是先查询账户Account,查询Account时不查询User,当程序逻辑中在需要使用User属性的数据时再去查询User
定义接口:
AccountMapper:添加方法
Account getAccountById(Integer id);
UserMapper:添加方法
User getUserById(Integer id);
配置映射文件:
在关联映射标签中使用fetchType=“lazy”
AccountMapper.xml
<!--公共字段-->
<sql id="accountColums">
id,money,user_id
</sql>
<!--通过id查询账户-->
<select id="getAccountById" resultMap="accountMap1">
select <include refid="accountColums"/> from account where id=#{id}
</select>
<!--优化配置-->
<resultMap id="baseResultMap" type="Account">
<!--映射当前实体的本身字段-->
<id column="id" property="id"/>
<result column="money" property="money"/>
</resultMap>
<!--账户查询结果映射-->
<resultMap id="accountMap1" type="Account" extends="baseResultMap">
<!--映射关联对象属性:association:映射对象属性的,collection:映射对象集合属性的-->
<!--嵌套查询方式配置:延迟加载只能在嵌套查询中配置-->
<!--column:指定用于嵌套查询的参数字段名-->
<!--select:指定嵌套查询的语句:namespace+id-->
<!--fetchType:指定嵌套查询的加载方式,值为lazy(延迟加载) 和 eager(及时加载)-->
<association property="user" javaType="User" column="uid" select="com.yaorange.mapper.UserMapper.getUserById" fetchType="lazy">
</association>
</resultMap>
UserMapper.xml:
<?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.yaorange.mapper.UserMapper">
<!--公共字段-->
<sql id="userColums">
id,name,sex,birthday,address
</sql>
<!--通过id查询用户-->
<select id="getUserById" resultType="User">
select <include refid="userColums"/> from user where id=#{uid}
</select>
</mapper>
配置全局文件
官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#settings
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--配置触发延迟加载语句的执行的方法:覆盖默认配置,避免调用toString会触发延迟加载语句执行
lazyLoadTriggerMethods:指定方法调用时会触发延迟加载语句的执行-->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode"/>
</settings>
测试代码
@Test
public void getAccountByIDTest() {
AccountMapper mapper = sqlSession.getMapper(AccountMapper.class);
Account account = mapper.getAccountById(2);
System.out.println(account);//不会执行延迟加载属性,因为没有使用延迟加载关联属性的数据
//执行下面代码时会触发延迟加载语句的执行:因为只要使用的延迟加载关联的属性时就执行语句
// System.out.println(account.getUser());
}
总结:
嵌套查询的使用:
在关联映射标签中使用select指定嵌套执行的statement的id(全路径:namespace+id),如果嵌套执行的语句需要参数就使用column属性指定当前表的字段名,在执行时就会将指定字段值传入嵌套sql中执行
如果想要指定嵌套查询的加载策略使用属性fetchType,值默认为eager表示及时加载,lazy表示使用是加载(延迟加载)
<association property="user" javaType="User" column="user_id" select="com.yaorange.dao.UserDao.getUserById"/>
2. Mybatis 多表查询之一对多(掌握)
2.1 一对多查询
需求:
查询所有用户信息及用户关联的账户信息。
分析:
用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。
同理:
用户和角色:一个用户可以有多个角色
订单和订单项:一个订单有多个订单项
2.1.1 编写SQL语句
SELECT u.id,u.name,u.birthday,u.sex,u.address, acc.id account_id, acc.money FROM user u LEFT JOIN account acc ON u.id = acc.user_id
--或
SELECT u.id,u.name,u.birthday,u.sex,u.address, acc.id account_id, acc.money FROM account acc RIGHT JOIN user u on acc.user_id = u.id
测试该SQL语句在MySQL客户端工具的查询结果如下:
理解简单连接和左右外连接测试语句:
SELECT u.*, acc.id id, acc.uid, acc.money FROM user u LEFT JOIN account acc ON u.id = acc.uid;
SELECT u.*, acc.id id, acc.uid, acc.money FROM user u RIGHT JOIN account acc ON u.id = acc.uid;
SELECT u.*, acc.id id, acc.uid, acc.money FROM user u , account acc where u.id = acc.uid;
2.1.2 User类加入List
一对一关联:使用的关联属性是对象属性(描述一)
一对多关联:使用的关联属性是对象集合属性(描述多)
public class User implements Serializable{
private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
", sex='" + sex + ''' +
", birthday=" + birthday +
", address='" + address + ''' +
", accounts=" + accounts +
'}';
}
}
2.1.3 用户持久层Dao接口中加入查询方法
/**
* 查询所有用户,同时获取出每个用户下的所有账户信息
* @return
*/
List<User> findAll();
2.1.4 用户持久层Dao映射文件配置
<?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.yaorange.mapper.UserDao">
<!-- 配置查询所有操作 -->
<select id="selectAll" resultMap="userAndAccountMap">
SELECT u.id,u.name,u.birthday,u.sex,u.address, acc.id as account_id, acc.money FROM user u LEFT JOIN account acc ON u.id = acc.user_id
</select>
<resultMap id="userAndAccountMap" type="User" extends="baseResultMap">
<!--引用其他文件中的ResultMap进行数据封装-->
<!--<collection property="accounts" ofType="Account" resultMap="com.yaorange.dao.AccountDao.baseResultMap"/>-->
<!--语句中使用了别名区分重名字段时,需要自定义数据封装-->
<collection property="accounts" ofType="Account">
<id column="account_id" property="id"/>
<result column="money" property="money"/>
</collection>
</resultMap>
</mapper>
collection :
部分定义了用户关联的账户信息。表示关联查询结果集
property=“accList”:
关联查询的结果集存储在User对象的上哪个属性。
ofType=“account”:
指定关联查询的结果集中的对象类型即List中存储的对象类型。此处可以使用别名,也可以使用全限定名。
2.2.5 测试方法
@Test
public void findAll() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象SqlSessionFactory
SqlSessionFactory factory = builder.build(in);
//4.使用SqlSessionFactory生产SqlSession对象
SqlSession session = factory.openSession();
//5.创建Dao的代理对象
UserDao userDao = session.getMapper(UserDao.class);
//6.执行查询方法
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println("-------每个用户的内容---------");
System.out.println(user);
System.out.println(user.getAccounts());
}
//7.释放资源
session.close();
in.close();
}
任务:执行完成一对多嵌套查询
3. Mybatis 多表查询之多对多(掌握)
一对一和一对多配置都是基于单向说明:
如:从账户看用户:一个账户只能关联一个用户,所以是一对一
从用户看账户:一个用户可以关联多个账户,所以是一对多
多对多是基于双向说明:
如:用户和角色:
从用户看角色:一个用户可以关联多个角色,是一对多关系
从角色看用户:一个角色可以关联多个用户,是一对多关系
只有满足上面情况,就可以说用户和角色之间是多对多关系
3.1 实现Role 到User 多对多
通过前面的学习,我们使用Mybatis 实现一对多关系的维护。多对多关系其实我们看成是双向的一对多关
系。
同理:学生和课程:也是多对多关系,一个学生可以有多个课程,一个课程中可以有多个学生
多对多就是通过双向查询:通过学生查询课程,也可以通过课程查询学生
而一对一和一对多都是描述单向查询:通过账户查询用户是一对一(多对一),通过用户查询账户是一对多
3.1.1 用户与角色的关系模型
用户与角色的多对多关系模型如下:
在MySQL 数据库中添加角色表,用户角色的中间表。
角色表
用户角色中间表
一对一关系设计:基于主键设计或基于外键设计
基于主键设计:保证两张表的主键字段值相等的表示的是一个数据(设计时只能让最多一张表主键自增)
基于外键设计:一般都是在主表添加外键字段值(也可以在从表添加外键),引用关联从表主键值(实际开发中,一般只添加形式上的外键(没有外键约束的外键))
一对多关系设计:基于外键或基于中间表设计
基于外键设计(常用):只能在多方表中添加外键字段
基于中间表设计:都是只能存储两列(分别为关联的两张表的主键值),表名一般都是两张表的名称使用_拼接
多对多关系设置:只能基于中间表
3.1.2 业务要求及实现SQL
需求:
实现查询所有角色并且加载它所分配的用户信息。(需要查询获取所有角色,哪怕某个角色没有用户也需要显示,只是用户为空)
分析:
查询角色我们需要用到Role表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE表)才能关联到用户信息。
下面是实现的SQL语句:
内连接:只能查询有匹配关联的数据,可能会显示不完整
SELECT r.*,u.id uid, u.username username, u.birthday birthday, u.sex sex, u.address address FROM ROLE r
INNER JOIN USER_ROLE ur ON ( r.id = ur.rid)
INNER JOIN USER u ON (ur.uid = u.id);
语句相当于:
select r.*,u.id uid, u.username username, u.birthday birthday, u.sex sex, u.address address FROM ROLE r,USER_ROLE ur,USER u where r.id = ur.rid and ur.uid = u.id;
左外连
select r.id as rid,r.role_name,r.role_desc,u.* from role r
left join user_role ur on r.id = ur.role_id
left join user u on u.id = ur.user_id;
通过分析查询所有时需要使用外连接(本次选择左外连接)
3.1.3 编写角色实体类
public class Role implements Serializable {
private Integer roleId;
private String roleName;
private String roleDesc;
//多对多的关系映射:一个角色可以赋予多个用户
private List<User> users;
public Integer getRoleId() {
return roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + ''' +
", roleDesc='" + roleDesc + ''' +
", users=" + users +
'}';
}
}
3.1.4 编写Role持久层接口
public interface RoleDao {
/**
* 查询所有角色
* @return
*/
List<Role> findAll();
}
3.1.5 编写映射文件
<?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.yaorange.mapper.RoleDao">
<!--查询所有-->
<select id="findAll" resultMap="roleMap">
select r.id as rid,r.role_name,r.role_desc,u.* from role r
left join user_role ur on r.id = ur.role_id
left join user u on u.id = ur.user_id;
</select>
<!--定义role表的ResultMap-->
<resultMap id="roleMap" type="Role">
<id column="rid" property="roleId"/>
<result column="role_name" property="roleName"/>
<result column="role_desc" property="roleDesc"/>
<collection property="users" ofType="User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</collection>
</resultMap>
</mapper>
提示:实际开发中,一定注意给每个表的字段命名时,建议设置前缀或后缀,避免连表查询时结果集字段重名,
比如:上面配置中,因为有重名所以结果映射中就无法进行继承ResultMap方式
3.1.6 编写测试类
@Test
public void findAll() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象SqlSessionFactory
SqlSessionFactory factory = builder.build(in);
//4.使用SqlSessionFactory生产SqlSession对象
SqlSession session = factory.openSession();
//5.创建Dao的代理对象
RoleDao roleDao = session.getMapper(RoleDao.class);
//6.执行查询方法
List<Role> roles = roleDao.findAll();
for(Role role : roles){
System.out.println("---每个角色的信息----");
System.out.println(role);
System.out.println(role.getUsers());
}
//7.释放资源
session.close();
in.close();
}
3.2 实现User到Role的多对多
3.2.1 User到Role的多对多
从User出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为User与Role之间是多对多关系,可以被拆解成两个一对多关系来实现。
3.2.2 作业:实现User到Role的一对多查询
需求:实现查询所有用户信息并关联查询出每个用户的角色列表。
面试题:
实现一对一,一对多和多对多的方式:
基于配置进行说明(使用association或collection标签配置)和实体设计说明(对象属性或集合属性描述关系)
注意:
association标签使用javaType属性指定映射的实体类型
collection标签使用ofType属性指定映射的实体类型
最后
以上就是紧张海燕为你收集整理的第二讲:Mybatis关联映射的全部内容,希望文章能够帮你解决第二讲:Mybatis关联映射所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复