1
2
3
4
5
6
7
8
9
10
11
12
13
|
SqlSession
sqlSession
=
null
;
try
{
sqlSession
=
sqlSessionFactory
.
openSession
(
)
;
//namespace+id
sqlSession
.
insert
(
"cn.jarjar.dao.BlogMapper.insertBlog"
,
blog
)
;
sqlSession
.
commit
(
true
)
}
catch
(
Exception
e
)
{
e
.
printStackTrace
(
)
;
sqlSession
.
rollback
(
true
)
;
}
finally
{
sqlSession
.
close
(
)
;
}
|
也就是要像原始的java.sql.Connection对象一样,必须按照:新建连接->执行SQL->提交(查询不需要)->如果操作数据存在异常需要回滚->释放数据库连接。
注意第一点和最后一点,每个SqlSession新建之后必须释放,不然会造成数据库连接泄露的危险。也就是意味着SqlSession是个有状态的对象,是无法进行复用的,所以只能局限于request或者方法的范围,也就是所谓的线程不安全。
1
2
3
4
5
6
7
|
//注入spring中配置的SqlSessionTemplate对象,单例
@Resource
(
name
=
"sqlSessionTemplate"
)
public
SqlSessionTemplate
sqlSessionTemplate
;
public
void
saveTestTrans
(
)
{
this
.
sqlSessionTemplate
.
selectList
(
"testdomain.selectAnySql"
,
"select * from my_blog where id='1'"
)
;
}
|
这里的SqlSessionTemplate不仅是单例的,而且不需要手工新建和关闭SqlSession
那么问题来了,为什么mybatis-spring.jar中的SqlSessionTemplate可以被多个dao复用,而且不会造成数据连接泄露呢,并且还可以自动新建和释放数据库连接?
官方解答是因为SqlSessionTemplate是线程安全的,也就是确保每个线程使用的sqlSession的唯一并不互相冲突。
首先看了一下mybatis-spring的源码,发现SqlSessionTemplate是通过代理拦截和SqlSessionHolder实现的sqlsession线程安全和自动新建和释放连接的。
看构造函数函数中构建代理类,该代理类实现SqlSession接口,定义了方法拦截器,如果调用代理类实例中实现SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptor的invoke方法,这个方法中自动进行了SqlSession的自动请求和释放(如果不被spring托管则自己新建和释放sqlsession,如果被spring管理则使用SqlSessionHolder进行request和relase操作)
以下网址针对SqlSessionTemplate的线程安全特性进行了详细的探究:http://www.cnblogs.com/daxin/p/3544188.html
然后又想到这样一个问题,虽然现在几乎所有项目都使用spring作为java程序的基本框架,如果我不使用spring管理mybatis,仅仅使用原始的mybatis,怎么样才能构建一个和SqlSessionTemplate相似的对象呢?
首先想到必须使用java的treadLocal构建一个sqlsession的对象,如ThreadLocal sqlSession = new ThreadLocal()。
经过查找,发现mybatis自身就有这样一个类实现了类似的功能,类路径:org.apache.ibatis.session.SqlSessionManager,但是没有注释,可能存在mybatis-spring这种神器之后,mybatis放弃了对这个类的维护。
该类实现了SqlSessionFactory, SqlSession并且在其中定义了一个treadLocal的sqlssion对象,同时使用了代理拦截进行了sqlsession的自动管理,具体代码可以自己查阅,对于理解mybatis原理和java的代理机制很有帮助。
那么写个简单的程序验证一下SqlSessionManager是否真的可以保证线程安全和自动新建和释放sqlssion:
TestSqlManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
private
static
SqlSession
sqlSession
;
public
static
SqlSession
getSqlSessionTest
(
)
{
if
(
sqlSession
==
null
)
{
//构建使用的SqlSessionFactory
SqlSessionFactory
sqlSessionFactory
=
MyBatisUtil
.
getSqlSessionFactory
(
)
;
sqlSession
=
SqlSessionManager
.
newInstance
(
sqlSessionFactory
)
;
}
return
sqlSession
;
}
public
static
void
main
(
String
[
]
args
)
throws
InterruptedException
{
Run
run
=
new
Run
(
)
;
List
for
(
int
i
=
0
;
i
<
100
;
i
++
)
{
Thread
t
=
new
Thread
(
run
)
;
threads
.
add
(
t
)
;
System
.
out
.
println
(
"thread:{"
+
t
.
getName
(
)
+
"}, start"
)
;
t
.
start
(
)
;
}
for
(
Thread
t
:
threads
)
{
System
.
out
.
println
(
"thread:{"
+
t
.
getName
(
)
+
"},join"
)
;
t
.
join
(
)
;
}
}
|
我本机装的mysql,通过监控语句:select SUBSTRING_INDEX(host,’:’,1) as ip , count(*) from information_schema.processlist group by ip;
发现执行过程中存在连接并发的情况,但是执行之后全部释放掉了。