MyBatis源码解析应用

1. 类讲解

1.1. org.mybatis.spring.SqlSessionFactoryBean

1.1.1. 回顾

前面简单的讲过了SqlSessionFactoryBean,用于构造SqlSessionFactory的FactoryBean,其实mapper.xml 文件的解析也是由这个类完成的;

1.1.2. afterPropertiesSet

最后一行代码是:this.sqlSessionFactory = buildSqlSessionFactory();其中分包括解析mapper.xml生成相关对象的逻辑;

1.1.3. buildSqlSessionFactory

构建SqlSessionFactory的核心业务逻辑:

       public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
            protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
                // ......
               if (!isEmpty(this.mapperLocations)) {
                     for (Resource mapperLocation : this.mapperLocations) {
                       if (mapperLocation == null) {
                         continue;
                       }
               
                       try {
                         XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                             configuration, mapperLocation.toString(), configuration.getSqlFragments());
                         xmlMapperBuilder.parse();
                       } catch (Exception e) {
                         throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                       } finally {
                         ErrorContext.instance().reset();
                       }
               
                       if (LOGGER.isDebugEnabled()) {
                         LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
                       }
                     }
                   } else {
                     if (LOGGER.isDebugEnabled()) {
                       LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
                     }
                   }
            }
       }  

这段解析mapper.xml的核心代码,主要业务逻辑交给XMLMapperBuilder来完成了,先是一个对象构造,然后一次parse()函数调用;

1.2. org.apache.ibatis.builder.xml.XMLMapperBuilder

看一下核心代码:

       public class XMLMapperBuilder extends BaseBuilder {
              public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
                this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
                    configuration, resource, sqlFragments);
              }
            
              private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
                super(configuration);
                this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
                this.parser = parser;
                this.sqlFragments = sqlFragments;
                this.resource = resource;
              }
            
              public void parse() {
                if (!configuration.isResourceLoaded(resource)) {
                  configurationElement(parser.evalNode("/mapper"));
                  configuration.addLoadedResource(resource);
                  bindMapperForNamespace();
                }
            
                parsePendingResultMaps();
                parsePendingCacheRefs();
                parsePendingStatements();
              }
       }  

上面这一部分是SqlSessionFactoryBean的buildSqlSessionFactory()方法调用的核心部分;

       public class XMLMapperBuilder extends BaseBuilder {
              private void configurationElement(XNode context) {
                   try {
                     String namespace = context.getStringAttribute("namespace");
                     if (namespace == null || namespace.equals("")) {
                       throw new BuilderException("Mapper's namespace cannot be empty");
                     }
                     builderAssistant.setCurrentNamespace(namespace);
                     cacheRefElement(context.evalNode("cache-ref"));
                     cacheElement(context.evalNode("cache"));
                     parameterMapElement(context.evalNodes("/mapper/parameterMap"));
                     resultMapElements(context.evalNodes("/mapper/resultMap"));
                     sqlElement(context.evalNodes("/mapper/sql"));
                     buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
                   } catch (Exception e) {
                     throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
                   }
               }
       }  

configurationElement函数里的“builderAssistant.setCurrentNamespace(namespace);”,其中,builderAssistant是org.apache.ibatis.builder.MapperBuilderAssistant的类对象;

       public class XMLMapperBuilder extends BaseBuilder {
               private void bindMapperForNamespace() {
                   String namespace = builderAssistant.getCurrentNamespace();
                   if (namespace != null) {
                     Class<?> boundType = null;
                     try {
                       boundType = Resources.classForName(namespace);
                     } catch (ClassNotFoundException e) {
                       //ignore, bound type is not required
                     }
                     if (boundType != null) {
                       if (!configuration.hasMapper(boundType)) {
                         // Spring may not know the real resource name so we set a flag
                         // to prevent loading again this resource from the mapper interface
                         // look at MapperAnnotationBuilder#loadXmlResource
                         configuration.addLoadedResource("namespace:" + namespace);
                         configuration.addMapper(boundType);
                       }
                     }
                   }
              }
       }  

