Ibatis源码分析

Ibatis源码分析

整体架构

UML

描述

SqlMapClientImpl接到请求后,创建SqlMapSessionImpl对象(ThreadLocal,保证线程安全);

SqlMapSessionImpl交由内部的代理类SqlMapExecutorDelegate执行;

代理类获取相应的MappedStatement,交由MappedStatement对象执行;

MappedStatement交由SqlExecutor执行;

最终使用JDBC方式执行sql。

时序图

初始化

UML

时序图

描述

ibatis初始化的核心目标是构建SqlMapClientImpl对象,主要思想如下: 
1. 构建NodeletParser配置文件解析类,它维护了节点XPath和对应处理方式的映射关系,并提供了节点的通用处理方法。 
2. SqlMapConfigParser和SqlMapParser在构造方法中向NodeletParser中添加节点XPath和对应处理方式的映射关系。 
3. 配置文件解析采用递归方式进行,首先生成当前节点的XPath信息,再从NodeletParser获取对应的处理方式并执行。 
4. 整个解析过程中每个节点生成的数据统一注入到XmlParserState,最终通过XmlParserStat获取SqlMapClientImpl对象并返回。

 

配置文件解析

类图

描述

创建SqlMapConfigParser时,创建vars 、SqlMapExecutorDelegate、SqlMapClientImpl,并注册各个节点对应的Nodelet定义。

public SqlMapConfigParser() {

    this(null, null);

  }

 

  public SqlMapConfigParser(XmlConverter sqlMapConfigConv,XmlConverter sqlMapConv) {

    super(new Variables());

    parser.setValidation(true);

    parser.setEntityResolver(new SqlMapClasspathEntityResolver());

 

    vars.sqlMapConfigConv = sqlMapConfigConv;

    vars.sqlMapConv = sqlMapConv;

 

    vars.delegate = new SqlMapExecutorDelegate();

    vars.typeHandlerFactory = vars.delegate.getTypeHandlerFactory();

    vars.client = new SqlMapClientImpl(vars.delegate);

 

    registerDefaultTypeAliases();

 

    addSqlMapConfigNodelets();

    addGlobalPropNodelets();

    addSettingsNodelets();

    addTypeAliasNodelets();

    addTypeHandlerNodelets();

    addTransactionManagerNodelets();

    addSqlMapNodelets();

 

  }

 

Vars作为一个上下文对象,存放整个解析过程中的变量。

开始调用parser.parse()解析

遇到"/sqlMapConfig/sqlMap"调用SqlMapParser解析,并将解析结果setSqlMapExecutorDelegate对象

vars对象中返回client

 

是否必须将ParameterMap ResultMap的解析放到 sql的前面??、

 

参数映射

类图

描述

1. 从纵向上体现了Statement类的整体继承关系,MappedStatement接口提供了SQL执行上下文信息和执行操作,如ParameterMap、ResultMap、SQL、Timeout等上下文信息和executeQueryForList等操作信息;BaseStatement抽象类提供了MappedStatement的初步实现,它组合了MappedStatement需要的上下文信息;GeneralStatement实现类提供了MappedStatement的执行操作的基本实现;InsertStatement、SelectStatement等实现类提供了针对不同类型SQL操作的特定实现。
2. 从横向上体现了Statement类的初始化数据和请求处理数据的分离。 类图中的第一行对象(ParameterMap/ResultMap/SQL等)在初始化过程中会构造完毕,请求处理时直接获取即可; 类图中的第三行对象(RequestScope)在请求处理时才会创建,通过执行方法的参数传入,再进行后续处理。这是会对入参做出校验,并将参数事实射入sql中,拼接出最终可执行的sql

结果集映射

 

 

批量提交

 

Request / Session / transaction / connection

Session

1、session可以是代码请求的显式session,也可以是当线程使用SqlMapClient实例(即执行一条语句)自动获得的session。

    2、一个session是附属于一个特定线程的,也就是说它是线程安全的,从下面创建session的过程中可以看出这点,当ThreadLoacle变量中存在session,则直接复用,当不存在时才新建:

private ThreadLocallocalSqlMapSession = new ThreadLocal();

……

