MyBatis学习(中)

一、MyBatis 连接池与事务深入

1.MyBatis的连接池技术

1.1 MyBatis 连接池分类:

​ 在 Mybatis 的 SqlMapConfig.xml 配置文件中,通过来实现 Mybatis 中连接池的配置。

​ 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());
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值