bindMapperForNamespace()函数是XMLMapperBuilder类里需要重点讲的一个函数,“boundType = Resources.classForName(namespace);”,这一句是把namespace视为一个类的全限定名,试图加载一个这样的类:

1.3. org.apache.ibatis.session.Configuration

       public class Configuration {
            protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");

           public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
               if (validateIncompleteStatements) {
                   buildAllStatements();
               }
               return mappedStatements.get(id);
           }
       }  

Configuration类保存了所有Mybatis的配置信息,一般情况下Mybatis在运行过程中只会创建一个Configration对象,并且配置信息不能再被修改。

2. 一次基于SqlSession的查询过程

2.1. 代码

2.1.1 所使用的mapper代码片断

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="aa" >
 <resultMap id="BaseResultMap" type="com.jd.dis.entity.po.pay.PayOdr" >
   <id column="id" property="id" jdbcType="BIGINT" />
   <!--......-->
 </resultMap>
 <sql id="Base_Column_List" >
   <!--......-->
 </sql>
 <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
   select 
   <include refid="Base_Column_List" />
   from pay_odr
   where id = #{id,jdbcType=BIGINT}
 </select>
</mapper>

这里mapper的namespace随便设置一个值,它不是一个类的全限定名;

2.1.2 所使用的java代码片断

       @Service
       public class PayServiceImpl implements PayService {
       
           @Autowired
           private SqlSession sqlSession;
       
           @Override
           @UseDataSource("pay")
           public APIResponse<PayOdr> getPayOrder(Long id) {
               PayOdr payOrder = this.sqlSession.selectOne("aa.selectByPrimaryKey", id);
               return APIResponse.successResp(payOrder);
           }
       }

2.2. 执行查询过程

2.2.1 org.apache.ibatis.session.SqlSession

2.2.2 Service层使用SqlSession查询

wwimgurljdcpkug2020cn_200509000_BussServiceSelect.png
  在上面的业务代码里使用SqlSession查询的地方打断点,step into进去:

2.2.3 org.mybatis.spring.SqlSessionTemplate

wwimgurljdcpkug2020cn_200509000_SqlSessionTemplate_selectOne.png
  会调用SqlSessionTemplate中的查询方法,可以看到进入到了org.mybatis.spring.SqlSessionTemplate,它对应“mybatis-spring:1.3.2”(实际的jar包是:mybatis-spring-1.3.2.jar)。Spring喜欢对各种第三方框架封装出*Template,看到这样一个类,也不要奇怪。
  看看SqlSessionTemplate的部分代码:

       public class SqlSessionTemplate implements SqlSession, DisposableBean {
           private final SqlSessionFactory sqlSessionFactory;
           
           private final ExecutorType executorType;
           
           private final SqlSession sqlSessionProxy;
           
           private final PersistenceExceptionTranslator exceptionTranslator;
       
           public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
               this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
           }
           
           public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
               this(sqlSessionFactory, executorType,
                   new MyBatisExceptionTranslator(
                       sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
           }
           
           public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
             PersistenceExceptionTranslator exceptionTranslator) {
               notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
               notNull(executorType, "Property 'executorType' is required");
               
               this.sqlSessionFactory = sqlSessionFactory;
               this.executorType = executorType;
               this.exceptionTranslator = exceptionTranslator;
               this.sqlSessionProxy = (SqlSession) newProxyInstance(
                   SqlSessionFactory.class.getClassLoader(),
                   new Class[] { SqlSession.class },
                   new SqlSessionInterceptor());
           }

           @Override
           public <T> T selectOne(String statement, Object parameter) {
               return this.sqlSessionProxy.<T> selectOne(statement, parameter);
           }
       }

