Mybatis3源码分析(09)-SqlSession创建及简要说明
来源:互联网 发布:婴儿面膜淘宝授权书 编辑:程序博客网 时间:2024/05/17 03:18
SqlSession的使用简单
在得到SqlSessionFactory之后就可以创建一个SqlSession对象了,SqlSession对象的生命周期通常像如下方法所示:
public static void useSqlSession(SqlSessionFactory sqlSessionFactory){//在通过SqlSessionFactory获取一个SqlSessionSqlSession sqlSession=sqlSessionFactory.openSession();//用标准的try/catch/finally写法操作数据库try{//select//update等待操作//提交事务sqlSession.commit();}catch(Exception e){//出错,回滚事务sqlSession.rollback();}finally{//关闭sqlSession.close();}}
SqlSession接口定义
SqlSession定义了操作数据库的基本,这个Mybatis定义的用户层接口,使用该接口基本能满足用户(调用客户端)访问数据库的基本要求。由于接口定义的代码和注释比较多,这里就不贴了。其主要的方法如下:
- select类方法
- update/insert/delete方法
- commit()
- rollback()
- close()
如果了解过jdbc,肯定知道这些方法的用途!
SqlSession的创建过程
SqlSessionFactoryBuilder.build()方法会创建一个DefaultSqlSessionFactory对象
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }我们再来看DefaultSqlSessionFactory.openSession()方法
public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); }
//这个是最终创建SqlSession对象的方法,需要三个参数 //execType,这个示例使用的是configuration.getDefaultExecutorType(),即在Configuration默认配置的 //事务隔离等级,我们对数据库操作里一般都不会带这个属性,这个属性由数据库分配即可 //autoCommit:这个一般都是false,不然事务将没有意义 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); //获取一个事务工厂,这个也是在配置文件中配置的 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //通过事务工厂获取一个事务 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //根据execType获取一个Executor,这个稍后再详细讨论 final Executor executor = configuration.newExecutor(tx, execType); //创建SqlSession对象,这里创建的DefaultSqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }查看XMLConfigBuilder,可以看到默认的execType为SIMPLE
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));即为SimpleExecutor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { //默认 executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }而事务工厂则是通过xml里配置的,也就是JdbcTransactionFactory
<environment id="development"><!-- 开发环境 --> //事务工厂<transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment>再来看DefaultSqlSession中五个重要的方法
//执行查询语句 public <e> List<e> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); //交由executor处理 List<e> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
//操作update/insert/delete语句 public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); //交由executor处理 return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
//事务提交 public void commit(boolean force) { try { //交由executor处理 executor.commit(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
//事务回滚 public void rollback(boolean force) { try { //交由executor处理 executor.rollback(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
//关闭 public void close() { try { //交由executor处理 executor.close(isCommitOrRollbackRequired(false)); dirty = false; } finally { ErrorContext.instance().reset(); } }
在看executor的五个方法,可以整理出如下关系。
- DefaultSqlSession持有一个Executor对象,默认为SimpleExecutor,如果没有设置缓存的话。
- Executor持有一个Transaction对象
- DefaultSqlSession将select/update/insert/delete/commit/rollback/close交由Executor处理
- Executor又将commit/rollback/close方法交由Transaction处理
- DefaultSqlSession/Executor/Transaction对象都在DefaultSqlSessionFactory.openSessionFromDataSource方法中创建
SqlSessionFactory和SqlSession对象的范围和线程安全
如下是官方的说明
可以看到
- SqlSessionFactory可以在整个应用程序中保持一个单例,也就是它是线程安全的
- SqlSession则需要每个线程持有不同的对象,也就是说它不是线程安全的。
我们来看代码验证上面两点
DefaultSqlSessionFactory生成SqlSession的方法
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }上面代码采用了线程封闭的技术,也就是说将对象封闭在当前线程范围内,保证这些对象其他线程不可见,这样就保证了自已的线程安全性。但有一个例外:就是用到了Congifuration,它是对其他线程可见的,但这个Configuration对象是实际不可变的,所以DefaultSqlSessionFactory是线程安全的。线程封闭和实际不可变对象,这两个概念在<<java并发编程实践>>一书有详细的说明。
DefaultSqlSession线程安全性分析
DefaultSqlSession有一个Executor对象的引用,对其的访问也没有使用线程安全的机制:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); //这里没有用synchronized或lock List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }而再看BaseExecutor,这个并不是线程安全的
protected BaseExecutor(Configuration configuration, Transaction transaction) { this.transaction = transaction; this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>(); //前面对PerpetualCache已经分析过,PerpetualCache是用HashMap实现的,并不是线程安全的 this.localCache = new PerpetualCache("LocalCache"); this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache"); this.closed = false; this.configuration = configuration; this.wrapper = this; }再看他对cache的访问,也没用使用同步
try { queryStack++; //这里访问localCache.getObject()并没有使用同步 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; }根据以上分析:Executor不是一线程安全的,SqlSession对他的访问没有使用同步机制,所以SqlSession并不是线程安全的。
0 0
- Mybatis3源码分析(09)-SqlSession创建及简要说明
- Mybatis3源码分析(一):从sqlSession说起
- mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建
- speex源码分析-5-解码简要说明
- Mybatis3源码分析(03)-加载Configuration-ResultMap说明
- MyBatis3.3.0文档学习总结(三) --sqlSession深入源码
- Qemu-KVM虚拟机初始化及创建过程源码简要分析(一)
- Qemu-KVM虚拟机初始化及创建过程源码简要分析(二)
- Mybatis源码阅读之SqlSession创建
- MyBatis源码分析(三)-SqlSession理解
- Mybatis3源码分析(22)-总结
- nova创建虚拟机过程源码简要分析(一)
- SqlSession创建
- Redis源码简要分析
- uboot源码简要分析
- Redis源码简要分析
- Redis源码简要分析
- Redis源码简要分析
- Java线程
- Java性能优化——开篇
- HDMI信号通过FPGA传到 电脑.(输入卡的回显)
- Pandas常用操作
- 工作压力与情绪管理
- Mybatis3源码分析(09)-SqlSession创建及简要说明
- 关于web性能的思考与分享[08]雅虎14条军规—高性能网站建设指南
- 读《暗时间》(刘未鹏)若章有感
- Altium Designer(Protel)网络连接方式Port和Net Label详解
- 计算机图形学OpenGL——键盘控制三维汽车
- 在Unity中接入Xbox360手柄
- block
- JZOJ3226. 【HBOI2013】ALO
- Kaggle 机器学习竞赛冠军及优胜者的源代码汇总