A mainstream framework: Mybatis frame (4) Mybatis connection pooling transaction, dynamic SQL statements, and multi-table operations

A: Mybatis connection pooling and practice depth

Here Insert Picture Description

1.Mybatis classification connection pool

Connection pooling can be reduced as we get technology to connect consumed time.
Mybatis uses its own connection pooling , by the master configuration file SqlMapConfig.xml dataSource tag , type attribute is represented using what manner the connection pool.

<!--            配置数据源(连接池)-->
            <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>

type attribute values:

POOLED using connection pooling traditional javax.sql.DataSource specification , mybatis there for the achievement of a standardized
using UNPOOLED traditional way to get connected , although achieve Javax.sql.DataSource interface, but did not use the pool of ideas . Here Insert Picture Description
JNDI service using JNDI service provided by technology , to get the DataSource object, different servers can get DataSource is not the same.

注意:如果不是web或者maven的war工程,是不能使用的。我们课程中使用的是**tomcat服务器,采用连接池就是dbcp连接池。**一般我们采用POOLED的方式,很多时候我们所说的数据源就是为了更好的管理数据库连接,也就是我们所说的连接池技术。

2.源码分析:

(1)Mybatis 中 DataSource 的存取
MyBatis 是通过工厂模式(factory)来创建数据源 DataSource 对象的,MyBatis 创建了 DataSource 实例后,会将其放到 Configuration对象内的 Environment 对象中, 供以后使用。
(2)Mybatis 中连接的获取过程分析
创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象来创建java.sql.Connection对象
只有在要用到的时候,才去获取并打开连接,当我们用完了就再立即将数据库连接归还到连接池中
Here Insert Picture Description
Here Insert Picture Description

3.Mybatis的事务控制:

JDBC 中事务可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法就可以开启事务,然后它是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚

所以由于Mybatis底层也是使用Connection,所以Mybatis事务提交方法底层也是使用JDBC的setAutoCommit()方法和rollback() commit()

/**
     * 用于在测试方法之后执行
     * @throws Exception
     */
    @After
    public void destory() throws Exception{
        //提交事务
        sqlSession.commit();

        //6.释放资源
        sqlSession.close();
        in.close();
    }

之前的 CUD 操作过程中,我们都要手动进行事务的提交,原因是 setAutoCommit()方法,在执行时它的值被设置为 false 了,所以我们在 CUD 操作中,必须通过 sqlSession.commit()方法来执行提交操作。

CUD 过程中必须使用 sqlSession.commit()提交事务?主要原因就是在连接池中取出的连接,都会将调connection.setAutoCommit(false)方法,这样我们就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit()方法实现事务提交。明白这一点后,我们现在一起尝试不进行手动提交,一样实现 CUD 操作。

