一、MyBatis 连接池与事务深入
1.MyBatis的连接池技术
1.1 MyBatis 连接池分类:
在 Mybatis 的 SqlMapConfig.xml 配置文件中,通过来实现 Mybatis 中连接池的配置。
在 Mybatis 中我们将它的数据源 dataSource 分为以下几类:
可以看出 Mybatis 将它自己的数据源分为三类:
-
UNPOOLED 不使用连接池的数据源
-
POOLED 使用连接池的数据源
-
JNDI 使用 JNDI 实现的数据源
具体结构如下:
相应地,MyBatis 内部分别定义了实现了 java.sql.DataSource 接口的 UnpooledDataSource,PooledDataSource 类来表示 UNPOOLED、POOLED 类型的数据源。
在这三种数据源中,我们一般采用的是 POOLED 数据源(很多时候我们所说的数据源就是为了更好的管理数据库连接,也就是我们所说的连接池技术)。
1.2 Mybatis 中数据源的配置
我们的数据源配置就是在 SqlMapConfig.xml 文件中,具体配置如下:
<!-- 配置连接池信息 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
MyBatis 在初始化时,根据<dataSource>的 type 属性来创建相应类型的的数据源 DataSource,即:
- type=”POOLED”:MyBatis 会创建 PooledDataSource 实例
- type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例
- type=”JNDI”:MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
1.3 MyBatis 中 DataSource 的获取
MyBatis 是 通 过 工 厂 模 式 来 创 建 数 据 源 DataSource 对 象 的 , MyBatis 定 义 了 抽 象 的 工 厂 接口:org.apache.ibatis.datasource.DataSourceFactory,通过其 getDataSource()方法返回数据源DataSource。
下面是 DataSourceFactory 源码,具体如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.datasource;
import java.util.Properties;
import javax.sql.DataSource;
public interface DataSourceFactory {
void setProperties(Properties var1);
DataSource getDataSource();
}
MyBatis 创建了 DataSource 实例后,会将其放到 Configuration 对象内的 Environment 对象中, 供以后使用。
2.MyBatis 的事务控制
2.1 MyBatis 中事务提交方式
Mybatis 中事务的提交方式,本质上就是调用 JDBC 的 setAutoCommit()来实现事务控制。
@Before // 用于在测试方法执行之前执行 public void init() throws Exception { // 1. 读取配置文件,生成字节输入流 in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 2. 获取SqlSessionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); // 3. 获取SqlSession对象 sqlSession = factory.openSession(); // 4. 获取dao的代理对象 userDao = sqlSession.getMapper(UserDao.class); } @Test public void testFindAll(){ // 5. 执行查询所有方法 List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } } @After // 用于在测试方法执行之后执行 public void destroy() throws Exception { // 提交事务 sqlSession.commit(); // 6. 释放资源 sqlSession.close(); in.close(); }
2.2 MyBatis 自动提交事务的设置
在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,这样我们就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit()方法实现事务提交。
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
// 在此次给openSession()方法添加参数true
sqlSession = factory.openSession(true);
// 4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 此时不需要再提交事务
// sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
二、MyBatis 的动态 SQL 语句
1. 动态 SQL 之<if>标签
1.1 持久层 Dao 接口
/**
* 根据传入参数条件查询
* @param user 查询的条件:有可能有用户名,有可能有性别,也有可能有地址,还有可能是都有或都没有
* @return
*/
List<User> findUserByCondition(User user);
1.2 持久层 Dao 映射配置
<select id="findUserByCondition" resultMap="userMap" parameterType="User">
select * from user where 1=1
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</select>
1.3 测试
/**
* 测试查询所有
*/
@Test
public void testFindByCondition() {
User u = new User();
u.setUserName("老王");
u.setUserSex("女");
// 5. 执行查询所有方法
List<User> users = userDao.findUserByCondition(u);
for (User user : users) {
System.out.println(user);
}
}
2. 动态 SQL 之<where>标签
2.1 持久层 Dao 映射配置
<select id="findUserByCondition" resultMap="userMap" parameterType="User">
<include refid="defaultUser"></include>
<where>
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</where>
</select>
3. 动态 SQL 之<foreach>标签
3.1 需求
传入多个 id 查询用户信息,用下边两个 sql 实现:
SELECT * FROM USERS WHERE username LIKE ‘%王%’ AND (id =41 OR id =42 OR id=45)
SELECT * FROM USERS WHERE username LIKE ‘%王%’ AND id IN (41,42,45)
这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。
在 QueryVo 中加入一个 List 集合用于封装参数
/**
* @author boy
* @create 2020-07-23 9:42
*/
public class QueryVo {
private User user;
private List<Integer> ids;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
3.2 持久层 Dao 接口
/**
* 根据QueryVo中提供的id集合,查询用户信息
* @param vo
* @return
*/
List<User> findUserInIds(QueryVo vo);
3.3 持久层 Dao 映射配置
<!-- 根据QueryVo中的Id集合实现查询用户列表 -->
<select id="findUserInIds" resultMap="userMap" parameterType="QueryVo">
<include refid="defaultUser"></include>
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
3.4 编写测试方法
/**
* 测试foreach标签的使用
*/
@Test
public void testFindInIds() {
QueryVo vo = new QueryVo();
List<Integer> list = new ArrayList<>();
list.add(41);
list.add(42);
list.add(43);
list.add(46);
list.add(80);
vo.setIds(list);
// 5. 执行查询所有方法
List<User> users = userDao.findUserInIds(vo);
for (User user : users) {
System.out.println(user);
}
}
4. MyBatis 中简化编写的 SQL 片段
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的。
4.1 定义代码片段
<!-- 了解的内容,抽取重复的sql语句 -->
<sql id="defaultUser">
select * from user
</sql>
4.2 引用代码片段
<!-- 查询所有 -->
<select id="findAll" resultMap="userMap">
<include refid="defaultUser"></include>
</select>
<!-- 根据id查询用户 -->
<select id="findById" parameterType="int" resultMap="userMap">
select * from user where id = #{uid}
</select>
三、MyBatis 多表查询之一对多
1. 一对一查询(多对一)
方式一:
1.1 定义账户信息的实体类
/**
* @author boy
* @create 2020-07-23 20:59
*/
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
1.2 编写 SQL 语句
实现查询账户信息时,也要查询账户所对应的用户信息。
Select a.*, u.username, u.address from account a, user u where a.uid = u.id
1.3 定义 AccountUser 类
为了能够封装上面 SQL 语句的查询结果,定义 AccountCustomer 类中要包含账户信息同时还要包含用户信息,所以我们要在定义 AccountUser 类时可以继承 User 类。
/**
* @author boy
* @create 2020-07-23 21:07
*/
public class AccountUser extends Account{
private String username;
private String address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return super.toString() + " AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}
1.4 定义账户的持久层 Dao 接口
/**
* @author boy
* @create 2020-07-23 21:00
*/
public interface AccountDao {
/**
* 查询所有账户,并且带有用户名称和地址信息
* @return
*/
List<AccountUser> findAll();
}
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.itheima.dao.AccountDao">
<!-- 查询所有 -->
<select id="findAll" resultType="AccountUser">
select u.*, a.id as aid, a.uid, a.money from account a, user u where u.id = a.uid;
</select>
</mapper>
注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。
1.6 创建 AccountTest 测试类
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class AccountTest {
private InputStream in;
private SqlSession sqlSession;
private AccountDao accountDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
accountDao = sqlSession.getMapper(AccountDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
// 5. 执行查询所有方法
List<Account> accounts = accountDao.findAll();
for (Account a : accounts) {
System.out.println("----------每个account的信息-----------");
System.out.println(a);
System.out.println(a.getUser());
}
}
}
方式二:
1.7 修改Account类
/**
* @author boy
* @create 2020-07-23 20:59
*/
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
// 从表实体应该包含一个主表实体的对象引用
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
1.8 修改 AccountDao 接口中的方法
/**
* @author boy
* @create 2020-07-23 21:00
*/
public interface AccountDao {
/**
* 查询所有账户,同时还要获取到当前账户的所属用户信息
* @return
*/
List<Account> findAll();
}
注意:第二种方式,将返回值改 为了 Account 类型。
因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息。
1.9 重新定义 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.itheima.dao.AccountDao">
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="Account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射,配置封装user的内容 -->
<association property="user" column="uid">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</association>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="accountUserMap">
select u.*, a.id as aid, a.uid, a.money from account a, user u where u.id = a.uid;
</select>
</mapper>
1.10 在 AccountTest 类中加入测试方法
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class AccountTest {
private InputStream in;
private SqlSession sqlSession;
private AccountDao accountDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
accountDao = sqlSession.getMapper(AccountDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
// 5. 执行查询所有方法
List<Account> accounts = accountDao.findAll();
for (Account a : accounts) {
System.out.println("----------每个account的信息-----------");
System.out.println(a);
System.out.println(a.getUser());
}
}
}
2. 一对多查询
2.1 编写 SQL 语句
SELECT u.*, a.id as aid, a.uid, a.money FROM user u LEFT JOIN account a ON u.id = a.uid
2.2 User 类加入 List<Account>
/**
* @author boy
* @create 2020-07-22 19:30
*/
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
// 一对多关系映射,主表实体应该包含从表实体的集合引用
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
2.3 用户持久层 Dao 接口中加入查询方法
/**
* 查询所有用户,同时获取到用户下所有账户的信息
* @return
*/
List<User> findAll();
2.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.itheima.dao.UserDao">
<!-- 定义User的resultMap -->
<resultMap id="userAccountMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!-- 配置user对象中accounts集合的映射 -->
<collection property="accounts" ofType="Account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select u.*, a.id as aid, a.uid, a.money from user u left outer join account a on u.id = a.uid
</select>
</mapper>
collection:
部分定义了用户关联的账户信息。表示关联查询结果集
property=“accounts”:
关联查询的结果集存储在 User 对象的上哪个属性。
ofType=“account”:
指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。
2.5 测试方法
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class UserTest {
private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println("----------每个用户的信息-----------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
}
四、MyBatis 多表查询之多对多
1. 实现 Role 到 User 多对多
1.1 业务要求及实现 SQL
需求:
实现查询所有对象并且加载它锁分配的用户信息。
分析:
查询角色我们需要用到 Role 表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE 表)才能关联到用户信息。
下面是实现的 SQL 语句:
SELECT
r.*, u.id uid, u.username, u.birthday, u.sex,u.address
FROM
role r
INNER JOIN
user_role ur
ON ( r.id = ur.rid)
INNER JOIN
user u
ON (ur.uid = u.id);
1.2 编写角色实体类
/**
* @author boy
* @create 2020-07-23 21:47
*/
public class Role implements Serializable {
private Integer roleId;
private String roleName;
private String roleDesc;
// 多对多的关系映射,一个角色可以赋予多个角色
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = 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;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
'}';
}
}
1.3 编写 Role 持久层接口
/**
* @author boy
* @create 2020-07-23 21:48
*/
public interface RoleDao {
/**
* 查询所有角色
* @return
*/
List<Role> findAll();
}
1.4 编写映射文件
<?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.itheima.dao.RoleDao">
<!-- 定义role表的ResultMap -->
<resultMap id="roleMap" type="Role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="User">
<id property="id" column="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="roleMap">
select u.*, r.id as rid, r.role_name, r.role_desc from role r
left outer join user_role ur on r.id = ur.rid
left outer join user u on u.id = ur.uid
</select>
</mapper>
1.5 编写测试类
/**
* @author boy
* @create 2020-07-23 21:50
*/
public class RoleTest {
private InputStream in;
private SqlSession sqlSession;
private RoleDao roleDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
roleDao = sqlSession.getMapper(RoleDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<Role> roles = roleDao.findAll();
for (Role role : roles) {
System.out.println("----------每个角色的信息-----------");
System.out.println(role);
System.out.println(role.getUsers());
}
}
}
2. 实现 User 到 Role 的多对多
2.1 User 到 Role 的多对多
从 User 出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为 User 与 Role 的多对多关系,可以被拆解成两个一对多关系来实现。
2.2 编写用户实体类
/**
* @author boy
* @create 2020-07-22 19:30
*/
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
// 多对多的关系映射:一个用户可以具备多个角色
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
2.3 编写 User 持久层接口
/**
* @author boy
* @create 2020-07-23 21:48
*/
public interface RoleDao {
/**
* 查询所有角色
* @return
*/
List<Role> findAll();
}
2.4 编写映射文件
<?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.itheima.dao.UserDao">
<!-- 定义User的resultMap -->
<resultMap id="userMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!-- 配置角色集合的映射 -->
<collection property="roles" ofType="Role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userMap">
select u.*, r.id as rid, r.role_name, r.role_desc from user u
left outer join user_role ur on u.id = ur.uid
left outer join role r on r.id = ur.rid
</select>
<!-- 根据id查询用户 -->
<select id="findById" parameterType="int" resultType="User">
select * from user where id = #{uid}
</select>
</mapper>
2.5 编写测试类
package com.itheima.test;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
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;
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class UserTest {
private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println("----------每个用户的信息-----------");
System.out.println(user);
System.out.println(user.getRoles());
}
}
}
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class UserTest {
private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println("----------每个用户的信息-----------");
System.out.println(user);
System.out.println(user.getRoles());
}
}
}