我是靠谱客的博主 腼腆纸鹤,最近开发中收集的这篇文章主要介绍SSM框架学习----Mybatis(3)Mybatis框架学习----(3),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Mybatis

  • Mybatis框架学习----(3)
    • 1. Mybatis连接池与事务深入
      • 1.1. Mybatis连接池技术
        • 1.1.1. Mybatis连接池的分类
        • 1.1.2. 使用POOLED和UNPOOLED连接池的实例
        • 1.2.3. UnpooledDataSource源码分析
        • 1.1.4. PooledDataSource源码分析
      • 1.2. Mybatis中的事务
        • 1.2.1. 事务
        • 1.2.2. 事务的ACID特性
        • 1.2.3. 不考虑隔离性引发的3个问题
        • 1.2.4. 4种隔离级别
        • 1.2.5. Mybatis中设置自动提交事务
    • 2. Mybatis映射文件的SQL深入
      • 2.1. 动态SQL----``标签
      • 2.2. 动态SQL----``标签
      • 2.3. 动态SQL----``标签
      • 2.4. Mybatis中简化书写的SQL片段
    • 3. Mybatis的多表关联查询
      • 3.1. 一对一查询
      • 3.2. 多对一查询
        • 多表关联查询----创建自定义类来解决
        • 多表关联查询----在配置文件中增加resultMap
      • 3.3. 一对多查询
      • 3.4. 多对多查询
        • 1. 在MySQL中建立User,Role以及User_Role表
        • 2. 实体类
        • 3. Dao接口
        • 4、RoleDao配置文件
        • 5. UserDao配置文件
        • 6. 测试类中进行测试并输出结果
      • 4. JNDI

Mybatis框架学习----(3)

1. Mybatis连接池与事务深入

1.1. Mybatis连接池技术

在Mybatis的SqlMapConfig.xml配置文件中,通过<dataSource type="pooled">来实现Mybatis中连接池的配置。连接池适用于存储连接的一个容器,容器是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一个连接对象,另外该集合必须实现队列的特性:先入先出。使用连接池可以减少获取连接对象所消耗的时间。

    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(数据库连接池)-->
            <dataSource type="POOLED">
1.1.1. Mybatis连接池的分类
  • POOLED采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对该规范的实现
  • UNPOOLED采用传统的获取连接的方式,虽然实现了DataSource接口,但是并没有使用池的思想,使用一个获取一个
  • JNDI采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所获取的DataSource是不一样的。如果不是web或者maven的war工程,是不能使用的。而Tomcat服务器采用的连接池是DPCP连接池。

三者关系结构如下:
请添加图片描述

相应地,Mybatis内部分别实现了java.sql.DataSource接口的UnpooledDataSource,PooledDataSource类来表示UNPOOLED,POOLED类型的数据源。

1.1.2. 使用POOLED和UNPOOLED连接池的实例

请添加图片描述
使用UNPOLLED作为连接池,调用findAll方法得到的输出结果如下:
请添加图片描述

可以发现,使用POOLED连接池,是从池中获取一个连接,而UNPOOLED则每次创建一个新的连接来使用。

1.2.3. UnpooledDataSource源码分析

请添加图片描述请添加图片描述
请添加图片描述
请添加图片描述

第一个doGetConnection方法获取获取用户名和密码,传递一个properties给第二个doGetConnection方法,其中的initializeDriver也就是JDBC中的注册驱动操作:

请添加图片描述
再通过DriverManager来获取连接并且返回一个connection连接。

1.1.4. PooledDataSource源码分析

请添加图片描述
请添加图片描述

当conn为空时,需要获取连接,并且获取连接的代码被写入到synchronized代码块中,保证获取连接是线程安全的;三个if条件判断:第一个if是当idleConnection连接不为空(空闲连接池还有空闲连接),那么从空闲连接池中获取连接,如果当空闲池中没有空闲连接了,那么会进行第二个if语句判断,查看活动池中是否已经达到了池的最大数量,如果没有达到,则直接从化活动池中选取一个返回当活动池也满时,那么会判断活动池中哪个是最先进来的(Oldest),将该连接进行返回

1.2. Mybatis中的事务

1.2.1. 事务

一个包含多个步骤的业务操作,这些操作要么同时成功,要么同时失败

