如何构建mybatis线程安全的sqlsession对象

如题所述

现象1:如果使用原生mybatis进行数据操作,那么必须按照以下方式使用:
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或者方法的范围,也就是所谓的线程不安全。
现象2:如果使用spring集成mybatis,官方提供了整和包mybatis-spring.jar,如果完成配置之后,使用方式及其简单,简单示例如下:
//注入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
问题1:
那么问题来了,为什么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
问题2:
然后又想到这样一个问题,虽然现在几乎所有项目都使用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
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

threads = new ArrayList

();
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;发现执行过程中存在连接并发的情况,但是执行之后全部释放掉了。
温馨提示:答案为网友推荐,仅供参考
第1个回答  2017-07-26
MyBatis是一款一流的支持自定义SQL、存储过程和高级映射的持久化框架。MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去设置参数和获取检索结果。MyBatis能够使用简单的XML格式或者注解进行来配置,能够映射基本数据元素、Map接口和POJOs(普通java对象)到数据库中的记录。简介每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpathresource),但你可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。MyBatis工作流程(1)加载配置并初始化触发条件:加载配置文件配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。(2)接收调用请求触发条件:调用Mybatis提供的API传入参数:为SQL的ID和传入参数对象处理过程:将请求传递给下层的请求处理层进行处理。(3)处理操作请求触发条件:API接口层传递请求过来传入参数:为SQL的ID和传入参数对象处理过程:(A)根据SQL的ID查找对应的MappedStatement对象。(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。(C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。(E)释放连接资源。(4)返回处理结果将最终的处理结果返回。

相关了解……

你可能感兴趣的内容

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 非常风气网