【MyBatis源码分析】insert方法、update方法、delete方法处理流程(上篇)

打开一个会话Session

前文分析了MyBatis将配置文件转换为Java对象的流程,本文开始分析一下insert方法、update方法、delete方法处理的流程,至于为什么这三个方法要放在一起说,是因为:

  1. 从语义的角度,insert、update、delete都是属于对数据库的行进行更新操作
  2. 从实现的角度,我们熟悉的PreparedStatement里面提供了两种execute方法,一种是executeUpdate(),一种是executeQuery(),前者对应的是insert、update与delete,后者对应的是select,因此对于MyBatis来说只有update与select

示例代码为这段:

 1 public long insertMail(Mail mail) {
 2     SqlSession ss = ssf.openSession();
 3     try {
 4         int rows = ss.insert(NAME_SPACE + "insertMail", mail);
 5         ss.commit();
 6         if (rows > 0) {
 7             return mail.getId();
 8         }
 9         return 0;
10     } catch (Exception e) {
11         ss.rollback();
12         return 0;
13     } finally {
14         ss.close();
15     }
16 }

首先关注的是第2行的代码,ssf是SqlSessionFactory,其类型是DefaultSqlSessionFactory,上文最后已经分析过了,这里通过DefaultSqlSessionFactory来打开一个Session,通过Session去进行CRUD操作。

看一下openSession()方法的实现:

1 public SqlSession openSession() {
2     return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
3 }

顾名思义,从DataSource中获取Session,第一个参数的值是ExecutorType.SIMPLE,继续跟代码:

 1 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
 2     Transaction tx = null;
 3     try {
 4       final Environment environment = configuration.getEnvironment();
 5       final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
 6       tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
 7       final Executor executor = configuration.newExecutor(tx, execType);
 8       return new DefaultSqlSession(configuration, executor, autoCommit);
 9     } catch (Exception e) {
10       closeTransaction(tx); // may have fetched a connection so lets call close()
11       throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
12     } finally {
13       ErrorContext.instance().reset();
14     }
15 }

第4行的代码,获取配置的环境信息Environment。

第5行的代码,从Environment中获取事物工厂TransactionFactory,由于中配置的是"JDBC",因此其真实类型是JdbcTransactionFactory,上文有说过。

第6行的代码,根据Environment中的DataSource(其实际类型是PooledDataSource)、TransactionIsolationLevel、autoCommit三个参数从TransactionFactory中获取一个事物,注意第三个参数autoCommit,它是openSession()方法中传过来的,其值为false,即MyBatis默认事物是不自动提交的

第7行的代码,实现跟一下:

 1 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
 2     executorType = executorType == null ? defaultExecutorType : executorType;
 3     executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
 4     Executor executor;
 5     if (ExecutorType.BATCH == executorType) {
 6       executor = new BatchExecutor(this, transaction);
 7     } else if (ExecutorType.REUSE == executorType) {
 8       executor = new ReuseExecutor(this, transaction);
 9     } else {
10       executor = new SimpleExecutor(this, transaction);
11     }
12     if (cacheEnabled) {
13       executor = new CachingExecutor(executor);
14     }
15     executor = (Executor) interceptorChain.pluginAll(executor);
16     return executor;
17 }

这里总结一下:

  • 根据ExecutorType获取一个执行器,这里是第10行的SimpleExecutor
  • 如果满足第12行的判断开启缓存功能,则执行第13行的代码。第13行的代码使用到了装饰器模式,传入Executor,给SimpleExecutor装饰上了缓存功能
  • 第15行的代码用于设置插件

这样就获取了一个Executor。最后将Executor、Configuration、autoCommit三个变量作为参数,实例化一个SqlSession出来,其实际类型为DefaultSqlSession。

 

insert方法执行流程

在看了openSession()方法知道最终获得了一个DefaultSqlSession之后,看一下DefaultSqlSession的insert方法是如何实现的:

 1 public int insert(String statement, Object parameter) {
 2     return update(statement, parameter);
 3 }

看到虽然调用的是insert方法,但是最终统一都会去执行update方法,delete方法也是如此,这个开头已经说过了,这里证明了这一点。

接着继续看第2行的方法实现:

 1 public int update(String statement, Object parameter) {
 2     try {
 3       dirty = true;
 4       MappedStatement ms = configuration.getMappedStatement(statement);
 5       return executor.update(ms, wrapCollection(parameter));
 6     } catch (Exception e) {
 7       throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
 8     } finally {
 9       ErrorContext.instance().reset();
10     }
11 }

第4行的代码根据statement从Configuration中获取MappedStatement,MappedStatement上文已经分析过了,存储在Configuration的mappedStatements字段中。

第5行的代码分为两部分,首先wrapCollection,顾名思义包装集合类,源码为:

 1 private Object wrapCollection(final Object object) {
 2     if (object instanceof Collection) {
 3       StrictMap map = new StrictMap();
 4       map.put("collection", object);
 5       if (object instanceof List) {
 6         map.put("list", object);
 7       }
 8       return map;
 9     } else if (object != null && object.getClass().isArray()) {
10       StrictMap map = new StrictMap();
11       map.put("array", object);
12       return map;
13     }
14     return object;
15 }

这里做了三层处理:

  • 如果参数是Collection(即集合)类型,放一个key为"collection"、value为参数的键值对
  • 如果参数是List类型,放一个key为"list"、value为参数的键值对
  • 如果参数是数组类型,放一个key为"array"、value为参数的键值对

将集合进行包装之后,就可以执行Executor的update方法了,Executor上面说了,是使用装饰器模式将SimpleExecutor加上了缓存功能的CacheExecutor,它的update方法实现为:

1 public int update(MappedStatement ms, Object parameterObject) throws SQLException {
2     flushCacheIfRequired(ms);
3     return delegate.update(ms, parameterObject);
4 }

第2行的代码是判断是否要求清缓存的,这里首先我们的示例配置文件mail.xml中没有配置,其次