注意:MySQL是自动提交事物的,而手动提交需要先开启事务,再提交。Oracle数据库默认是手动提交的

1.2.2. 事务的ACID特性
  • 原子性:不可分割的最小操作单位,要么同时成功,要么同时失败
  • 持久性:事务提交或回滚,数据库会持久化的保存数据
  • 隔离性:多个事务之间相互独立
  • 一致性:事务操作前后,数据总量不变
1.2.3. 不考虑隔离性引发的3个问题
  • 脏读:一个事务读取到另一个事务没有提交的数据
  • 不可重复读:在同一个事务中,两次读取到的数据不一致
  • 幻读:一个DML操作数据表中的所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改
1.2.4. 4种隔离级别

事务的隔离级别:多个事务操作同一批数据,会存在问题,因此需要设置不同的隔离级别来解决

  • read uncommitted:读未提交,引发的问题有:脏读,不可重复读,幻读

  • read committed: 读已提交,引发的问题有:不可重复读,幻读(Oracle默认)

  • repeatable read: 可重复读,引发的问题有:幻读(MySQL默认)

  • serializable:串行化,不会引起任何问题,事务一个一个执行

隔离级别从上到下安全性越来越高,但是执行效率越来越低

MySQL中查询隔离级别:

-- 查询隔离级别
select @@transaction_isolation;
-- 设置隔离级别
set global transaction isolation level  级别字符串;

请添加图片描述

1.2.5. Mybatis中设置自动提交事务

在SqlSessionFactory接口的源码中,对于openSession抽象方法存在重载,当传入true时,可以设置为auto commit
就不再需要手动去提交事务:

SqlSession session = factory.openSession(true);

与此同时,不再需要去手动提交事务,对于session.close()可以去掉

2. Mybatis映射文件的SQL深入

在某些逻辑复杂的业务中,SQL是动态变化的,之前使用的sql语句不能满足需求。

2.1. 动态SQL----<if>标签

根据实体类的不同取值,使用不同的SQL语句进行查询,比如:在id不为空时可以根据id来查询,如果username不为空需要加入用户名作为条件。这种情况在多条件组合查询中经常使用。

与之前插入sql语句的步骤相同:

  • 在Dao接口增加一个方法用来表示多条件查询用户信息
List<User> findUserByCondition(User user);
  • 在Dao接口的配置文件中,增加该抽象方法的sql语句
    <select id="findUserByCondition" resultType="User" parameterType="user">
<!-- 使用where 1=1 在多条件查询时防止if语句查不到导致SQL查询错误,1=1true则 select * from where true; -->
        select * from account where 1=1
        <if test="username != null">
            and username = #{username}
        </if>
        <if test="sex != null">
            and sex = #{sex}
        </if>
    </select>

由于已经在SqlMapConfig中配置了typeAliases别名,所以domian包下的类名不再区分大小写,使用where 1=1保证没有其他条件查询条件时,sql语句不会报错

  • 在测试类中进行测试
    @Test
    public void findUserByCondition(){
        User u = new User();
        u.setUsername("大司马");
        u.setSex("男");
        List<User> list = userDao.findUserByCondition(u);
        for(User user : list){
            System.out.println(user);
        }
    }
  • 输出查询结果

请添加图片描述

2.2. 动态SQL----<where>标签

如果不想在每条sql语句中都增加这条where 1=1永真语句,可以使用where标签,具体如下:

    <select id="findUserByCondition" resultType="user" parameterType="user">
        select * from account
        <where>
            <if test="username != null">
                username = #{username}
            </if>
            <if test="id != null">
               and id = #{id}
            </if>
        </where>
    </select>

2.3. 动态SQL----<foreach>标签

对于SQL语句:select 字段 from 表名 where id in {};

<foreach>标签用于遍历集合,属性有:

  • collection 遍历的集合元素,注意编写时不要写#{}
  • open 语句的开始部分
  • close 语句的结束部分
  • item 遍历集合的每个元素,生成的变量名
  • separator 分隔符

在QueryVo中封装一个List集合用于封装参数

  • 在Dao接口中增加一个抽象方法 findUserInIds,根据Id list来查询id属于该集合的用户信息
