事务
什么是事务?
事务的四大特性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
当\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语句会执行失败
- 当编写where标签时,如果内容中第一个是and去掉第一个and
- 如果\
中有内容会生成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
对一个集合进行遍历
- 可以传入可迭代对象
- 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/