上面断点里可以看到,执行sql的动作是交给sqlSessionProxy来完成的,扫一下这个类里代码的其它部分,可以发现,实际上在这个spring封装的template中,所有的sql执行都是交给sqlSessionProxy来完成的,它在类里被定义为是final的;
  sqlSessionProxy虽然被定义为final的,但它实际代表的实现了SqlSession接口的类对象却是“经常性”变动的,只是在一次sql语句执行的过程中是不变的,所有不同的sql执行过程所使用的是同一个SqlSessionTemplate;
  sqlSessionProxy是一个代理类强转换过来的SqlSession接口的实现,不同线程的调用,此对象的引用会不同,调用代理类对象sqlSessionProxy所实现的SqlSession接口的方法,会引起对SqlSessionInterceptor类的invoke方法的调用;
  关注一下SqlSessionInterceptor:

2.2.3.1 org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor
       // ......
       import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
       import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
       import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;
       // ......
       public class SqlSessionTemplate implements SqlSession, DisposableBean {
         private class SqlSessionInterceptor implements InvocationHandler {
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             SqlSession sqlSession = getSqlSession(
                 SqlSessionTemplate.this.sqlSessionFactory,
                 SqlSessionTemplate.this.executorType,
                 SqlSessionTemplate.this.exceptionTranslator);
             try {
               Object result = method.invoke(sqlSession, args);
               if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                 // force commit even on non-dirty sessions because some databases require
                 // a commit/rollback before calling close()
                 sqlSession.commit(true);
               }
               return result;
             } catch (Throwable t) {
               Throwable unwrapped = unwrapThrowable(t);
               if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                 // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
                 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                 sqlSession = null;
                 Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
                 if (translated != null) {
                   unwrapped = translated;
                 }
               }
               throw unwrapped;
             } finally {
               if (sqlSession != null) {
                 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
               }
             }
           }
         }
       }

SqlSessionInterceptor可以认为是Spring整合MyBatis的核心实现,是SqlSessionTemplate能成为Spring容器管理的Bean并能对外提供线程安全的SqlSession服务的坚强依赖;

2.2.4 org.mybatis.spring.SqlSessionUtils

前面讲的SqlSessionTemplate类使用到了SqlSessionUtils中的静态方法,来简单看看:

       public final class SqlSessionUtils {
         public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
       
           notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
           notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
           //从从前线程的threadLocal 中获取sqlSessionHolder
           SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
       
           SqlSession session = sessionHolder(executorType, holder);
           if (session != null) {
             return session;
           }
       
           if (LOGGER.isDebugEnabled()) {
             LOGGER.debug("Creating a new SqlSession");
           }
       
           session = sessionFactory.openSession(executorType);
       
           registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
       
           return session;
         }

         private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
           SqlSession session = null;
           if (holder != null && holder.isSynchronizedWithTransaction()) {
             if (holder.getExecutorType() != executorType) {
               throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
             }
       
             //增加该holder,也就是同一事务中同一个sqlSessionFactory创建的唯一sqlSession,其引用数增加,被使用的次数增加
             holder.requested();
       
             if (LOGGER.isDebugEnabled()) {
               LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
             }
       
             session = holder.getSqlSession();
           }
           return session;
         }

         private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
             PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
           SqlSessionHolder holder;
           if (TransactionSynchronizationManager.isSynchronizationActive()) {
             Environment environment = sessionFactory.getConfiguration().getEnvironment();
       
             if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
               if (LOGGER.isDebugEnabled()) {
                 LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
               }
       
               holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
               //如果当前会话处在事务当中,则将holder 绑定到ThreadLocal 中
               //以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources中 
               TransactionSynchronizationManager.bindResource(sessionFactory, holder);
               //将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations 
               TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
               //设置当前holder和当前事务同步 
               holder.setSynchronizedWithTransaction(true);
               //holder 引用次数+1
               holder.requested();
             } else {
               if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
                 if (LOGGER.isDebugEnabled()) {
                   LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
                 }
               } else {
                 throw new TransientDataAccessResourceException(
                     "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
               }
             }
           } else {
             if (LOGGER.isDebugEnabled()) {
               LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
             }
           }
         }
       }

2.2.5 org.mybatis.spring.SqlSessionHolder

       public final class SqlSessionHolder extends ResourceHolderSupport {
             private final SqlSession sqlSession;
           
             public SqlSession getSqlSession() {
               return sqlSession;
             }
       }

