如何扒开 SqlSession 的外衣

运维 数据库运维
如果我们配置的是MANAGED,会把事务交给容器来管理,比如JBOSS,Weblogic。因为我们是本地跑的程序,如果配置成MANAGED就会不有任何事务。

[[360740]]

老规矩,先上案例代码,我们按照这个案例一步一步的搞定Mybatis源码。

  1. public class MybatisApplication { 
  2.     public static final String URL = "jdbc:mysql://localhost:3306/mblog"
  3.     public static final String USER = "root"
  4.     public static final String PASSWORD = "123456"
  5.  
  6.     public static void main(String[] args) { 
  7.         String resource = "mybatis-config.xml"
  8.         InputStream inputStream = null
  9.         SqlSession sqlSession = null
  10.         try { 
  11.             inputStream = Resources.getResourceAsStream(resource); 
  12.             SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 
  13.             sqlSession = sqlSessionFactory.openSession(); 
  14.             UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 
  15.             System.out.println(userMapper.selectById(1)); 
  16.  
  17.         } catch (Exception e) { 
  18.             e.printStackTrace(); 
  19.         } finally { 
  20.             try { 
  21.                 inputStream.close(); 
  22.             } catch (IOException e) { 
  23.                 e.printStackTrace(); 
  24.             } 
  25.             sqlSession.close(); 
  26.         } 
  27.     } 

由于很多小伙伴在催,说Mybatis源码系列好像何时才有下文了,为此老田熬夜写了这篇。

 

继续开撸~~

  1. SqlSession sqlSession = sqlSessionFactory.openSession(); 

前面那篇文章已经分析了,这里的sqlSessionFactory其实就是DefaultSqlSessionFactory。

所以这里,我们就从DefaultSqlSessionFactory里的openSession方法开始。

  1. public class DefaultSqlSessionFactory implements SqlSessionFactory { 
  2.  
  3.   private final Configuration configuration; 
  4.  
  5.   public DefaultSqlSessionFactory(Configuration configuration) { 
  6.     this.configuration = configuration; 
  7.   } 
  8.   //创建session,这个方法直接调用本类中的另外一个方法 
  9.   @Override 
  10.   public SqlSession openSession() { 
  11.     return openSessionFromDataSource(configuration.getDefaultExecutorType(), nullfalse); 
  12.   } 
  13.   //其实是调用这个方法 
  14.   private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { 
  15.     Transaction tx = null
  16.     try { 
  17.       //对应xml标签<environments> ,这个在配置文件解析的时候就已经存放到configuration中了。 
  18.       final Environment environment = configuration.getEnvironment(); 
  19.       final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); 
  20.       tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); 
  21.       //创建一个executor来执行SQL   
  22.       final Executor executor = configuration.newExecutor(tx, execType); 
  23.       //这里也说明了,为什么我们代码里的SqlSession是DefaultSqlSession 
  24.       return new DefaultSqlSession(configuration, executor, autoCommit); 
  25.     } catch (Exception e) { 
  26.       closeTransaction(tx); // may have fetched a connection so lets call close() 
  27.       throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e); 
  28.     } finally { 
  29.       ErrorContext.instance().reset(); 
  30.     } 
  31.   } 
  32.    
  33.     private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) { 
  34.     if (environment == null || environment.getTransactionFactory() == null) { 
  35.       return new ManagedTransactionFactory(); 
  36.     } 
  37.     return environment.getTransactionFactory(); 
  38.   } 

这个方法中的主要内容有:

 

下面我们就来逐个攻破。

创建事务Transaction

事务工厂类型可以配置为JDBC类型或者MANAGED类型。

JdbcTransactionFactory生产JdbcTransaction。

ManagedTransactionFactory生产ManagedTransaction。

如果配置的JDBC,则会使用Connection对象的commit()、rollback()、close()方法来管理事务。

如果我们配置的是MANAGED,会把事务交给容器来管理,比如JBOSS,Weblogic。因为我们是本地跑的程序,如果配置成MANAGED就会不有任何事务。

但是,如果我们项目中是Spring集成Mybatis,则没有必要配置事务,因为我们会直接在applicationContext.xml里配置数据源和事务管理器,从而覆盖Mybatis的配置。

创建执行器Executor