private SqlMapSessionImpl getLocalSqlMapSession() {

    SqlMapSessionImpl sqlMapSession =(SqlMapSessionImpl) localSqlMapSession.get();

    if (sqlMapSession == null ||sqlMapSession.isClosed()) {

      sqlMapSession = new SqlMapSessionImpl(this);

      localSqlMapSession.set(sqlMapSession);

    }

    return sqlMapSession;

  }

         3、一个事务transaction会在执行中会独占一个session,运行时的session数必然要大于transaction数量。这也是Ibatis的参数:maxSessions必须大于maxTransactions的原因。代码参见TransactionManager.begin()方法:

           public void begin(SessionScopesession, int transactionIsolation) throws SQLException, TransactionException {

    Transactiontrans = session.getTransaction();

   TransactionState state = session.getTransactionState();

    if (state ==TransactionState.STATE_STARTED) {

      throw newTransactionException("TransactionManager could not start a newtransaction.  " +

          "Atransaction is already started.");

    } else if (state ==TransactionState.STATE_USER_PROVIDED) {

      throw newTransactionException("TransactionManager could not start a newtransaction.  " +

          "A userprovided connection is currently being used by this session.  " +

          "The calling.setUserConnection (null) will clear the user provided transaction.");

    }

 

    txThrottle.increment();

 

    try {

      trans = transactionConfig.newTransaction(transactionIsolation);

      session.setCommitRequired(false);

    } catch (SQLException e) {

      txThrottle.decrement();

      throw e;

    } catch(TransactionException e) {

      txThrottle.decrement();

      throw e;

    }

 

    session.setTransaction(trans);

    session.setTransactionState(TransactionState.STATE_STARTED);

  }

4、SqlMapSessionImpl类的作用是为SqlMapClientImpl加入会话控制

5、在构造SqlMapSessionImpl对象时,会创建session

public SqlMapSessionImpl(ExtendedSqlMapClient client) {

    this.delegate =client.getDelegate();

    this.session = this.delegate.popSession();

    this.session.setSqlMapClient(client);

    this.session.setSqlMapExecutor(client);

    this.session.setSqlMapTxMgr(client);

    this.closed = false;

  }

6、session的创建受到参数maxSessions的限制,如果当前活动session已经超过这个上限,则线程会一直等待别的线程释放session资源。、

protected SessionScope popSession() {

    return (SessionScope)sessionPool.pop();

  }

 

  public Object pop() {

    Object o = null;

    while (o== null) {

      try {

        throttle.increment();

        o = pool.remove(0);

      } catch (Exception e) {

        // threadcollision detected, retry

      }

    }

    return o;

  }

 

Request

1、调用一次SqlMapClient,会产生一个Request对象,它属于一个session(会话)中

  public int update(SessionScope session, String id, Object param) throws SQLException {

    ………….

      RequestScope request =popRequest(session, ms);

    ……..

  }

 

 

protected RequestScope popRequest(SessionScopesession, MappedStatement mappedStatement) {

    RequestScope request = (RequestScope) requestPool.pop();

    session.incrementRequestStackDepth();

    request.setSession(session);

   mappedStatement.initRequest(request);

    return request;

  }

2、每个SqlMapExecutorDelegate实例存在一个requestPool,总共有maxRequest大小,如果请求到来时,requestPool已用尽,则需等待。

public Object pop() {

    Object o = null;

    while (o == null) {

      try {

        throttle.increment();

        o = pool.remove(0);

      } catch (Exception e) {

        // threadcollision detected, retry

      }

    }

    return o;

  }