一个持有SqlSession等对象实例的计数器,继承自ResourceHolderSupport;

2.2.6 org.springframework.transaction.support.ResourceHolderSupport

       public abstract class ResourceHolderSupport implements ResourceHolder {
           private boolean synchronizedWithTransaction = false;
       
           private boolean rollbackOnly = false;
       
           @Nullable
           private Date deadline;
       
           private int referenceCount = 0;
       
           private boolean isVoid = false;
       }

2.2.7 org.springframework.transaction.support.TransactionSynchronizationManager

       public abstract class TransactionSynchronizationManager {
           private static final ThreadLocal<Map<Object, Object>> resources =
                   new NamedThreadLocal<>("Transactional resources");
       
           private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
                   new NamedThreadLocal<>("Transaction synchronizations");
       
           private static final ThreadLocal<String> currentTransactionName =
                   new NamedThreadLocal<>("Current transaction name");
       
           private static final ThreadLocal<Boolean> currentTransactionReadOnly =
                   new NamedThreadLocal<>("Current transaction read-only status");
       
           private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
                   new NamedThreadLocal<>("Current transaction isolation level");
       
           private static final ThreadLocal<Boolean> actualTransactionActive =
                   new NamedThreadLocal<>("Actual transaction active");

           @Nullable
           public static Object getResource(Object key) {
               Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
               Object value = doGetResource(actualKey);
               if (value != null && logger.isTraceEnabled()) {
                   logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
                           Thread.currentThread().getName() + "]");
               }
               return value;
           }
       
           /**
            * Actually check the value of the resource that is bound for the given key.
            */
           @Nullable
           private static Object doGetResource(Object actualKey) {
               Map<Object, Object> map = resources.get();
               if (map == null) {
                   return null;
               }
               Object value = map.get(actualKey);
               // Transparently remove ResourceHolder that was marked as void...
               if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
                   map.remove(actualKey);
                   // Remove entire ThreadLocal if empty...
                   if (map.isEmpty()) {
                       resources.remove();
                   }
                   value = null;
               }
               return value;
           }

           // 当前线程事务同步机制是否是激活状态
           public static boolean isSynchronizationActive() {
               return (synchronizations.get() != null);
           }

       }
2.2.7.1 resources

private static final ThreadLocal<Map<Object, Object>> resources
  保存当前线程所用的数据操作资源,存储到Map里,当时是间接持有,来看一个程序运行中当前线程中map的数据:
wwimgurljdcpkug2020cn_200509000_TransactionSynchronizationManager_resources_map.png
  其中,有一个键值对,DefaultSqlSessionFactory对象为key,值为SqlSesseionHolder对象,通过SqlSesseionHolder可以获取SqlSession;

2.2.8 org.springframework.transaction.support.TransactionSynchronizationUtils

       public abstract class TransactionSynchronizationUtils {
           static Object unwrapResourceIfNecessary(Object resource) {
               Assert.notNull(resource, "Resource must not be null");
               Object resourceRef = resource;
               // unwrap infrastructure proxy
               if (resourceRef instanceof InfrastructureProxy) {
                   resourceRef = ((InfrastructureProxy) resourceRef).getWrappedObject();
               }
               if (aopAvailable) {
                   // now unwrap scoped proxy
                   resourceRef = ScopedProxyUnwrapper.unwrapIfNecessary(resourceRef);
               }
               return resourceRef;
           }

           private static class ScopedProxyUnwrapper {
               public static Object unwrapIfNecessary(Object resource) {
                   if (resource instanceof ScopedObject) {
                       return ((ScopedObject) resource).getTargetObject();
                   }
                   else {
                       return resource;
                   }
               }
           }
       }