List<User> findUserInIds(QueryVo vo);
  • 在domain包下增加一个QueryVo类,成员变量有User以及一个id集合ids
package com.huzhen.domain;
import java.util.List;

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;
    }
}

  • 配置sql语句
 <select id="findUserInIds" resultType="user" parameterType="queryVo">
        select * from account
        <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>

foreach中open和close的内容组成的是查询条件,item查询的结果为uid存放在()中,并且#{uid}与item是对应的。

  • 测试类中演示
    @Test
    public void testFindByIdList(){
        QueryVo vo = new QueryVo();
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 2, 3, 11, 14);
        vo.setIds(list);

        List<User> users = userDao.findUserInIds(vo);
        for(User user : users){
            System.out.println(user);
        }
    }

请添加图片描述

2.4. Mybatis中简化书写的SQL片段

对于sql的配置文件,其中存在重复的sq语句,可以进行封装:

    <!-- 抽取重复的sql语句 -->
    <sql id="defaultUser">
        select *
        from account
    </sql>

调用defaultUser:

    <select id="findUserInIds" resultType="user" 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. Mybatis的多表关联查询

使用User-Account表来分析,一个User可以有多个账户

User:

idintprimary key
usernamevarchar(20)not null
sexvarchar(4)
birthdaydate
addressvarchar(20)

Account:

编码idintprimary key
用户编号uidintforeign key
金额moneydouble

在MySQL中创建表以及插入数据如下:

请添加图片描述
请添加图片描述

3.1. 一对一查询

查询所有账户信息,从查询账户信息出发关联查询用户信息为一对一查询。

步骤:

  • 建立两张表User,Account,让用户表和账户表之间存在一对多的关系,在Account表中添加外码约束

  • 建立两个实体类User,Account

  • 建立两个配置文件UserDao,AccountDao

  • 实现配置:

    • 查询所有账户信息

    • 当查询用户时,可以同时得到用户所属的账户信息

    • 当查询账户时,可以同时得到账户所属的用户信息

  1. 实现Account接口
public interface AccountInterface {

    /**
     * 查询所有账户信息
     */
    List<Account> findAll();

    /**
     * 查询所有账户信息, 同时需要得到当前账户的所属用户信息
     */
    List<AccountUser> findAccount();

}
  1. AccountDao配置文件
<?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.huzhen.dao.AccountInterface">
    <!-- 抽取重复的sql语句 -->
    <sql id="defaultSelectAccount">
        select *
        from account;
    </sql>

    <select id="findAll" resultType="account">
        <include refid="defaultSelectAccount"></include>
    </select>

    <!-- 创建account的子类来进行AccountUser表的关联查询 -->
    <select id="findAccount" resultType="AccountUser">
        select t2.*, t1.username, t1.sex, t1.address from user t1, account t2
        where t1.id = t2.uid;
    </select>

</mapper>
  1. 测试类中演示
package com.huzhen.test;

import com.huzhen.dao.AccountInterface;
import com.huzhen.domain.Account;
import com.huzhen.domain.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.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@SuppressWarnings("all")
public class test2 {

    private static InputStream is;
    private static SqlSession session;
    private static AccountInterface accountDao;

    @BeforeAll
    public static void init() throws IOException {
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        session = factory.openSession();
        accountDao = session.getMapper(AccountInterface.class);
    }

