`
longgangbai
  • 浏览: 7258414 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

ibatis_2.3源码中insert&update&query垂直浅析

阅读更多

题记:

    废话不多,慢慢写吧

 

 

写的可能有点乱,但思路是按照insert操作来写的。

 

 

 

问题1:线程安全

    在整个app多线程中,可以共用一个SqlMapClient来执行操作。原理是利用了ThreadLocal,ThreadLocal实际上是不同的Thread以ThreadLocal自身对象为key的一份ThreadLocalMap拷贝,每个thread创建了各自的Map,这个Map的key就是ThreadLocal的hashcode,所以,同一个ThreadLocal存储的不同value会存储在Map中table数组的同一个index上,并组成了链表。不同的ThreadLocal则存储在Thread中不同的index处,这个Map也不可能大小总为1;

 

    所以为了用ThreadLocal,我想一般需要增加一个胶合层来做一些工作,看上去像门面模式。在ibatis中,这个类就是SqlMapClientImpl,如下:

 

Java代码  收藏代码
  1. public class SqlMapClientImpl implements ExtendedSqlMapClient {  
  2.   
  3.   /** 
  4.    * Delegate for SQL execution 
  5.    */  
  6.   public SqlMapExecutorDelegate delegate;  
  7.   protected ThreadLocal localSqlMapSession = new ThreadLocal();  
  8.   
  9.  protected SqlMapSessionImpl getLocalSqlMapSession() {  
  10.     SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get();  
  11.     if (sqlMapSession == null || sqlMapSession.isClosed()) {  
  12.       sqlMapSession = new SqlMapSessionImpl(this);  
  13.       localSqlMapSession.set(sqlMapSession);  
  14.     }  
  15.     return sqlMapSession;  
  16.   }  
  17. ...  
  18. }  

 

 

 这里看到,真正做事的是SqlMapExecutorDelegate这个类,并且还注意到,这里其实把SqlMapExecutorDelegate这个实现类传递给了sqlMapSession,又封装了一层。

 

 

问题2:Session上下文(事务控制)

    不是很深刻理解SessionScope,但从SessionScope的成员变量上看,每个SessionScope管理自己的事务,在这个事务中执行过的所有sql语句。

    去看了看SqlMapSessionImpl ,并且集成了SqlMapExecutor接口,这里应该是代理模式,因为SqlMapExecutorDelegate和SqlMapSessionImpl的接口是“相同”,如下:

 

Java代码  收藏代码
  1. public class SqlMapSessionImpl implements SqlMapSession {  
  2.   
  3.   protected SqlMapExecutorDelegate delegate;  
  4.   protected SessionScope session;  
  5.   protected boolean closed;  
  6.   
  7.   /** 
  8.    * Constructor 
  9.    * 
  10.    * @param client - the client that will use the session 
  11.    */  
  12.   public SqlMapSessionImpl(ExtendedSqlMapClient client) {  
  13.     this.delegate = client.getDelegate();  
  14.     this.session = this.delegate.popSession();  
  15.     this.session.setSqlMapClient(client);  
  16.     this.session.setSqlMapExecutor(client);  
  17.     this.session.setSqlMapTxMgr(client);  
  18.     this.closed = false;  
  19.   }  

 

 

 

多出了个SessionScope,这个SessionScope类似装饰模式,而SessionScope应该是上下文的主要工作类了。SessionScope被SqlMapExecutorDelegate中的一个pool维护着,到此,可以看到很多操作都包装在了SqlMapExecutorDelegate类里。

  SessionScope对象池用Collections.synchronizedList(new ArrayList(size));线程安全的List来存储,因为所有SessionScope都公用同一个SqlMapExecutorDelegate。

 

总结下关系是:SqlMapSessionImpl 封装了主要工作类SessionScope 和 接收 外部引用SqlMapExecutorDelegate,SessionScope正是由SqlMapExecutorDelegate的对象池来管理。SqlMapSessionImpl只是做了代理工作。

    目前我感觉不用SqlMapSessionImpl代理类,直接用delegate.popSession();来获得一个SessionScope也应该是可以的,为什么要这样设计呢?理由在哪里呢?

 

 

问题3:insert操作

    我们继续,看到每个线程把持着自己的SqlMapSessionImpl,并且在SqlMapSessionImpl初始化的时候从对象池中的List拿到了一个SessionScope,SessionScope里包含了Transaction,接着就可以进行数据库操作了就看SqlMapSessionImpl 的insert方法:

 

Java代码  收藏代码
  1. public Object insert(String id, Object param) throws SQLException {  
  2.   return delegate.insert(session, id, param);  
  3. }  

 

 

 看到,insert操作回传给了公共的SqlMapExecutorDelegate,并传递了SessionScope来区分。

 

insert代码如下:

 

Java代码  收藏代码
  1. public Object insert(SessionScope session, String id, Object param) throws SQLException {  
  2.    Object generatedKey = null;  
  3.   
  4.    MappedStatement ms = getMappedStatement(id);  
  5.    Transaction trans = getTransaction(session);  
  6.    boolean autoStart = trans == null;  
  7.   
  8.    try {  
  9.      trans = autoStartTransaction(session, autoStart, trans);  
  10.   
  11.      SelectKeyStatement selectKeyStatement = null;  
  12.      if (ms instanceof InsertStatement) {  
  13.        selectKeyStatement = ((InsertStatement) ms).getSelectKeyStatement();  
  14.      }  
  15.   
  16.      if (selectKeyStatement != null && !selectKeyStatement.isAfter()) {  
  17.        generatedKey = executeSelectKey(session, trans, ms, param);  
  18.      }  
  19.   
  20.      RequestScope request = popRequest(session, ms);  
  21.      try {  
  22.        ms.executeUpdate(request, trans, param);  
  23.      } finally {  
  24.        pushRequest(request);  
  25.      }  
  26.   
  27.      if (selectKeyStatement != null && selectKeyStatement.isAfter()) {  
  28.        generatedKey = executeSelectKey(session, trans, ms, param);  
  29.      }  
  30.   
  31.      autoCommitTransaction(session, autoStart);  
  32.    } finally {  
  33.      autoEndTransaction(session, autoStart);  
  34.    }  
  35.   
  36.    return generatedKey;  
  37.  }  

 

 

     id是XML中定义SQL语句的名称,param是参数。用getMappedStatement方法,根据id来获取需要执行的MappedStatement,XML文件中所有定义的Statement都存储在一个叫mappedStatements的HashMap中,并以XML定义的名称(即Id)为key,MappedStatement对象为value,这样根据名称就取到了对应的MappedStatement。

    然后根据session来获取Transaction,如下:

 

 

public Transaction getTransaction(SessionScope session) { return session.getTransaction(); }

 

 

     很显然,在session初始化的时候就已经定义好了一个Transaction,但这个Transaction不一定有值。

 

    拿到Transaction后,判断是否自动提交事务,如果是的,则开始session中的事务,个人觉得一个好纠结的函数,希望有大大来解释下:

 

Java代码  收藏代码
  1. protected Transaction autoStartTransaction(SessionScope session, boolean autoStart, Transaction trans) throws SQLException {  
  2.     Transaction transaction = trans;  
  3.     if (autoStart) {  
  4.       session.getSqlMapTxMgr().startTransaction();  
  5.       transaction = getTransaction(session);  
  6.     }  
  7.     return transaction;  
  8.   }  

 

 

 

   然后看到session.getSqlMapTxMgr().startTransaction();这个方法,根据之前的分析,SqlMapSessionImpl管理这一个SessionScope和一个SqlMapExecutorDelegate,而getSqlMapTxMgr()实际上指向了SqlMapSessionImpl,而SqlMapSessionImpl只是一个代理类,这时SqlMapSessionImpl的startTransaction()方法实际指向了SqlMapExecutorDelegate的startTransaction(Session)方法,因为SqlMapSessionImpl把持这SessionScope和SqlMapExecutorDelegate对象,所以很轻松把当前SqlMapSessionImpl中的SessionScope为参数调用SqlMapExecutorDelegate的startTransaction(Session)了,最后SqlMapExecutorDelegate调用自己的事务管理类TransactionManager把Session参数传进去,并做事务操作,startTransaction这时新建立一个Transaction对象,并把new出来的Transaction对象赋值给Session,且事务池+1,设定状态。

 

    总结一下:SqlMapSessionImpl作为了SessionScope和SqlMapExecutorDelegate中间沟通的桥梁。

 

    executeUpdate被封装在GeneralStatement对象里面,并其子类有DeleteStatement,SelectStatement,InsertStatement等,所有的SQL解析,执行,返回结果集都集中在这里。

 

    最后提交事务:autoCommitTransaction(session, autoStart);如果autoStart为true,则执行session中的commitTransaction()方法,类似startTransaction()一样实际调用SqlMapExecutorDelegate的commitTransaction()方法,利用TransactionManager类的public void commit(SessionScope session)方法提交事务,如下:

 

Java代码  收藏代码
  1. public void commit(SessionScope session) throws SQLException, TransactionException {  
  2.    Transaction trans = session.getTransaction();  
  3.    TransactionState state = session.getTransactionState();  
  4.    if (state == TransactionState.STATE_USER_PROVIDED) {  
  5.       } else if (state != TransactionState.STATE_STARTED && state != TransactionState.STATE_COMMITTED ) {  
  6.      throw new TransactionException("TransactionManager could not commit.  No transaction is started.");  
  7.    }  
  8.    if (session.isCommitRequired() || forceCommit) {  
  9.      trans.commit();  
  10.      session.setCommitRequired(false);  
  11.    }  
  12.    session.setTransactionState(TransactionState.STATE_COMMITTED);  
  13.  }  

 

 

    总结一下:所有的事务控制都转接给公共SqlMapExecutorDelegate类中的TransactionManager类操作,TransactionManager自己维护着一个事务池,事务的新增和销毁都在这里集中管理。

 

 

问题4:执行SQL语句

    之前分析到SQL语句的校验、执行和结果集的返回都封装在了XXXStatement类中,BaseStatement是个抽象类,也是个很重要的类,GeneralStatement是extend这个抽象类的基础类。

其中执行sql的语句为:

 

 

Java代码  收藏代码
  1. ms.executeUpdate(request, trans, param);  

 

    request是从request对象池中取到的(request对象池?what's that?),trans只是用来传递Connection的,param是参数对象了。

 

    首先建立点关键日志信息:ErrorContext;

    然后校验param参数,通过GeneralStatement的validateParameter方法,不过我没看明白;

    然后通过Sql接口的getParameterMap方法取sql语句中映射的元素,返回ParameterMap;

    取返回的元素,通过getResultMap方法,和上面类似,返回ResultMap;所以可以看到ParameterMap和ResultMap这两组类是存储ibatis的映射的,进一步发现ParameterMap和ResultMap其实在初始化XML的时候就解析好了,并储存在这些BaseStatement里,一条sql语句一个BaseStatement对象,而这些BaseStatement对象缓存SqlMapExecutorDelegate里的一个HashMap里,要用的时候就拿出来,并非用时再临时创建。另外这里的Sql接口被设计成为支持动态sql和静态sql,接收的参数为RequestScopes(存储了ParameterMap和ResultMap)、parameterObject。

    然后开始取parameterObject的值(也就是param对象里的成员变量值),通过ParameterMap的getParameterObjectValues方法,ParameterMap内部则调用DataExchange的getData方法(不同的param,比如JavaBean,Map,List需要不同的取值方式),所以每个ParameterMap都指向一个自己特定的DataExchange方式,有ComplexDataExchange,DomDataExchange,JavaBeanDataExchange,ListDataExchange等,用来支持XML中指定不同的parameter。每个DataExchange处理方式当然就不一样了,但都返回一个Object []数组。

 

    然后对于动态sql,需要重新构造一个新的sql,调用String sqlString = sql.getSql(request, parameterObject);方法;

    然后开始执行sql,调用rows = sqlExecuteUpdate(request, trans.getConnection(), sqlString, parameters);实际上是调用SqlExecutor这个实现类来执行所有sql的。

      首先构造PreparedStatement,先从SessionScope中取是否有缓存,设置PreparedStatement超时时间,然后通过ParameterMap的setParameters方法将之前取的parameterObject值set到PreparedStatement里面去。

    最后执行execute,取到返回行数。

  最后清理下现场。其中有个executeListeners,没看太明白。

 

    总结一下:在ibatis初始化的时候,也就是解析XML配置文件的时候,就对XML中每一个sql语句创建了一个BaseStatement对象存储在SqlMapExecutorDelegate的一个HashMap里面,在创建BaseStatement的时候还额外创建了ParameterMap和ResultMap对象,ParameterMap是用来管理sql中的输入参数的,根据parameterClass的不同,创建用于处理不同的DomDataExchange,而ResultMap则是管理sql中的输出结果的。在执行insert操作的时候,只需要从HashMap中取到对应的BaseStatement,并校验外部传来的param,获取param里的值,并重新构造一个sql语句,然后创建PreparedStatement,往PreparedStatement中注入这些values,最后执行,整个过程就基本执行了。

 

 整段代码如下:

 

 

Java代码  收藏代码
  1. public int executeUpdate(RequestScope request, Transaction trans, Object parameterObject)  
  2.      throws SQLException {  
  3.    ErrorContext errorContext = request.getErrorContext();  
  4.    errorContext.setActivity("preparing the mapped statement for execution");  
  5.    errorContext.setObjectId(this.getId());  
  6.    errorContext.setResource(this.getResource());  
  7.   
  8.    request.getSession().setCommitRequired(true);  
  9.   
  10.    try {  
  11.      parameterObject = validateParameter(parameterObject);  
  12.   
  13.      Sql sql = getSql();  
  14.   
  15.      errorContext.setMoreInfo("Check the parameter map.");  
  16.      ParameterMap parameterMap = sql.getParameterMap(request, parameterObject);  
  17.   
  18.      errorContext.setMoreInfo("Check the result map.");  
  19.      ResultMap resultMap = sql.getResultMap(request, parameterObject);  
  20.   
  21.      request.setResultMap(resultMap);  
  22.      request.setParameterMap(parameterMap);  
  23.   
  24.      int rows = 0;  
  25.   
  26.      errorContext.setMoreInfo("Check the parameter map.");  
  27.      Object[] parameters = parameterMap.getParameterObjectValues(request, parameterObject);  
  28.   
  29.      errorContext.setMoreInfo("Check the SQL statement.");  
  30.      String sqlString = sql.getSql(request, parameterObject);  
  31.   
  32.      errorContext.setActivity("executing mapped statement");  
  33.      errorContext.setMoreInfo("Check the statement or the result map.");  
  34.      rows = sqlExecuteUpdate(request, trans.getConnection(), sqlString, parameters);  
  35.   
  36.      errorContext.setMoreInfo("Check the output parameters.");  
  37.      if (parameterObject != null) {  
  38.        postProcessParameterObject(request, parameterObject, parameters);  
  39.      }  
  40.   
  41.      errorContext.reset();  
  42.      sql.cleanup(request);  
  43.      notifyListeners();  
  44.      return rows;  
  45.    } catch (SQLException e) {  
  46.      errorContext.setCause(e);  
  47.      throw new NestedSQLException(errorContext.toString(), e.getSQLState(), e.getErrorCode(), e);  
  48.    } catch (Exception e) {  
  49.      errorContext.setCause(e);  
  50.      throw new NestedSQLException(errorContext.toString(), e);  
  51.    }  
  52.  }  

 

 

 

Update

     update跟insert就都是从一个娘胎肚子里出来的,只是SqlMapExecutorDelegate的update方法里,没有executeSelectKey的操作。

 

Query

     query就复杂点,因为要处理skipResults,maxResults,以及结果集,所以多出了RowHandler。

 

转载自:

http://www.iteye.com/topic/832157

 

 

Ibatis做为一个半自动化的Orm框架有他的缺点和优点。在这里我就不宽泛的说这些了。就说说为什么SqlMapClient是线程安全的,他是怎么实现的。 

提出问题: 

Java代码  收藏代码
  1. private static SqlMapClient sqlMapper;  
  2.   
  3.   /** 
  4.    * It's not a good idea to put code that can fail in a class initializer, 
  5.    * but for sake of argument, here's how you configure an SQL Map. 
  6.    */  
  7.   static {  
  8.     try {  
  9.       Reader reader = Resources.getResourceAsReader("com/mydomain/data/SqlMapConfig.xml");  
  10.       sqlMapper = SqlMapClientBuilder.buildSqlMapClient(reader);  
  11.       reader.close();   
  12.     } catch (IOException e) {  
  13.       // Fail fast.  
  14.       throw new RuntimeException("Something bad happened while building the SqlMapClient instance." + e, e);  
  15.     }  
  16.   }  



这是一段ibatis simple工程的代码,大家都能看明白这是一个单例,只有一个SqlMapClient对象存在,在多线程的情况下,SqlMapClient是怎么解决事务隔离呢,怎么共享资源的呢? 

一、 SqlMapClient是怎么被创建的 
打开SqlMapClientBuilder发现buildSqlMapClien一句话 

Java代码  收藏代码
  1. public static SqlMapClient buildSqlMapClient(Reader reader) {  
  2. //    return new XmlSqlMapClientBuilder().buildSqlMap(reader);  
  3.             return new SqlMapConfigParser().parse(reader);  
  4.   }  



我们顺着这条线一路看下去 
SqlMapConfigParser类的做了两件事把reader交个一个NodeletParser去解析reader(也就是我们的配置文件),在一个就是XmlParserState的一个属性产生一个SqlMapClient对象 

Java代码  收藏代码
  1. public class SqlMapConfigParser {  
  2.   
  3.   protected final NodeletParser parser = new NodeletParser();  
  4.   private XmlParserState state = new XmlParserState();  
  5. public SqlMapClient parse(Reader reader) {  
  6.     try {  
  7.       usingStreams = false;  
  8.   
  9.       parser.parse(reader);  
  10.       return state.getConfig().getClient();  
  11.     } catch (Exception e) {  
  12.       throw new RuntimeException("Error occurred.  Cause: " + e, e);  
  13.     }  
  14.   }  



打开NodeletParser的parse方法,我们发现他就是解析xml配置文件的 

Java代码  收藏代码
  1. public void parse(Reader reader) throws NodeletException {  
  2.     try {  
  3.       Document doc = createDocument(reader);  
  4.       parse(doc.getLastChild());  
  5.     } catch (Exception e) {  
  6.       throw new NodeletException("Error parsing XML.  Cause: " + e, e);  
  7.     }  
  8.   }  



最后这些文件被分门别类的放在了XmlParserState的这些属性里 

Java代码  收藏代码
  1. private SqlMapConfiguration config = new SqlMapConfiguration();  
  2.   
  3.   private Properties globalProps = new Properties();  
  4.   private Properties txProps = new Properties();  
  5.   private Properties dsProps = new Properties();  
  6.   private Properties cacheProps = new Properties();  
  7.   private boolean useStatementNamespaces = false;  
  8.   private Map sqlIncludes = new HashMap();  
  9.   
  10.   private ParameterMapConfig paramConfig;  
  11.   private ResultMapConfig resultConfig;  
  12.   private CacheModelConfig cacheConfig;  
  13.   
  14.   private String namespace;  
  15. private DataSource dataSource;  



现在我们回过头看return state.getConfig().getClient(); 
是这句话获得了SqlMapClient对象,这个对象是怎么创建的呢,在SqlMapConfiguration的构造方法里面就已经创建好了。 

Java代码  收藏代码
  1. public SqlMapConfiguration() {  
  2.     errorContext = new ErrorContext();  
  3.     delegate = new SqlMapExecutorDelegate();  
  4.     typeHandlerFactory = delegate.getTypeHandlerFactory();  
  5.     client = new SqlMapClientImpl(delegate);  
  6.     registerDefaultTypeAliases();  
  7.   }  



原来我们的到的并不是SqlMapClient(接口不能实现)对象,而是他的一个实现SqlMapClientImpl 

二、 深入SqlMapClientImpl内部 
SqlMapClientImpl类中只有三个字段 

Java代码  收藏代码
  1.   private static final Log log = LogFactory.getLog(SqlMapClientImpl.class);  
  2.   
  3. public SqlMapExecutorDelegate delegate;  
  4.   
  5. protected ThreadLocal localSqlMapSession = new ThreadLocal();  


log是一个日志记录的对象,与线程安全肯定是无关的 
SqlMapExecutorDelegate这个类里面有什么东西呢 

Java代码  收藏代码
  1. private static final Probe PROBE = ProbeFactory.getProbe();  
  2.   
  3.   private boolean lazyLoadingEnabled;  
  4.   private boolean cacheModelsEnabled;  
  5.   private boolean enhancementEnabled;  
  6.   private boolean useColumnLabel = true;  
  7.   private boolean forceMultipleResultSetSupport;  
  8.   
  9.   private TransactionManager txManager;  
  10.   
  11.   private HashMap mappedStatements;  
  12.   private HashMap cacheModels;  
  13.   private HashMap resultMaps;  
  14.   private HashMap parameterMaps;  
  15.   
  16.   protected SqlExecutor sqlExecutor;  
  17.   private TypeHandlerFactory typeHandlerFactory;  
  18.   private DataExchangeFactory dataExchangeFactory;  
  19.     
  20.   private ResultObjectFactory resultObjectFactory;  
  21.   private boolean statementCacheEnabled;  


这些属性都是一些关于跟sqlMap配置的一些信息,这些信息和线程安全也没有很大的关系。 

最后就剩下localSqlMapSession字段了,其实有经验的同学一眼就能看出来这点的,ThreadLocal就是为处理线程安全而来的,他的实质为每个线程保存一个副本。他的实现就是存在一个全局的Map存放localSqlMapSession,key是线程的id号value值是一个localSqlMapSession的副本。 
SqlMapClientImpl里面的方法: 

Java代码  收藏代码
  1. public Object insert(String id, Object param) throws SQLException {  
  2.     return getLocalSqlMapSession().insert(id, param);  
  3.   }  
  4.   
  5.   public Object insert(String id) throws SQLException {  
  6.     return getLocalSqlMapSession().insert(id);  
  7.   }  
  8.   
  9.   public int update(String id, Object param) throws SQLException {  
  10.     return getLocalSqlMapSession().update(id, param);  
  11.   }  
  12.   
  13.   public int update(String id) throws SQLException {  
  14.     return getLocalSqlMapSession().update(id);  
  15.   }  
  16.   
  17.   public int delete(String id, Object param) throws SQLException {  
  18.     return getLocalSqlMapSession().delete(id, param);  
  19.   }  
  20.   
  21.   public int delete(String id) throws SQLException {  
  22.     return getLocalSqlMapSession().delete(id);  
  23.   }  
  24.   
  25.   public Object queryForObject(String id, Object paramObject) throws SQLException {  
  26.     return getLocalSqlMapSession().queryForObject(id, paramObject);  
  27.   }  


多么熟悉的方法啊,这就是我们经常用的curd的方法。从代码上证明了我们的推测,线程安全就是和localSqlMapSession有关 
虽然找到了相关的属性,但是他们是怎么实现的呢。 
三、 线程安全的实现。 
就dao部分的线程安全来说一个是主要是事务的完成性。如果事务能够保证完整性,那么就可以说是线程安全的。 
localSqlMapSession存的是什么什么东西呢,我们打开代码看看。 

Java代码  收藏代码
  1. protected SqlMapSessionImpl getLocalSqlMapSession() {  
  2.     SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get();  
  3.     if (sqlMapSession == null || sqlMapSession.isClosed()) {  
  4.       sqlMapSession = new SqlMapSessionImpl(this);  
  5.       localSqlMapSession.set(sqlMapSession);  
  6.     }  
  7.     return sqlMapSession;  
  8.   }  


再研究一下SqlMapSessionImpl,这个类只有三个字段 
protected SqlMapExecutorDelegate delegate; 
protected SessionScope sessionScope; 
protected boolean closed; 

很明显SessionScope这是我们要找的东西 

 

Java代码  收藏代码
  1. private static long nextId;  
  2.   private long id;  
  3.   // Used by Any  
  4.   private SqlMapClient sqlMapClient;  
  5.   private SqlMapExecutor sqlMapExecutor;  
  6.   private SqlMapTransactionManager sqlMapTxMgr;  
  7.   private int requestStackDepth;  
  8.   // Used by TransactionManager  
  9.   private Transaction transaction;  
  10.   private TransactionState transactionState;  
  11.   // Used by SqlMapExecutorDelegate.setUserProvidedTransaction()  
  12.   private TransactionState savedTransactionState;  
  13.   // Used by StandardSqlMapClient and GeneralStatement  
  14.   private boolean inBatch;  
  15.   // Used by SqlExecutor  
  16.   private Object batch;  
  17.   private boolean commitRequired;  
  18.   private Map preparedStatements;  



根据我们的分析事务的完整性足以保证dao层的线程安全。Transaction保存在ThreadLocal里面证明了SqlMapClient是线程安全的,我们在整个工程中只要一个SqlMapClient对象就够了。 

再来看下SessionScope这个类的字段 
private SqlMapClient sqlMapClient;保存的是一个SqlMapClient 
  private SqlMapExecutor sqlMapExecutor; 执行sql用的 
  private SqlMapTransactionManager sqlMapTxMgr; 管理事务的 
  private int requestStackDepth; 
  // Used by TransactionManager 
  private Transaction transaction; 事务 
  private TransactionState transactionState; 事务的状态 
  // Used by SqlMapExecutorDelegate.setUserProvidedTransaction() 
  private TransactionState savedTransactionState; 事务的保存状态 
  // Used by StandardSqlMapClient and GeneralStatement 
  private boolean inBatch;是否批处理 
  // Used by SqlExecutor 
  private Object batch; 
  private boolean commitRequired;是否用提交 
  private Map preparedStatements;这个应该是保存批处理的PreparedStatement 

我们突然发现没有连接类Connection,如果用jdbc的话Connection是多么重要的一个对象啊,在这里没有保存Connection呢。打开JdbcTransaction(一个Transaction的实现) 

Java代码  收藏代码
  1. private static final Log connectionLog = LogFactory.getLog(Connection.class);  
  2.   
  3.   private DataSource dataSource;  
  4.   private Connection connection;  
  5.   private IsolationLevel isolationLevel = new IsolationLevel();  
  6.   
  7.   public JdbcTransaction(DataSource ds, int isolationLevel) throws TransactionException {  
  8.     // Check Parameters  
  9.     dataSource = ds;  
  10.     if (dataSource == null) {  
  11.       throw new TransactionException("JdbcTransaction initialization failed.  DataSource was null.");  
  12.     }  
  13.     this.isolationLevel.setIsolationLevel(isolationLevel);  
  14.   }  
  15.   
  16.   private void init() throws SQLException, TransactionException {  
  17.     // Open JDBC Transaction  
  18.     connection = dataSource.getConnection();  
  19.     if (connection == null) {  
  20.       throw new TransactionException("JdbcTransaction could not start transaction.  Cause: The DataSource returned a null connection.");  
  21.     }  
  22.     // Isolation Level  
  23.     isolationLevel.applyIsolationLevel(connection);  
  24.     // AutoCommit  
  25.     if (connection.getAutoCommit()) {  
  26.       connection.setAutoCommit(false);  
  27.     }  
  28.     // Debug  
  29.     if (connectionLog.isDebugEnabled()) {  
  30.       connection = ConnectionLogProxy.newInstance(connection);  
  31.     }  
  32.   }  
  33.   
  34.   public void commit() throws SQLException, TransactionException {  
  35.     if (connection != null) {  
  36.       connection.commit();  
  37.     }  
  38.   }  
  39.   
  40.   public void rollback() throws SQLException, TransactionException {  
  41.     if (connection != null) {  
  42.       connection.rollback();  
  43.     }  
  44.   }  
  45.   
  46.   public void close() throws SQLException, TransactionException {  
  47.     if (connection != null) {  
  48.       try {  
  49.         isolationLevel.restoreIsolationLevel(connection);  
  50.       } finally {  
  51.         connection.close();  
  52.         connection = null;  
  53.       }  
  54.     }  
  55.   }  
  56.   
  57.   public Connection getConnection() throws SQLException, TransactionException {  
  58.     if (connection == null) {  
  59.       init();  
  60.     }  
  61.     return connection;  
  62.   }  



原来Connection在这里保存着呢,事务的提交,回滚也是在这里实现的。 

到这里大致明白了,ibatis为每一个操作SqlMapClient的线程建立一个SessionScope对象,这里面保存了Transaction,Connection,要执行的PreparedStatement。 
SqlMapClient对象里面保存的是全局有关的缓存策略,ParameterMap,ResultMap,jdbc到Java对象的类型转换,别名等信息。 


在每个执行的Statement中还有一个StatementScope,这里保存的是每个执行语句的状态。这里就不看了。 

第一次分析代码,思路有点混乱哈,自己挺有收获的,给大家分享一下,欢迎拍砖哈。 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics