【教程】Mybatis 使用 -- Day 03【多表和事务】

佐手、 提交于 2020-01-31 21:09:39

文中代码托管在码云平台,点击进入

连接池

  • 连接池可以减少获取连接所消耗的时间和性能,就像一个容器,把连接初始化后放在这个容器里,当要用的时候就取出连接,可以说连接池就是一个用于存储连接的容器,该容器使用集合实现,该集合必须是线程安全的,不能两个线程拿到统一的连接,该集合还必须有队列的特性(先进先出
  • Mybatis连接池提供了 3 种方式的配置
    (1)配置的位置:主配置文件的dataSource标签,其type属性就是表示采用何种连接池,其值有三种:
    POOLED 采用传统的java.sql.DataSource规范中的连接,Mybatis中有针对规范的实现,每次使用的时候就从池中获取一个来用
    UNPOOLED 采用传统的获取连接的方式,虽然也实现了java.sql.DataSource接口,但是并没有使用池的思想,每次使用的时候就创建一个新的连接来用
    JNDI 采用服务器提供的JNDI技术实现来获取DATaSource对象,不同的服务器所能拿到的DataSource是不同的,如果不是web或者maven的var工程是不能使用的
    (2)POOLED工作流程:首先查看空闲区,如果空闲区还有连接就直接拿来用,如果空闲区中的连接已经没有了,那么会去查看活动区,看看活动区中的活动连接数量是否已经到达了最大数量,如果已经到达了最大数量,就判断这里面那个是最先进来的,也就是最老的,将其返回。
    实际开发中使用的是Tomcat服务器,采用的连接池就是dbcp连接池
    (3)JNDI:Java Naming and Directory Interface(Java命名和目录接口),是SUN公司提供的一种标准的Java命名系统接口,目的是模仿windows系统中的注册表,在服务器中注册数据源。
    ❤windows 注册表可以堪称是一个Map<key,calue>这样的结构,value在JNDI中存储的就是对象,key存的就是路径+名称
    ❤在Tomcat服务器启动时会准备一个这样的数据源结构,这样在web的项目中能使用到。
    ❤在web 项目中需要在JSP中才能使用,在test测试类中反而是用不了,因为test类中没有Tomcat服务器提供的数据源,因此无法使用,而在JSP中就可以使用。
    在这里插入图片描述

事务控制以及设计方法

事务的基本概念

  • 一项事务是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元,即事务可理解为逻辑上的一组操作,组成这组操作的各个单元,要么都成功,要么有一个失败就全部失败,例如银行转账
  • 事务的4个特性(原子性,一致性,隔离性,持久性)
  • 不考虑隔离产生的3个问题
    脏读即一个事务读取到另一个事务未提交的数据;
    不可重复读即一个事务读到了另一个事务已经提交的update数据,导致同一个事务中的多次查询结果不一致;
    虚读即一个事物读到了另一个事务已经提交的insert的数据,导致同一个事务中的多次查询结果不一致;
  • 事务的4种隔离级别(MySQL中默认的隔离级别 - repeatable read)

    在这里插入图片描述
    在这里插入图片描述

Mybatis中的事务操作

  • Mybatis 中是通过 SqlSession 对象的commit()rollback()实现事务的提交和回滚
  • 但是在实际开发中,除非每次对数据库的操作只有一个才会设置自动提交,否则都不会使用这种方式
// 3 使用工厂生产SqlSession对象
// 使用true参数可设置自动提交
sqlSession = factory.openSession(true);
// 5.2 提交事务,如果不提交会自动回滚
//设置了自动提交过后可不必再用代码手动提交
sqlSession.commit();

Mybatis 基于XML配置的动态SQL语句的使用

  • 使用了if where foreach sql标签的SQL语句后面的;号可以不加,由Mybatis自行判断条件决定是否结束语句
  • if 标签:根据不同的情况拼接不同的查询条件
    (1)在sql语句的地方,大小写无关紧要,但是涉及到实体类的,要在实体类中查找值的就要注意大小写问题,所以实体类和数据库表最好使用统一的名称
    (2)where后面的1=1是为了能够让if标签里面的语句能连上,避免产生错误
    (3)当需要多个条件组合判断时,不能用&&,要用and符号连接
    <!-- 根据条件查询 -->
    <select id="selectByCondition" resultType="user" parameterType="user">
        select * from user where 1=1
        <if test="userName!=NULL">
            and userName = #{userName}
        </if>
    </select>
    
  • where 标签
    (1)如果语句太多,频发使用where 1=1来连接会没那么美观,于是有了where标签,Mybatis会根据where中的条件判断是否需要加上条件
    <!-- where标签的使用 -->
    <select id="selectByCondition" resultType="user" parameterType="user">
        select * from user
        <where>
            <if test="userName!=NULL">
                and userName = #{userName}
            </if>
        </where>
    </select>
    
  • foreach 标签:类似于sql语句中的子查询
    (1)在QueryVo对象中添加属性,在接口类中添加方法,配置映射
    (2)collection 代表要便利的集合元素
    (3)open 代表语句开始部分 close 代表语句结束部分
    (4)item 代表遍历集合的每个元素,生成的变量名,跟后面#{}中括号里面的变量名要一致
    (5)separator 代表分隔符
    <!-- 根据QueryVo对象中的id集合实现查询用户列表 -->
    <select id="selectByCondition" resultType="user" parameterType="user">
        select * from user
        <where>
        	<if test="ids!=NULL and ids.size>0">
        		<foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
        			#{id}
        		</foreach>
        	</if>
        </where>
    </select>
    
  • sql 标签:抽取重复的SQL语句
    <sql id="defaultSql">
    	select * from user
    </sql>
    <select id="" resultType="">
    	<include refid="defaultSql"></include>
    </select>
    

多表查询

实现流程:
(1)建立两张表,用户表和账户表
(2)建立两个实体类,用户类和账户类
(3)通过外键让两张表具备一对多的关系,也要让两个实体类能体现出一对多的关系
(4)建立两个配置文件
(5)实现配置:当查询用户时,能够同时得到用户下包含的账户信息或查询账户时能够得到账户所属的用户信息

一对多

  • 一对多和多对一可以分开分析
    例如一个用户可以下多个订单,而多个订单属于同一用户
    对于用户是一对多,对于订单是多对一
  • 但是拿出每一个订单,都只能属于一个用户,所以 Mybatis 中把多对一看成了一对一
  • Mybatis 会自动识别出查询结果重复的地方并封装好
  • 当查询结果中出现重复的列名时,要在SQL语句中将重复了的列名改成唯一的别名
  • 实现步骤 查询用户下的所有账号
    (1)在从表中添加对方作为属性 private List<Account> accounts;
    (2)新建一个接口类的方法List<Account> findAll();
    (3)在配置文件中配置映射关系
    (4)调用查询
    <!-- 返回所有用户并获取其账户 -->
    <!-- 定义User的resultMap -->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"/>
        <result property="userName" column="userName"/>
        <result property="age" column="age"/>
        <!-- 配置user对象中accounts对象集合的映射 -->
        <collection property="accounts" ofType="com.learn.domain.Account">
            <id property="id" column="aid"/>
            <id property="id" column="money"/>
        </collection>
    </resultMap>
    <select id="findAllUserAndAccount" resultMap="userAccountMap">
        select * from user u left outer join account a on u.id = a.id
    </select>
//调用执行
public void testFindAll() {
	//获取所有用户
    List<User> users = userDao.findAll();
    for (User user : users) {
        System.out.println("-----每个用户的信息-----");
        //打印当前用户
        System.out.println(user);
        //打印当前用户的所有账号
        System.out.println(user.getAccounts());
    }
}

在这里插入图片描述

一对一

  • 实现步骤
    (1)在从表中添加对方作为属性 private User user;
    (2)新建一个接口类的方法List<Account> findAll();
    (3)在配置文件中配置映射关系
    (4)调用查询
<!-- 定义封装account和user的resultMao -->
<resultMap id="accountUserMap" type="account"><id property="id" column="aid"/>
    <result property="uid" column="uid"/>
    <result property="money" column="money"/>
    <!-- 一对一的关系映射,配置封装user的内容 -->
    <!-- 相当于配置account中刚添加的user对象 -->
    <association property="user" column="uid">
        <id property="id" column="id"/>
        <result property="userName" column="userName"/>
        <result property="age" column="age"/>
    </association>
</resultMap>
<!-- 查找全部账号并显示其用户的信息 -->
<select id="findAll" resultMap="accountUserMap">
    select a.id as aid, a.uid, a.money, u.* from user u, account a  where u.id = a.uid
</select>
//调用执行
public void testFindAll() {
    List<Account> accountList = accountDao.findAll();
    for (Account account : accountList) {
        System.out.println("-------------------------");
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

(5)输出结果
在这里插入图片描述

多对多

  • 例如:一个用户有多个角色,一个角色有多个用户
  • 实现步骤
    (1)建立两张表,并让两张表具有多对多的关系:需要有中间表,并且有两张表的主键
    (2)建立两个实体类,并让这两个实体能体现出多对多的关系:两个实体类都各种包含对方的对象集合引用
    (3)建立两个配置文件
    (4)实现配置,调用方法。实现当查询用户时能查询到该用户的所有角色,当查询角色时能够查询该角色下的所有用户
  • 注意:当sql语句很长需要换行的时候,或前或后最好加上一个空格,以免再执行的时候将头尾的词语连接在一起,没有空格作为分隔符,防止出错。

两个实体类

public class User{
	private List<Role> roles;
}
public class Role{
	private List<User> users;
}

配置文件

<!-- Role的配置文件 IRoleDao.xml -->
<resultMap id="roleMap" type="role">
	<id property="roleId" column="rid"/>
	<result property="roleName" column="role_name"/>
	<result property="roleDesc" column="role_desc"/>
	<collection property="users" ofType="user">
		<id property="id" column="id"/>
		<result property="userName" column="userName"/>
		<result property="age" column="age"/>
	</collection>
</resultMap>
<select id="fildAll" resultMap="roleMap">
	select u.*,r.id as rid,r.rolename,r.role_desc from role r  
	left outer join on r.id=ur.rid  
	left outer join user u on u.id=ur.uid
</select>
<!-- Role的配置文件 IUserDao.xml -->
<resultMap id="userMap" type="user">
	<id property="id" column="id"/>
	<result property="userName" column="userName"/>
	<result property="age" column="age"/>
	<collection property="roles" ofType="role">
		<id property="roleId" column="rid"/>
		<result property="roleName" column="role_name"/>
		<result property="roleDesc" column="role_desc"/>
	</collection>
</resultMap>
<select id="fildAll" resultMap="roleMap">
	select u.*,r.id as rid,r.rolename,r.role_desc from user u 
	left outer join on u.id=ur.uid  
	left outer join user u on r.id=ur.rid
</select>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!