    @AfterAll
    public static void close(){
        session.commit();
        session.close();
        if(is != null){
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void testFindAll(){

        List<Account> list = accountDao.findAll();
        for(Account account : list){
            System.out.println(account);
        }
    }
    
    @Test
    public void testFindAccountUser(){
        List<AccountUser> account = accountDao.findAccount();
        for(AccountUser accountUser : account){
            System.out.println(accountUser);
        }
    }
}

对于查询单个表,即查询Account信息时,与之前的操作类似,结果如下:

请添加图片描述

3.2. 多对一查询

需求:输出每个账户信息的同时,输出所属的用户信息

多表关联查询----创建自定义类来解决

对于多表关联查询,如果查询帐户信息的同时,需要得到用户的username,sex以及address信息时,解决方法:

可以创建一个AccountUser类,继承Account类,类中的成员变量为username,sex以及address,并且在toString方法中增加父类的toString()

package com.huzhen.domain;

public class AccountUser extends Account{

    private String username;
    private String address;
    private String sex;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    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 + ''' +
                ", sex='" + sex + ''' +
                '}';
    }
}

在测试类中得到的输出为:

请添加图片描述

多表关联查询----在配置文件中增加resultMap
<resultMap id="accountUserMap" type="account">
        <!-- property对应实体类Account  column对应数据库中Account表的属性名 -->
        <id property="id" column="Aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射,配置封装User的内容 property对应user类,column的uid为Account中的外码 -->
        <association property="user" column="uid" javaType="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>
        </association>
    </resultMap>

定义封装Account和User的resultType,id属性为该resultMap的名字,type属性为主体类Account

<id>标签对应的是Account的主码,property对应实体类Account,column对应数据库中的Account表,由于查询user表中的id与Account中的id存在重名,因此使用别名Aid来表示Account中的id;<result>标签对应非主码的设置;<assocation>标签表示references参考的是从表User,是多对一结果关系映射,其中property对应user实体类,column对应Account中的外码属性uid,而javaType表示封装的对象类型为User,下面的id以及result标签与主表的配置一致。

从表实体应包含一个主表实体的对象引用, 实现account表关联user表,因此在Account表中增加一个user属性

private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

并且在AccountDao.xml配置:

<select id="findAll" resultMap="accountUserMap">
    <!-- 使用resultMap进行多表关联查询 -->
    select t1.*, t2.id as Aid, t2.uid, t2.money
    from user t1, account t2
    where t1.id = t2.uid;
</select>

其中,resultMap对应这上面配置的resultMap,此时的查询语句中,返回的字段为两个表的全部字段,由于在toString()方法中是全部进行输出,如果这里不返回account表中的uid或者id,在终端打印出来的信息会报null,而这里的Aid与上面配置resultMap中的account对应的column是一致的。

在test类进行测试以及打印输出结果:

    @Test
    public void testFindAll(){
        List<Account> list = accountDao.findAll();
        for(Account account : list){
            System.out.println("每个Account信息: ");
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }

请添加图片描述

3.3. 一对多查询

需求:查询所有用户以及用户关联的账户信息,如果某个用户信息没有账户信息,也需要将用户信息查询出来,使用左外连接–Left join

sql语句为:

SELECT t1.*, t2.id AS Aid, t2.money 
FROM USER t1 LEFT JOIN account t2
ON t1.`id`=t2.`Uid`;

又由于返回用户信息的同时需要返回帐户信息,而一个用户可以有多个账户,因此需要用一个集合来接收,在上面定义的user实体类中增加一对多的关系映射:

    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

在userDao.xml配置文件中定义User表关联Account表的resultMap:

<resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <!-- 配置User实体类中的Accounts集合的映射 property表示User类中Account集合的成员变量名
        ofType表示集合中的泛型, 如果没有使用typeAliases配置别名,则此时需要指定Account的全限定类名
        由于是一对多关系结果映射,所以使用collection-->
        <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>
    <!-- 使用左外连接返回左表(User)的全部数据 -->
    <select id="findUserAndAccount"  resultMap="userAccountMap">
        SELECT t1.*, t2.id AS Aid, t2.uid, t2.money
        FROM USER t1 LEFT JOIN account t2
        ON t1.`id`=t2.`uid`;
    </select>

resultMap中的id为User表示主表为user表,与3.2中的例子相反,id和result标签对应user实体类与user表的属性,由于返回的是一个集合,因此使用collection标签来定义从表Account,property指向user实体类中定义Account集合的成员变量名accounts,而ofType指向集合accounts中的泛型是account;另外,对于account实体类和account标的id配置中,在column属性中需要使用别名Aid来区分开user的id属性。

在测试类中进行测试:

    @Test
    public void testFindUserAndAccount(){
        List<User> list = userDao.findUserAndAccount();
        for(User user : list){
            System.out.println("User Information: ");
            System.out.println(user);
            System.out.println(user.getAccounts());
        }
    }

可以看到,id=2和id=3的用户有两个账户信息,而id=5-8的用户没有账户信息也可以打印出来。
请添加图片描述

3.4. 多对多查询

需求:用户和角色表,一个用户可以有多个角色,一个角色可以赋予给多个用户

  • 用户表使用User-Account关系中的User,并且新建一个角色表Role
  • 建立两个实体类,User 和Role,让用户和角色的实体类各自包含一个对方集合引用
  • 建立两个配置文件:用户的配置文件以及角色的配置文件
  • 实现配置:当查询用户时,可以同时得到用户所包含的角色信息;当查询角色时,可以同时得到角色所属的用户信息
1. 在MySQL中建立User,Role以及User_Role表

请添加图片描述
请添加图片描述
请添加图片描述

2. 实体类

实体类包括User以及Role

package com.huzhen.domain;

import java.io.Serializable;
import java.util.List;

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 + ''' +
                '}';
    }
}

package com.huzhen.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    
    // 多对多的关系映射,一个用户可以拥有多个角色
    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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + ''' +
                ", birthday=" + birthday +
                ", sex='" + sex + ''' +
                ", address='" + address + ''' +
                '}';
    }

