一、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. 是。等待最老的那个连接被归还后,重置后返回

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属性
Last modification:September 16th, 2019 at 08:38 am