使用factory.openSession(true); 此时事务就设置为自动提交了,但是分析知道这种方法只能提交一份。
Here Insert Picture Description

    /**
     * 用于在测试方法之前执行
     * @throws Exception
     */
    @Before
    public void init() throws Exception{
        //1.读取环境里面的总体配置文件(里面参数)
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产一个SqlSession对象(true为提交一个数据时才自动提交事务)
        sqlSession = factory.openSession(true);
        //4.使用SqlSession创建Dao接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    /**
     * 用于在测试方法之后执行
     * @throws Exception
     */
    @After
    public void destory() throws Exception{
        //提交事务
        //sqlSession.commit();

        //6.释放资源
        sqlSession.close();
        in.close();
    }

虽然这也是一种方式,但就编程而言,设置为自动提交方式为 false再根据情况决定是否进行提交,即这种方式更常用。

sqlSession = factory.openSession();
sqlSession.commit();
//6.释放资源
  sqlSession.close();
  in.close();

二:Mybatis 的动态SQL语句

Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL 是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。

(1)if 标签

标签的 test 属性中写的是对象的属性名(User里面的),如果是包装类的对象要使用 OGNL 表达式的写法。另外要注意 where 1=1 的作用~!

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

(2)where 标签

简化上面 where 1=1 的条件拼装,使得SQL语句看起来简单,我们可以采用标签来简化开发。

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

(3)foreach 标签

传入多个 id 查询用户信息,我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。
SQL 语句:
select 字段 from user where id in (?)
foreach 标签用于遍历集合,它的属性:

foreach标签的属性:

< foreach collection=“ids” open="and id in (" close=")" item = “userId” separator="," >

collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分

item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符,

<!--    根据queryvo中的Id集合实现查询用户列表-->
    <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 = "userId" separator=",">
                    #{userId}
                </foreach>
            </if>
        </where>
    </select>
</mapper>

三:Mybatis多表查询之 一对(一)多

1. 一对一查询:

方法一:(定义AccountUser来输出)

(1)定义账户信息的实体类():Account :id,uid(对应用户的id),money

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

(2)编写SQL语句:select account.*,user.username, user.address from account, user where account.uid = user.id
(查询所有的账号表中的信息和用户表中的用户名和地址,当账号对应的用户id与用户表中user对应)

(3)定义 AccountUser 类:为了能够封装上面 SQL 语句的查询结果(账号所有信息和用户的用户名和地址),定义 AccountUser 类中要包含账户信息同时还要包含用户信息,所以我们要在定义 AccountUser 类时可以继承 Account 类

public class AccountUser extends Account {

    private String username;
    private String address;

(4)定义层账户的持久层Dao接口:

public interface IAccountDao {

    /**
     * 查询所有商品,同时获取到当前帐户的所属用户信息
     * @return
     */
    List<Account> findAll();
    }

(5)定义 IAccountDao.xml 文件中的查询配置信息:

<select id="findAllAccout" resultType="accountuser">
        select a.*,u.username,u.address from account a , user u where u.id = a.uid
</select>

注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType
的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。

(6)创建 AccountTest 测试类执行即可。

总结:定义专门的 AccountUser 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普遍。

方法二:(resultType为Account来输出)以后最常用

账户实体类(Account)中包含用户

<!--    定义封装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>

(1)修改账号实体类(Account):

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    //从表实体应该包含一个主表实体的对象引用
    private User user;

(2)重新定义 IAccountDao.xml 文件 (重新写resultMap)

<!--    定义封装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" 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>

2. 一对多查询:(resultMap)

用户信息和他的账户信息为一对多关系(用户多个账号的查询),并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。
(1)编写 SQL 语句:
select u.*,a.id as aid,a.uid,a.money from account a, user u where u.id = a.id;
(查询所有用户表和账户的id 用户id money,从用户和账号表,当用户中id = 账号的id)

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

(3) User mapping file configuration persistence Dao (IUserDao.xml)

<resultMap type="user" id="userMap">
	<id column="id" property="id"></id>
	<result column="username" property="username"/>
	<result column="address" property="address"/>
	<result column="sex" property="sex"/>
	<result column="birthday" property="birthday"/>
	
	<!-- collection 是用于建立一对多中集合属性的对应关系ofType 用于指定集合元素的数据类型-->
	<collection property="accounts" ofType="account">
	<id column="aid" property="id"/>
	<result column="uid" property="uid"/>
	<result column="money" property="money"/>
	</collection>
</resultMap>

<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select account.*,user.username, user.address from account, user where account.uid = user.id
</select>

(4) preparation of test methods can

Four: Mybatis-many multi-table queries of

1. Implement Role to User-many

Relational model and user roles:
a user can have multiple roles, empathy: a role can give multiple usersHere Insert Picture Description
(1) business requirements and implement SQL:

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

(2) the preparation of the role of entity classes (Role):

public class Role implements Serializable {

    private Integer roleId;
    private String roleName;
    private String roleDesc;

    //多对多的关系映射:一个角色可以赋予多个用户
    private List<User> users;

(3) write Role persistence mapping configuration file (IRoleDao.xml):
resultMap for the role, which has a user

<!--定义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 column="id" property="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>

(4) Finally, write test classes

2. Implement User to Role-many

A user can have multiple roles, so that the user's relationship to the role still many relationship. So that we can think of many relationships User and Role, it can be broken down into two-to-many relationship to achieve.

Behind the SQL statement, the role of the entity class, persistence mapping configuration file empathy.

Published 47 original articles · won praise 18 · views 4877

Guess you like

Origin blog.csdn.net/qq_43605085/article/details/97131682