    public static void main(String[] args) {

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        // 集合转换成数组
        int[] res = list.stream().mapToInt(Integer::intValue).toArray();
        System.out.println(res);
    }
}

需要注意的是这里的Role实体类中的成员变量名与Role表的字段不是对应的,因此需要在RoleDao配置文件进行resultMap配置

3. Dao接口
public interface UserInterface {

    /**
     * 查询所有用户
     * @return
     */
    List<User> findAll();

    /**
     * 查询所有用户以及对应的角色信息,如果某个用户没有角色,也需要进行输出
     * @return
     */
    List<User> findUserAndRole();
}
public interface RoleInterface {

    /**
     * 单表查询,只查询Role表的角色信息
     * @return
     */
    List<Role> findAll();

    /**
     * 查询角色信息的同时,返回其对应的用户信息
     */
    List<Role> findRoleAndUser();
}
4、RoleDao配置文件

需求:返回角色信息以及角色对应的用户信息,如果某个角色没有对应的用户,也需要进行输出,那么这里使用左外连接即可,sql语句:

SELECT t1.*, t2.`id` AS Uid, t2.`username`, t2.`sex`, t2.`birthday`, t2.`address`
FROM role t1 
LEFT OUTER JOIN user_role t3 
ON t1.`id` = t3.`rid`
LEFT OUTER JOIN USER t2
ON t3.`uid` = t2.`id`;

RoleDao.xml:

<mapper namespace="com.huzhen.dao.RoleInterface">
    <resultMap id="roleMap" type="role">
        <id property="roleId" column="id"></id>
        <result property="roleName" column="name"></result>
        <result property="roleDesc" column="description"></result>
        <!--property表示role类中user集合的成员变量名         Role -> User
        ofType表示集合中的泛型, 如果没有使用typeAliases配置别名,则此时需要指定Account的全限定类名
        由于是一对多关系结果映射,所以使用collection-->
        <collection property="users" ofType="user">
            <id property="id" column="Uid"></id>
            <result property="username" column="username"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="roleMap">
         select * from role;
    </select>

    <select id="findRoleAndUser" resultMap="roleMap">
        SELECT t1.*, t2.`id` AS Uid, t2.`username`, t2.`sex`, t2.`birthday`, t2.`address`
          FROM role t1
          LEFT OUTER JOIN user_role t3
          ON t1.`id` = t3.`rid`
          LEFT OUTER JOIN USER t2
          ON t3.`uid` = t2.`id`;
    </select>

</mapper>

注意:这里的resultMap中type对应role实体类,并且property对应role类中的成员变量名,而column对应role表中的字段;

由于一个角色可以有多个用户,那么返回的是一个集合,需要在role类中定义一个user的集合,来封装返回的用户信息:

    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

并且在collection标签中的ofType为user,同样为了区分id,对于user中的id使用别名uid来区分。

5. UserDao配置文件

需求:返回用户信息的同时,返回用户对应的角色信息,如果某个用户没有角色,也需要进行返回,与role -> user同理,使用left outer join或者right outer join,sql语句:

SELECT t1.*, t2.`id` AS Rid, t2.`name`, t2.`description`
FROM USER t1
LEFT OUTER JOIN user_role t3
ON t1.`id`=t3.`uid`
LEFT OUTER JOIN role t2
ON t3.`rid` = t2.`id`;

