Mybatis事务、连接池、动态SQL

事务

什么是事务?

事务的四大特性ACID

不考虑隔离性会产生的三个问题

解决方法:四种隔离级别

以上内容见Mybatis事务、连接池、动态SQL

mybatis通过sqlsession的commit和rollback实现提交和回滚

(通过Executor实现,Executor管理Connection)

如果使用SqlSessionFactory获取SqlSession的时候传递了参数:

factory.openSesison(true);

可以实现自动提交

连接池

我们在实际开发中都会使用连接池。
因为它可以减少我们获取连接所需时间。

mybatis的连接池:

POOLED UNPOOLED JNDI

  • POOLED: javax.sql.DataSource
  • UNPOOLED: 采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是并没有使用池的思想
  • JNDI: 采用服务器提供的JNDI实现,来获取DataSource对象,如果不是web或者maven的war工程,是不能使用的。

MyBatis是通过工厂模式来创建数据源 DataSource 对象的,MyBatis 定义了抽象的工厂接口:org.apache.ibatis.datasource.DataSourceFactory,通过其getDataSource()方法返回数据源DataSource。

上述三种不同类型的 type,则有对应的以下 dataSource 工厂:
+ POOLED :PooledDataSourceFactory
+ UNPOOLED :UnpooledDataSourceFactory
+ JNDI :JndiDataSourceFactory

Unpooled获取Connection

当\的type属性被配置成了”UNPOOLED”,MyBatis首先会实例化一个UnpooledDataSourceFactory工厂实例, 然后通过 getDataSource() 方法返回一个UnpooledDataSource 实例对象引用,我们假定为 dataSource。

使用 UnpooledDataSource 的 getConnection(),每调用一次就会产生一个新的 Connection 实例对象。

UnpooledDataSource 会做以下事情:
1. 初始化驱动:判断 driver 驱动是否已经加载到内存中,如果还没有加载,则会动态地加
载 driver 类,并实例化一个 Driver 对象,使用 DriverManager.registerDriver()方法将其注册到内存中,以供后续使用。
2. 创建 Connection 对象:使用DriverManager.getConnection()方法创建连接。
3. 配置 Connection 对象:设置是否自动提交 autoCommit 和隔离级别 isolationLevel。
4. 返回 Connection 对象。

Pooled获取Connection

PooledDataSource 将 java.sql.Connection 对象包裹成 PooledConnection 对象放到了 PoolState 类型的容器中维护。MyBatis 将连接池中的 PooledConnection 分为两种状态:空闲状态(idle)活动状态(active),这两种状态的 PooledConnection 对象分别被存储到PoolState 容器内的 idleConnections 和 activeConnections 两个 List 集合中:
+ idleConnections: 空闲(idle)状态 PooledConnection 对象被放置到此集合中,表示当前闲置的没有被使用的 PooledConnection 集合,调用 PooledDataSource 的 getConnection()方法时,会优先从此集合中取 PooledConnection 对象。 当用完一个 java.sql.Connection 对象时,MyBatis 会将其包裹成 PooledConnection 对象放到此集合中。
+ activeConnections: 活动(active)状态的PooledConnection对象被放置到名为activeConnections的ArrayList中,表示当前正在被使用的PooledConnection集合,调用PooledDataSource的getConnection()方法时,会优先从idleConnections 集合中取PooledConnection 对象,如果没有,则看此集合是否已满,如果未满,PooledDataSource会创建出一个 PooledConnection,添加到此集合中,并返回。

popConnection()方法到底做了什么:
1. 先看是否有空闲(idle)状态下的 PooledConnection 对象,如果有,就直接返回一个可用的 PooledConnection 对象;否则进行第 2 步。
2. 查看活动状态的 PooledConnection 池 activeConnections 是否已满;如果没有满,则创建一个新的 PooledConnection 对象,然后放到 activeConnections 池中,然后返回此PooledConnection 对象;否则进行第三步;
3. 看最先进入 activeConnections 池中的 PooledConnection 对象是否已经过期:如果已经过期,从 activeConnections 池中移除此对象,然后创建一个新的 PooledConnection 对象,添加到 activeConnections 中,然后将此对象返回;否则进行第 4 步。
4. 线程等待

当我们的程序中使用完 Connection 对象时,如果不使用数据库连接池,我们一般会调用connection.close()方法,关闭 connection 连接,释放资源。

我们希望当 Connection 使用完后,调用.close()方法,而实际上 Connection 资源并没有被释
放,而实际上被添加到了连接池中。

这里要使用代理模式,为真正的 Connection 对象创建一个代理对象,代理对象所有的方法都是调用相应的真正 Connection 对象的方法实现。当代理对象执行 close()方法时,要特殊处理,不调用真正 Connection 对象的 close()方法,而是将 Connection 对象添加到连接池中。

MyBatis 的 PooledDataSource 的 PoolState 内部维护的对象是 PooledConnection 类型的对象,而 PooledConnection 则是对真正的数据库连接 java.sql.Connection 实例对象的包裹器。

PooledConenction 实现了 InvocationHandler 接口,并且 proxyConnection 对象也是根据这个它来生成的代理对象。

动态SQL

根据不同的条件需要执行不同的SQL命令,称为动态SQL

官方文档参考:https://mybatis.org/mybatis-3/dynamic-sql.html

条件判断

在Mapper配置文件中,在sql中,可以写一些特殊的标签,其中的属性test用于判断条件,基于OGNL表达式

  • 例:
    <if test="title != null"> (逻辑运算用 and or)

if

从若干条件中分支

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

choose,when,otherwise

从若干条件中选择一项

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

where

若写了where但if中没有一个条件满足,则SQL语句会执行失败

  1. 当编写where标签时,如果内容中第一个是and去掉第一个and
  2. 如果\中有内容会生成where关键字,如果没有内容不生成where关键字
<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

set

动态更新(update)语句

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

foreach

对一个集合进行遍历

  1. 可以传入可迭代对象
  2. index是当前迭代的次数,item是当前迭代获取的元素
    • 当使用map对象的时候,index是键,item是值
<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

bind

bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

sql

SQL元素用来定义能够被其他语句引用的可重用的SQL语句块,例如:

<sql id="userColumns">id,username,password</sql>

这个SQL语句块能够被其他语句引用,配置代码如下:

<select id="selectUser" parameterType="int" resultType="hashmap">
select<include refid="userColumns"/>
    from userinfo where id = #{id}
</select>

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/mybatis%e4%ba%8b%e5%8a%a1%e3%80%81%e8%bf%9e%e6%8e%a5%e6%b1%a0%e3%80%81%e5%8a%a8%e6%80%81sql/