欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java 框架 04 — MyBatis_03(连接池和事务的深入、动态sql语句、多表查询_1对1_1对多_多对多)

程序员文章站 2022-05-23 17:58:37
...


欢迎访问笔者个人技术博客:http://rukihuang.xyz/

一、MyBatis连接池与事务的深入

1.1 MyBatis的连接池技术

1.1.1 连接池的分类

  1. UNPOOLED:不适用连接池的数据源
  2. POOLED:使用连接池的数据源
  3. JNDI:使用JNDI实现的数据源
  • MyBatis内部分别定义了实现java.sql.DataSource接口的UnPooledDataSourcePooledDataSource,来表示UNPOOLEDPOOLED类型的数据源。
  • 一般采用POOLED数据源

1.1.2 数据源的配置

  • 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>
    

1.1.3 MyBatis中DataSource的存取

  • MyBatis是通过工厂模式来创建数据源DataSource对象的,org.apache.ibatis.datasource.DataSourceFactory,通过其getDataSource()方法返回数据源DataSource

1.1.4 MyBatis中连接的获取过程分析

  1. 先找空闲池中有没有空闲的连接
    1. 有。直接返回
    2. 无。看活动池是否已经达到最大数量
      1. 否。在活动池创建一个新的连接来用
      2. 是。等待最老的那个连接被归还后,重置后返回

Java 框架 04 — MyBatis_03(连接池和事务的深入、动态sql语句、多表查询_1对1_1对多_多对多)

Java 框架 04 — MyBatis_03(连接池和事务的深入、动态sql语句、多表查询_1对1_1对多_多对多)

1.2 MyBatis的事务控制

  • MyBatis框架是对JDBC的封装,所以其本身也是用JDBC的setAutoCommit()方式来设置事务的提交方式。

    session = sessionFactory.openSession(true);//设置自动提交
    

二、MyBatis的动态sql语句

2.1 if标签

2.1.1 持久层DAO接口

    /**
     * 根据条件查用户
     * @param user
     * @return
     */
    List<User> findUserByCondition(User user);

2.1.2 持久层DAO映射配置

  • where 1=1的作用
    • 为了加 and 拼接 sql 语句。因为不能保证拼接的sql语句是否带and,索性加一个永真条件,是的后面添加的条件,都带有and,那就不用判断了。
  • 不是sql语句中的内容,严格区分大小写
  • test:判断条件字段是否为空
    <select id="findUserByCondition" resultMap="userMap" parameterType="user">
        select * from user where 1=1
        <if test="username != null">
            and username = #{username}
        </if>
        <if test="sex != null">
            and sex = #{sex}
        </if>
    </select>

2.2 where标签

  • 简化where 1=1的拼装,可以使用where标签

2.2.1 持久层DAO接口

    /**
     * 根据条件查用户
     * @param user
     * @return
     */
    List<User> findUserByCondition(User user);

2.2.2 持久层DAO映射配置

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

2.3 foreach标签

  • 进行范围查询时,对参数进行传递。如:

    SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
    

2.3.1 条件实体类QueryVo

  • 在条件实体类QueryVo中加入List集合用于封装参数
package com.ruki.domain;

import java.util.List;

public class QueryVo {
    private User user;
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

    public User getUser() {
        return user;
    }

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

2.3.2 持久层DAO接口

    /**
     * 子查询foreach标签
     * @param vo
     * @return
     */
    List<User> findUserInIds(QueryVo vo);

2.3.3 持久层DAO映射配置

  • collection:集合属性名
  • open:开始
  • close:结束
  • item:遍历出来的元素
  • separator:分隔符
  • #{id}:将值取出,与item的值对应
    <!-- foreach标签 -->
    <select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
        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>

2.4 简化编写的sql片段

  • 在持久层DAO映射配置中,有许多重复的查询语句select * from user,可将其提取出来。
  • sql
    • id:提取出来的sql片段名
  • include
    • refid:引用sql片段的id
    <!-- sql的抽取 -->
    <sql id="defaultUser">
        select * from user
    </sql>
    
    <!-- 查询所有用户 -->
    <select id="findAll" resultMap="userMap">
        <include refid="defaultUser"></include>
        <!-- select * from user -->
    </select>

三、MyBatis多表查询(一对一)

3.1 方式一:新建全属性实体类

  • 定义专门的pojo类作为输出类型,定义sql查询结果集所有的字段,并将字段封装到含有两部分属性的AccountUser实体类中,然后进行输出。

3.1.1 实体类

  • Account实体类
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
	//getter setter toString ...
}
  • User实体类
public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
	//getter setter toString ...
}