调用configuration的newExecutor方法创建Executor。

  1. final Executor executor = configuration.newExecutor(tx, execType); 
  2. //Configuration中 
  3. public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 
  4.     executorType = executorType == null ? defaultExecutorType : executorType; 
  5.     executorType = executorType == null ? ExecutorType.SIMPLE : executorType; 
  6.     Executor executor; 
  7.     //第一步 
  8.     if (ExecutorType.BATCH == executorType) { 
  9.       executor = new BatchExecutor(this, transaction); 
  10.     } else if (ExecutorType.REUSE == executorType) { 
  11.       executor = new ReuseExecutor(this, transaction); 
  12.     } else { 
  13.       executor = new SimpleExecutor(this, transaction); 
  14.     } 
  15.     //第二步 
  16.     if (cacheEnabled) { 
  17.       executor = new CachingExecutor(executor); 
  18.     } 
  19.     //第三步 
  20.     executor = (Executor) interceptorChain.pluginAll(executor); 
  21.     return executor; 
  22.   } 

此方法分三个步骤。

第一步:创建执行器

Executor的基本类型有三种:

  1. public enum ExecutorType { 
  2.   SIMPLE, REUSE, BATCH 

SIMPLE为默认类型。

 

为什么要让抽象类BaseExecutor实现Executor接口,然后让具体实现类继承抽象类呢?

这就是模板方法模式的实现。

模板方法模式就是定义一个算法骨架,并允许子类为一个或者多个步骤提供实现。模板方法是得子类可以再不改变算法结构的情况下,重新定义算法的某些步骤。

抽象方法是在子类汇总实现的,每种执行器自己实现自己的逻辑,BaseExecutor最终会调用到具体的子类中。

抽象方法

  1. protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException; 
  2.  
  3. protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException; 
  4.  
  5. protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException; 
  6.  
  7. protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)  throws SQLException; 

第二步:缓存装饰

在上面代码中的第二步

  1. if (cacheEnabled) { 
  2.       executor = new CachingExecutor(executor); 

如果cacheEnabled=true,会用装饰器设计模式对Executor进行装饰。

第三步:插件代理缓存装饰完后,就会执行

  1. executor = (Executor) interceptorChain.pluginAll(executor); 

这里会对Executor植入插件逻辑。

比如:分页插件中就需要把插件植入的Executor

 

好了,到此,执行器创建的就搞定了。

创建DefaultSqlSession对象

把前面解析配置文件创建的Configuration对象和创建的执行器Executor赋给DefaultSqlSession中的属性。

  1. public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { 
  2.   this.configuration = configuration; 
  3.   this.executor = executor; 
  4.   this.dirty = false
  5.   this.autoCommit = autoCommit; 

到这里,SqlSession(DefaultSqlSession)对象就创建完毕。

总结

本文我们讲了如何创建SqlSession的几个步骤,最后我们获得一个DefaultSqlSession对象,里面包含了执行器Executor和配置对象Configuration。Executor是SQL的实际执行对象。Configuration里保存着配置文件内容。

本文源码分析的整个流程如下图:

本文转载自微信公众号「Java后端技术全栈」,可以通过以下二维码关注。转载本文请联系Java后端技术全栈公众号。

 

责任编辑:武晓燕 来源: Java后端技术全栈
相关推荐

2018-12-29 16:40:29

c语言编程语言指针

2021-05-25 07:59:59

Linux运维Linux系统

2012-08-30 09:17:28

Win 7Win 8操作系统

2021-08-10 12:05:19

Linuxworkqueue内核

2021-03-28 20:44:34

Kafka中间件MQ

2015-11-02 16:42:26

2020-12-01 10:27:39

区块链比特币

2013-10-10 09:24:34

2010-06-29 16:36:27

间谍木马恶意程序卡巴斯基

2020-12-09 09:22:53

GETPOSTWeb

2023-08-14 07:19:23

2017-09-07 07:20:10

2009-05-27 08:44:24

2014-11-13 17:48:21

2012-09-06 09:57:34

Saas云安全云计算

2022-11-26 10:14:48

Zookeepermybatisspring

2018-02-26 12:55:00

2022-11-15 07:35:50

Spring事件观察者模式

2020-02-06 10:20:19

硬件黑客技术

2011-09-12 15:50:15

联想笔记本
点赞
收藏

51CTO技术栈公众号