UserDao.xml

    <resultMap id="userMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <result property="address" column="address"></result>

        <collection property="roles" ofType="role">
            <id property="roleId" column="Rid"></id>
            <result property="roleName" column="name"></result>
            <result property="roleDesc" column="description"></result>
        </collection>
    </resultMap>
    <select id="findUserAndRole" resultMap="userMap">
        SELECT t1.*, t2.`id` AS Rid, t2.`name`, t2.`description`
         FROM USER t1
         LEFT OUTER JOIN user_role t3
         ON t1.`id`=t3.`uid`
         LEFT OUTER JOIN role t2
         ON t3.`rid` = t2.`id`;
    </select>

与4中的RoleDao配置文件类似,大同小异,故不再赘述。

6. 测试类中进行测试并输出结果
  • 6.1. RoleTest
public class RoleTest {
    /**
     * 由于需要测试多个操作,而每个test中读取配置文件,获取SqlSessionFactory来创建对象SqlSession
     * 以及获取Dao的代理对象和释放资源都是共用的,所以可以封装在一个init方法中
     */
    private static InputStream is;
    private static SqlSession sqlSession;
    private static RoleInterface roleDao;

    @BeforeAll
    public static void init() throws IOException {
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        sqlSession = factory.openSession();
        roleDao = sqlSession.getMapper(RoleInterface.class);
    }
    // AfterAll和BeforeAll注解修饰的必须是静态方法
    @AfterAll
    public static void close() throws IOException{
        sqlSession.commit(); // 对于更新,插入,删除操作必须有提交事物才能正常运行
        sqlSession.close();
        is.close();
    }
    /**
     * 查询所有role角色信息
     */
    @Test
    public void testFindAll(){
        List<Role> list = roleDao.findAll();
        for(Role role : list){
            System.out.println("Role information: ");
            System.out.println(role);
        }
    }

    /**
     * 查询所有role信息的同时,返回对应的用户信息
     */
    @Test
    public void testFindRoleAndUser(){
        List<Role> list = roleDao.findRoleAndUser();
        for(Role role : list){
            System.out.println();
            System.out.println(role);
            System.out.println(role.getUsers());
        }
    }
}

请添加图片描述

可以看到,输出结果为19条记录,与MySQL中的输出结果一致

  • 6.2. UserTest
public class UserTest {
    /**
     * 由于需要测试多个操作,而每个test中读取配置文件,获取SqlSessionFactory来创建对象SqlSession
     * 以及获取Dao的代理对象和释放资源都是共用的,所以可以封装在一个init方法中
     */
    private static InputStream is;
    private static SqlSession sqlSession;
    private static UserInterface userDao;

    @BeforeAll
    public static void init() throws IOException {
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        sqlSession = factory.openSession();
        userDao = sqlSession.getMapper(UserInterface.class);
    }
    // AfterAll和BeforeAll注解修饰的必须是静态方法
    @AfterAll
    public static void close() throws IOException{
        sqlSession.commit(); // 对于更新,插入,删除操作必须有提交事物才能正常运行
        sqlSession.close();
        is.close();
    }
    /**
     * 测试查询所有用户
     */
    @Test
    public void testFindAll(){
        List<User> list = userDao.findAll();
        for(User user : list){
            System.out.println(user);
        }
    }

    @Test
    public void testFindUserAndRole(){
        List<User> list = userDao.findUserAndRole();
        for(User user : list){
            System.out.println("User information:");
            System.out.println(user);
            System.out.println(user.getRoles());
        }
    }
}

请添加图片描述

同样,输出结果为19条记录,与MySQL中的输出一致,验证正确。

4. JNDI

Java Naming and Directory Interfacejava命名和目录接口,是SUN公司推出的一套规范,属于JavaEE技术之一,目的是模仿windows系统中的注册表。

注册表是Map结构,那么JNDI中的key和value对应为:

Key(绝对路径 + 名称)Value(JNDI中存储的是对象 Object)
路径是固定的,name是可以指定的存放对象可以指定,通过配置文件的方式来指定

最后

以上就是腼腆纸鹤为你收集整理的SSM框架学习----Mybatis(3)Mybatis框架学习----(3)的全部内容,希望文章能够帮你解决SSM框架学习----Mybatis(3)Mybatis框架学习----(3)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部