3.1.2 sql语句_等值连接(关键)

SELECT 
	account.*, 
	user.username, 
	user.address 
FROM 
	account, 
	user 
WHERE account.uid = user.id

3.1.3 AccountUser类(含全属性)

  • 继承Account类,并且包含账户信息的同时,还要包含用户信息
public class AccountUser extends Account {
    private String username;
    private String address;
	//getter setter toString ...
}

3.1.4 IAccountDao接口

    /**
     * 查询所有账户,以及对应的用户信息
     * @return
     */
    List<AccountUser> findAllAccountUser();

3.1.5 IAccountDao.xml

    <!-- 查询所有AccountUser -->
    <select id="findAllAccountUser" resultType="accountuser">
        select a.*, u.username, u.address from account a, user u where a.uid = u.id;
    </select>

3.1.6 测试

    @Test
    public void testFindAllAccountUser(){
        List<AccountUser> aus = accountDao.findAllAccountUser();
        for(AccountUser au : aus){
            System.out.println(au);
        }
    }

3.2 方式二:添加从属关系属性

  • 使用resultMap,定义专门的resultMap用于映射一对一查询结果。
  • 通过面向对象的(has a)关系可以得知,我们可以在Account类中加入一个User类的对象来代表这个账户是哪个用户的

3.2.1 修改Account类

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;

    //从表应包含主表的引用属性
    private User user;
	
	//getter setter toString
}

3.2.2 修改Account接口方法

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

3.2.3 重新定义AccountDao.xml

  • 一对一关系映射采用association标签
    • association
      • property:属性名
      • column:外键字段名
      • javaType:包装类型。(一般为全限定类名,但这里已经在SqlMapConfig.xml文件中设置了别名)
    <!-- 定义封装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" 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>

    <!-- 查询所有用户 -->
    <select id="findAll" resultMap="accountUserMap">
        select u.*, a.id aid, a.uid, a.money from account a, user u where u.id = a.uid
    </select>

3.2.4 测试

    /**
     * 测试查询所有账户
     */
    @Test
    public void testFindAll() throws IOException {
        List<Account> accounts = accountDao.findAll();
        for(Account account : accounts){
            System.out.println("---每一个account的信息");
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }

四、MyBatis多表查询(一对多)

4.1 编写SQL语句(左外连接)

select 
	u.*,
	a.id,
	a.uid,
	a.money
from 
	user u 
LEFT JOIN account a ON a.UID = u.id;

4.2 User类中添加List<account>

public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
	//主表包含从表的属性
    private List<Account> accounts;
    
	//getter setter toString
}

4.3 IUserDao接口

    /**
     * 查找所有
     * @return
     */
    List<User> findAll();

4.4 IUserDao.xml

  • 一对多采用collection标签
    • collection
      • property:属性名
      • ofType:泛型(一般为全限定类名,但这里已经在SqlMapConfig.xml文件中设置了别名)
    <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>
		
        <!-- collection是用于建立一对多中集合属性的对应关系 
			ofType用于指定集合元素的数据类型 -->
        <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 * from user u LEFT JOIN account a ON a.UID = u.id;
    </select>

4.5 测试

    @Test
    public void testFindAll() throws IOException {
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
            System.out.println(user.getAccounts());

        }
    }

五、MyBatis多表查询(多对多)

  • 通过中间表形成多对多关联

5.1 Role -> User 的多对多

5.1.1 编写sql语句(role为驱动表)

select 
	u.*, 
	r.ID as rid, 
	r.ROLE_NAME, 
	r.ROLE_DESC 
from 
	role r
left join 
	user_role ur on r.ID = ur.RID
left join 
	user u on ur.UID = u.id;

5.1.2 Role实体类

public class Role implements Serializable {

    private Integer roleId;
    private String roleName;
    private String roleDesc;
	//多对多的关系映射:一个角色可以赋予多个用户
    private List<User> users;
    
    //getter setter toString
}

5.1.3 IRoleDao接口

    /**
     * 查询所有role
     * @return
     */
    List<Role> findAll();

5.1.4 IRoleDao.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.ruki.dao.IRoleDao">
    <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 property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="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 join user_role ur
         on r.ID = ur.RID
         left join user u
         on ur.UID = u.id;
    </select>
</mapper>

5.1.5 测试

    @Test
    public void testFindAll() throws IOException {
        List<Role> roles = roleDao.findAll();
        for(Role role : roles){
            System.out.println("------------------------");
            System.out.println(role);
            System.out.println(role.getUsers());
        }
    }

5.2 User -> Role 的多对多

  • 原理同上,sql顺序改变一下,User类中添加roles属性