2.2.9 org.springframework.core.InfrastructureProxy

       /**
        * Interface to be implemented by transparent resource proxies that need to be
        * considered as equal to the underlying resource, for example for consistent
        * lookup key comparisons. Note that this interface does imply such special
        * semantics and does not constitute a general-purpose mixin!
        *
        * <p>Such wrappers will automatically be unwrapped for key comparisons in
        * {@link org.springframework.transaction.support.TransactionSynchronizationManager}.
        *
        * <p>Only fully transparent proxies, e.g. for redirection or service lookups,
        * are supposed to implement this interface. Proxies that decorate the target
        * object with new behavior, such as AOP proxies, do <i>not</i> qualify here!
        *
        * @author Juergen Hoeller
        * @since 2.5.4
        * @see org.springframework.transaction.support.TransactionSynchronizationManager
        */
       public interface InfrastructureProxy {
       
       	/**
       	 * Return the underlying resource (never {@code null}).
       	 */
       	Object getWrappedObject();
       
       }

透明资源代理要实现的接口,为了被认为等同于底层资源,例如用于一致的查找的关键字比较。
  注意,这个接口意味着这样的特殊语义,并不构成一般用途的聚合!
  这种包装器将在TransactionSynchronizationManager中自动被拆箱来进行关键字比较。
  只有完全透明的代理,例如 对于重定向或服务查找,应该实现此接口。 使用新行为来装饰目标对象的代理(例如AOP代理)不适用于此处!
  也就是相当于对象本身等同于所封装的内部对象

2.2.10 org.apache.ibatis.session.SqlSessionFactory

       public abstract class TransactionSynchronizationManager {
         SqlSession openSession();
       
         SqlSession openSession(boolean autoCommit);
         SqlSession openSession(Connection connection);
         SqlSession openSession(TransactionIsolationLevel level);
       
         SqlSession openSession(ExecutorType execType);
         SqlSession openSession(ExecutorType execType, boolean autoCommit);
         SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
         SqlSession openSession(ExecutorType execType, Connection connection);
       
         Configuration getConfiguration();
       }

生产SqlSession对象的工厂接口;

2.2.11 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory

       public class DefaultSqlSessionFactory implements SqlSessionFactory {
         private final Configuration configuration;
       
         public DefaultSqlSessionFactory(Configuration configuration) {
           this.configuration = configuration;
         }
       
         @Override
         public SqlSession openSession(ExecutorType execType) {
           return openSessionFromDataSource(execType, null, 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);
             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();
           }
         }
       }

SqlSessionFactory接口的默认实现;

2.2.12 org.apache.ibatis.executor.Executor

       public interface Executor {
         ResultHandler NO_RESULT_HANDLER = null;
         int update(MappedStatement ms, Object parameter) throws SQLException;
         <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
         <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
         <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
         List<BatchResult> flushStatements() throws SQLException;
         void commit(boolean required) throws SQLException;
         void rollback(boolean required) throws SQLException;
         CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
         boolean isCached(MappedStatement ms, CacheKey key);
         void clearLocalCache();
         void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
         Transaction getTransaction();
         void close(boolean forceRollback);
         boolean isClosed();
         void setExecutorWrapper(Executor executor);
       }

2.2.13 org.apache.ibatis.executor.BaseExecutor

       public abstract class BaseExecutor implements Executor {
         protected Transaction transaction;
         protected Executor wrapper;
       
         protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
         protected PerpetualCache localCache;
         protected PerpetualCache localOutputParameterCache;
         protected Configuration configuration;
       
         protected int queryStack;
         private boolean closed;
           
         // ......
       }

2.2.14 org.apache.ibatis.executor.ReuseExecutor

       public class ReuseExecutor extends BaseExecutor {
           private final Map<String, Statement> statementMap = new HashMap<String, Statement>();
           
           private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
               Statement stmt;
               BoundSql boundSql = handler.getBoundSql();
               String sql = boundSql.getSql();
               if (hasStatementFor(sql)) {
                 stmt = getStatement(sql);
                 applyTransactionTimeout(stmt);
               } else {
                 Connection connection = getConnection(statementLog);
                 stmt = handler.prepare(connection, transaction.getTimeout());
                 putStatement(sql, stmt);
               }
               handler.parameterize(stmt);
               return stmt;
             } 
         // ......
       }

3. 事务回滚

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值