3、一个Request的到来,可能开启一个新的transaction,也可能沿用该session对应的transaction,取决于客户端是否显示调用了SqlMapClientImpl. startTransaction()。如果显示调用,则该Request会被放入已有事务中处理,如果没有,则会新建一个事务,并被Ibatis自动提交,:public ObjectqueryForObject(SessionScope session, String id, Object paramObject, ObjectresultObject) throws SQLException {

    Object object = null;

    MappedStatement ms =getMappedStatement(id);

    Transaction trans =getTransaction(session);

    boolean autoStart = trans == null;

    try {

      trans =autoStartTransaction(session, autoStart, trans);

      RequestScope request =popRequest(session, ms);

      try {

        object =ms.executeQueryForObject(request, trans, paramObject, resultObject);

      } finally {

        pushRequest(request);

      }

      autoCommitTransaction(session, autoStart);

    } finally {

      autoEndTransaction(session, autoStart);

    }

    return object;

 }

                private Transaction autoStartTransaction(SessionScope session, boolean autoStart, Transactiontrans) throws SQLException {

    Transaction transaction = trans;

    if (autoStart) {

     session.getSqlMapTxMgr().startTransaction();

      transaction =getTransaction(session);

    }

//   System.out.println(transaction);

    return transaction;

  }

private voidautoCommitTransaction(SessionScope session, boolean autoStart) throws SQLException {

    if (autoStart) {

      session.getSqlMapTxMgr().commitTransaction();

    }

  }

private voidautoEndTransaction(SessionScope session, boolean autoStart) throws SQLException {

    if (autoStart) {

      session.getSqlMapTxMgr().endTransaction();

    }

  }

transaction

transaction上可以挂很多request,transaction开始时,将autoCommit=false,然后transaction上可以挂很多request,等到事务提交时,统一将request对应的PreparedStatement提交至数据库。

Connection

每个transaction都持有一个connection,该connection在transaction结束时,会被放回连接池中,或者直接被销毁。所以transaction的大小依赖于DBMS的限制。

 

 

Thread ThreadLocal object

 

综上所述:

每次调用SqlClientMap都会产生一个Request;

每个transaction均会独占一个session,并在事务结束时,将session归还sessionPool

如果没有显示开启一个事务,ibatis会为每个request自动开启一个transaction,如果是查询交易,则不会提交事务,如果是update,则会自动提交事务,最后会自动回收事务和session资源。这时一个request会占用一个transaction,一个session

如果显示开始一个transaction,则事务中的所有request会被统一提交,这时一个transaction对应一个session,多个request。

MaxRequest >MaxSession > MaxTransaction

为什么MaxSession > MaxTransaction??从以上分析,实际上他们是相等的。

SqlMapClientImpl / SqlMapSessionImpl/SqlMapExecutorDelegate

SqlMapClientImpl

在一个应用中(如Spring容器)只会创建一个实例,在加载ibatis配置文件时完成

SqlMapSessionImpl

会创建多个对象实例,原因还是session存储了线程对应的资源和状态,无法共享,所以会为每个线程创建一个SqlMapSessionImpl实例,并将其存储在ThreadLocal对象中,ThreadLocal对象保存了每个线程对应于该变量的副本。

*******************ThreadLocal *************************************

线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。

ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。

概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

ThreadLocal vs synchronized

ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。

 

ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。

 

ThreadLocalSynchonized都用于解决多线程并发访问。但是ThreadLocalsynchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

 

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。

SqlMapExecutorDelegate

只会创建一个对象实例,在加载ibatis配置文件时完成

 

一次带事务的更新

UML类图

描述

1、 startTransaction

SqlMapClientImpl创建一个新的SqlMapSessionImpl实例,持有它并将其放入ThreadLocal中,在创建SqlMapSessionImpl对象时,会创建一个SessionScope(简称session)对象,并持有它;

SqlMapExecutorDelegate调用TransactionManager的begin方法开始事务,这个过程中会创建一个JdbcTransaction对象,并将其配给session对象

返回。

2、 update

SqlMapClientImpl从ThreadLocal中获取SqlMapSessionImpl对象,由SqlMapSessionImpl对象调用SqlMapExecutorDelegate(简称delegate)的update方法,在这个过程中,delegate会获得预先缓存好的MappedStatement对象(简称statement),transaction对象,并从RequestPool中pop一个Reaquest对象,然后调用statement.excuteUpdate(request,transaction,params),此方法会调用transaction.getConnection()获得一个Connection对象,最后执行PreparedStatement。

3、 commitTransaction

调用connection.commit()提交事务

4、 endTransaction

如果第3步失败,则调用connection.rollback()回滚事务,并调用transaction.close()将transaction和connection关闭和放空;将sessiion对象push回sessionPool

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值