zoukankan      html  css  js  c++  java
  • ibatis源码分析

    背景:调试模式下,单步运行一个查询订单协议操作,记录了ibatis框架的执行动作,侧面剖析其原理。


    一、简介:

    1. dal 层的dao接口实现类通常会继承SqlMapClientDaoSupport。spring容器在初始化一个dao bean实例时,通常会注入两块信息DataSource(数据源)和sqlMapClient(主要是sql语句),这两块信息会封装到SqlMapClientTemplate



    2. 其中数据源的实例通常采用apache的开源项目dbcp

    代码配置如下:

    1. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">   
    2.     <property name="driverClassName" value="com.mysql.jdbc.Driver" />  
    3.     <property name="url" value="xxxx" />  
    4.     <property name="username"><value>xxxx</value></property>  
    5.         <property name="password"><value>xxxxx</value></property>  
    6.         <property name="maxActive"><value>20</value></property>  
    7.         <property name="initialSize"><value>1</value></property>  
    8.         <property name="maxWait"><value>60000</value></property>  
    9.         <property name="maxIdle"><value>20</value></property>  
    10.         <property name="minIdle"><value>3</value></property>  
    11.         <property name="removeAbandoned"><value>true</value></property>  
    12.         <property name="removeAbandonedTimeout"><value>180</value></property>  
    13.         <property name="connectionProperties"><value>clientEncoding=GBK</value></property>  
    14. </bean>  
    各配置参数的具体含义可参照:dbcp基本配置和重连配置


    3. sqlMapClient

    1. <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  
    2.         <property name="configLocation">  
    3.             <value>classpath:sqlmap.xml</value>  
    4.         </property>  
    5.     </bean>  
    6.   
    7.     <bean id="sqlMapClientTddl" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  
    8.         <property name="dataSource" ref="tGroupDataSource" />  
    9.         <property name="configLocation" value="classpath:sqlmap.xml"/>  
    10.     </bean>  
    sqlMapClient,主要是借助于实现FactoryBean和InitializingBean两个接口,加载sql.xml文件资源信息,得到sqlMapClient组件

    注:上面的sqlMapClient默认不配置数据源,后面的SqlMapClientTemplate优先从全局变量中取,如果没有再从sqlMapClient中查找。

    1. public DataSource getDataSource() {  
    2.         DataSource ds = super.getDataSource();  
    3.         return (ds != null ? ds : this.sqlMapClient.getDataSource());  
    4.     }  
    构造sqlMapClient组件的代码块。

    1. public void afterPropertiesSet() throws Exception {  
    2.         if (this.lobHandler != null) {  
    3.             // Make given LobHandler available for SqlMapClient configuration.  
    4.             // Do early because because mapping resource might refer to custom types.  
    5.             configTimeLobHandlerHolder.set(this.lobHandler);  
    6.         }  
    7.   
    8.         try {  
    9.             this.sqlMapClient = buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties);  
    10.   
    11.             // Tell the SqlMapClient to use the given DataSource, if any.  
    12.             if (this.dataSource != null) {  
    13.                 TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();  
    14.                 DataSource dataSourceToUse = this.dataSource;  
    15.                 if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {  
    16.                     dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);  
    17.                 }  
    18.                 transactionConfig.setDataSource(dataSourceToUse);  
    19.                 transactionConfig.initialize(this.transactionConfigProperties);  
    20.                 applyTransactionConfig(this.sqlMapClient, transactionConfig);  
    21.             }  
    22.         }  
    23.   
    24.         finally {  
    25.             if (this.lobHandler != null) {  
    26.                 // Reset LobHandler holder.  
    27.                 configTimeLobHandlerHolder.set(null);  
    28.             }  
    29.         }  
    30.     }  

    4. SqlMapExecutor 

    该接口是对SQL操作行为的抽象,提供了SQL单条执行和批处理涉及的所有操作方法


    5. SqlMapTransactionManager 
    该接口是对事务行为的抽象,提供了事务执行过程中涉及的所有方法。 
     
    6. SqlMapClient 
    该接口定位是SQL执行客户端,是线程安全的,用于处理多个线程的sql执行。它继承了上面两个接口,这意味着该接口具有SQL执行和事务处理的能力,该接口的核心实现类是SqlMapClientImpl。 

    7. SqlMapSession 
    该接口在继承关系上和SqlMapClient一致,但它的定位是保存单线程sql执行过程的session信息。该接口的核心实现类是SqlMapSessionImpl


    8. MappedStatement 
    该接口定位是单条SQL执行时的上下文环境信息,如SQL标识、SQL、参数信息、返回结果、操作行为等。 

    9. ParameterMap/ResultMap 
    该接口用于在SQL执行的前后提供参数准备和执行结果集的处理。 

    整体类图:



    二、具体调用

    接下来就到了数据持久层的代码调用,所有的数据库DML操作(增、删、改、查)都是借助于SqlMapClientTemplate来实现的。


    1. public OrderEnsureProtocolDO getOrderEnsureProtocolByOrderId(Long orderId) {  
    2.         if (orderId == null) {  
    3.             return null;  
    4.         }  
    5.         return (OrderEnsureProtocolDO) this.getSqlMapClientTemplate().queryForObject("MS-FIND-ORDERENSUREPROTOCOL-BY-ORDERID",  
    6.                                                                                      orderId);  
    7.     }  

    如果一次执行的sql较多,我们会采用批处理的形式

    1. public void batchDeleteOfferSaleRecord(final List<Long> orderEntryIds) throws Exception {  
    2.        if (orderEntryIds == null || orderEntryIds.size() < 1 || orderEntryIds.size() > 50) {  
    3.            return;  
    4.        }  
    5.   
    6.        this.getSqlMapClientTemplate().execute(new SqlMapClientCallback() {  
    7.   
    8.            @Override  
    9.            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {  
    10.                executor.startBatch();  
    11.                for (Long entryId : orderEntryIds) {  
    12.                    executor.insert(MS_DELETE_SALE_RECORD, entryId);  
    13.                }  
    14.   
    15.                return executor.executeBatch();  
    16.            }  
    17.        });  
    18.    }  

    不管采用上面哪种方式,查看源代码会发现,最后都是在调用execute(SqlMapClientCallback action)方法

    1. public Object queryForObject(final String statementName, final Object parameterObject)  
    2.             throws DataAccessException {  
    3.   
    4.         return execute(new SqlMapClientCallback() {  
    5.             public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {  
    6.                 return executor.queryForObject(statementName, parameterObject);  
    7.             }  
    8.         });  
    9.     }  

    1. public Object execute(SqlMapClientCallback action) throws DataAccessException {  
    2.         Assert.notNull(action, "Callback object must not be null");  
    3.         Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");  
    4.         //获取session信息(SqlMapSessionImpl实例)  
    5.         SqlMapSession session = this.sqlMapClient.openSession();  
    6.         if (logger.isDebugEnabled()) {  
    7.             logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");  
    8.         }  
    9.         Connection ibatisCon = null;  
    10.   
    11.         try {  
    12.             Connection springCon = null;//数据库连接  
    13.             DataSource dataSource = getDataSource();  
    14.             boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);  
    15.   
    16.             try {  
    17.                 //获取数据获连接  
    18.                 ibatisCon = session.getCurrentConnection();  
    19.                 if (ibatisCon == null) {  
    20.                     springCon = (transactionAware ?  
    21.                             dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));  
    22.                     //将数据源set到session会话中  
    23.                     session.setUserConnection(springCon);  
    24.                     if (logger.isDebugEnabled()) {  
    25.                         logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");  
    26.                     }  
    27.                 }  
    28.                 else {  
    29.                     if (logger.isDebugEnabled()) {  
    30.                         logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");  
    31.                     }  
    32.                 }  
    33.             }  
    34.             catch (SQLException ex) {  
    35.                 throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);  
    36.             }  
    37.             try {  
    38.                 //执行SQL   
    39.                 return action.doInSqlMapClient(session);  
    40.             }  
    41.             catch (SQLException ex) {  
    42.                 throw getExceptionTranslator().translate("SqlMapClient operation", null, ex);  
    43.             }  
    44.             finally {  
    45.                 省略。。。一系列的关闭工作  
    46.         }  
    47.     }  

    SqlMapSessionImpl().queryForObject()的方法很简单,直接交给代理对象SqlMapExecutorDelegate处理(里面注入了很多功能对象,负责具体的sql执行)

    1. public Object queryForObject(String id, Object paramObject) throws SQLException {  
    2.   return delegate.queryForObject(session, id, paramObject);  
    3. }  
    经过N层重载,最后调用内部的通用方法

    1.  入参:   
    2.  id=MS-FIND-ORDERENSUREPROTOCOL-BY-ORDERID  
    3.  paramObject=26749329  
    4.    
    5.  public Object queryForObject(SessionScope session, String id, Object paramObject, Object resultObject) throws SQLException {  
    6.    Object object = null;  
    7.   
    8. //MappedStatement对象集是上文中提及的初始化方法SqlMapClientFactoryBean.afterPropertiesSet()中,由配置文件构建而成  
    9. //调试中的ms为SelectStatement,具体的执行器  
    10.    MappedStatement ms = getMappedStatement(id);   
    11. // 用于事务执行  
    12.    Transaction trans = getTransaction(session);   
    13.    boolean autoStart = trans == null;  
    14.   
    15.    try {  
    16.      trans = autoStartTransaction(session, autoStart, trans);  
    17.   // 从RequestScope池中获取该次sql执行中的上下文环境RequestScope   
    18.      RequestScope request = popRequest(session, ms);    
    19.      try {  
    20.     // 执行sql  
    21.        object = ms.executeQueryForObject(request, trans, paramObject, resultObject);     
    22.      } finally {  
    23.        pushRequest(request);  //归还RequestScope  
    24.      }  
    25.   
    26.      autoCommitTransaction(session, autoStart);  
    27.    } finally {  
    28.      autoEndTransaction(session, autoStart);  
    29.    }  
    30.   
    31.    return object;  
    32.  }  

    接下来由MappedStatement.executeQueryForObject()来执行
    1. public Object executeQueryForObject(RequestScope request, Transaction trans, Object parameterObject, Object resultObject)  
    2.      throws SQLException {  
    3.    try {  
    4.      Object object = null;  
    5.   
    6.      DefaultRowHandler rowHandler = new DefaultRowHandler();  
    7.    //执行sql语句  
    8.      executeQueryWithCallback(request, trans.getConnection(), parameterObject, resultObject, rowHandler, SqlExecutor.NO_SKIPPED_RESULTS, SqlExecutor.NO_MAXIMUM_RESULTS);  
    9.        
    10.      //结果处理,返回结果  
    11.      List list = rowHandler.getList();   
    12.      if (list.size() > 1) {  
    13.        throw new SQLException("Error: executeQueryForObject returned too many results.");  
    14.      } else if (list.size() > 0) {  
    15.        object = list.get(0);  
    16.      }  
    17.    。。。。。。。。。  
    18.  }  
    MappedStatement.executeQueryWithCallback()方法包含了参数值映射、sql准备和sql执行等关键过程

    1. protected void executeQueryWithCallback(RequestScope request, Connection conn, Object parameterObject, Object resultObject, RowHandler rowHandler, int skipResults, int maxResults)  
    2.     throws SQLException {  
    3. //预先封装错误信息,如果报错时便于排查问题  
    4.   ErrorContext errorContext = request.getErrorContext();  
    5.   errorContext.setActivity("preparing the mapped statement for execution");  
    6.   errorContext.setObjectId(this.getId());  
    7.   errorContext.setResource(this.getResource());  
    8.   try {  
    9.  //验证入参  
    10.     parameterObject = validateParameter(parameterObject);    
    11.   
    12.  //获取SQL对象  
    13.     Sql sql = getSql();    
    14.   
    15.     errorContext.setMoreInfo("Check the parameter map.");  
    16.  // 入参映射  
    17.     ParameterMap parameterMap = sql.getParameterMap(request, parameterObject);  
    18.   
    19.     errorContext.setMoreInfo("Check the result map.");  
    20.  //获取结果  
    21.     ResultMap resultMap = sql.getResultMap(request, parameterObject);   
    22.   
    23.     request.setResultMap(resultMap);  
    24.     request.setParameterMap(parameterMap);  
    25.   
    26.     errorContext.setMoreInfo("Check the parameter map.");  
    27.  //获取参数值  
    28.     Object[] parameters = parameterMap.getParameterObjectValues(request, parameterObject);    
    29.   
    30.     errorContext.setMoreInfo("Check the SQL statement.");  
    31.  //获取拼装后的sql语句  
    32.     String sqlString = sql.getSql(request, parameterObject);    
    33.   
    34.     errorContext.setActivity("executing mapped statement");  
    35.     errorContext.setMoreInfo("Check the SQL statement or the result map.");  
    36.     RowHandlerCallback callback = new RowHandlerCallback(resultMap, resultObject, rowHandler);  
    37.   //sql执行  
    38.     sqlExecuteQuery(request, conn, sqlString, parameters, skipResults, maxResults, callback);   
    39.   
    40.     ....省略  
    41. }  

    最后调用com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(RequestScope, Connection, String, Object[], int, int, RowHandlerCallback)

    1. public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {  
    2.   ...省略  
    3.   PreparedStatement ps = null;  
    4.   ResultSet rs = null;  
    5.   setupResultObjectFactory(request);  
    6.   try {  
    7.     errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");  
    8.     Integer rsType = request.getStatement().getResultSetType();  
    9.     //初始化PreparedStatement,设置sql、参数值等  
    10.     if (rsType != null) {  
    11.       ps = prepareStatement(request.getSession(), conn, sql, rsType);  
    12.     } else {  
    13.       ps = prepareStatement(request.getSession(), conn, sql);  
    14.     }  
    15.     setStatementTimeout(request.getStatement(), ps);  
    16.     Integer fetchSize = request.getStatement().getFetchSize();  
    17.     if (fetchSize != null) {  
    18.       ps.setFetchSize(fetchSize.intValue());  
    19.     }  
    20.     errorContext.setMoreInfo("Check the parameters (set parameters failed).");  
    21.     request.getParameterMap().setParameters(request, ps, parameters);  
    22.     errorContext.setMoreInfo("Check the statement (query failed).");  
    23.   //执行  
    24.     ps.execute();  
    25.     errorContext.setMoreInfo("Check the results (failed to retrieve results).");  
    26.   
    27.     //结果集处理  
    28.     rs = handleMultipleResults(ps, request, skipResults, maxResults, callback);  
    29.       
    30.     。。。省略  
    31.   
    32. }  
  • 相关阅读:
    尚筹网11阿里云OSS对象存储
    阿里云的OSS对象存储
    尚筹网10用户登录
    尚筹网09用户注册
    尚筹网08环境搭建
    实体类的进一步划分
    尚筹网07分布式架构
    临时弹出一个QQ对话窗口
    Input框改placeholder中字体的颜色
    判断银行卡号的正则
  • 原文地址:https://www.cnblogs.com/daichangya/p/12958689.html
Copyright © 2011-2022 走看看