1、什么是mybatis-spring
mybatis-spring会帮助你将mybatis代码无缝地整合到Spring中。使用这个类库中的类,Spring将会加载必要的MyBatis工厂类(SqlSessionFactoryBuilder)和session 类,同时也提供一个简单的方式来注入MyBatis数据映射器(MapperFactoryBean)和SqlSession到业务层的bean 中。 而且它也会处理事务,翻译MyBatis的异常到Spring 的DataAccessException异常(抽象类,数据访问异常)中。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" lazy-init="false">
	<property name="dataSource" ref="dataSource" />
	<property name="mapperLocations" value="classpath:sqlmapper/*Mapper.xml" />
	<property name="plugins">
		<list>
			<bean class="com.pinganfu.common.pagination.PaginationInterceptor">
				<property name="dialect">
					<bean class="com.pinganfu.common.pagination.OracleDialect" />
				</property>
			</bean>
		</list>
	</property>
/bean>

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

2、SqlSessionTemplate源码解读

  // SqlSessionTemplate.java
  public int update(String statement, Object parameter) {
    // sqlSessionProxy是mybatis SqlSession的JDK动态代理类,为了实现一些功能的增加
    return this.sqlSessionProxy.update(statement, parameter);
  }

  // SqlSessionTemplate.java的构造方法
  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;
    // sqlSessionProxy初始化的地方
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }
  // SqlSessionTemplate.java的构造方法
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
    // exceptionTranslator初始化的地址,为MyBatisExceptionTranslator
    this(sqlSessionFactory, executorType,
        new MyBatisExceptionTranslator(
            sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
  } 
  // SqlSessionInterceptor.java
  private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 获取目标SqlSession
      final SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        // 执行SqlSession
        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);
        // 对mybatis抛出的PersistenceException进行转换
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      }
    }
  }
  // MyBatisExceptionTranslator.java
  public DataAccessException translateExceptionIfPossible(RuntimeException e) {
    if (e instanceof PersistenceException) {
      // Batch exceptions come inside another PersistenceException
      // recursion has a risk of infinite loop so better make another if
      if (e.getCause() instanceof PersistenceException) {
        e = (PersistenceException) e.getCause();
      }
      if (e.getCause() instanceof SQLException) {
        // 初始化异常翻译器
        this.initExceptionTranslator();
        // 对异常进行翻译
        return this.exceptionTranslator.translate(e.getMessage() + "\n", null, (SQLException) e.getCause());
      }
      return new MyBatisSystemException(e);
    }
    return null;
  }

  private synchronized void initExceptionTranslator() {
    if (this.exceptionTranslator == null) {
      this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(this.dataSource);
    }
  }

3、自定义异常的地方

  protected DataAccessException doTranslate(String task, String sql, SQLException ex) {
   SQLException sqlEx = ex;
   if (sqlEx instanceof BatchUpdateException && sqlEx.getNextException() != null) {
	SQLException nestedSqlEx = sqlEx.getNextException();
	if (nestedSqlEx.getErrorCode() > 0 || nestedSqlEx.getSQLState() != null) {
	  logger.debug("Using nested SQLException from the BatchUpdateException");
	  sqlEx = nestedSqlEx;
	}
   }
   // First, try custom translation from overridden method.
   DataAccessException dex = customTranslate(task, sql, sqlEx);
   if (dex != null) {
	return dex;
   }
   // Next, try the custom SQLException translator, if available.(这里我们可以自定义错误码翻译器)
   if (this.sqlErrorCodes != null) {
	SQLExceptionTranslator customTranslator = this.sqlErrorCodes.getCustomSqlExceptionTranslator();
   }
   if (customTranslator != null) {
	DataAccessException customDex = customTranslator.translate(task, sql, sqlEx);
	if (customDex != null) {
  	 return customDex;
	}
   }

   // Check SQLErrorCodes with corresponding error code, if available.
   if (this.sqlErrorCodes != null) {
	String errorCode;
	if (this.sqlErrorCodes.isUseSqlStateForTranslation()) {
	 errorCode = sqlEx.getSQLState();
    } else {
	 SQLException current = sqlEx;
	 while (current.getErrorCode() == 0 && current.getCause() instanceof SQLException) {
	  current = (SQLException) current.getCause();
	 }
	 errorCode = Integer.toString(current.getErrorCode());
	}

   if (errorCode != null) {
	 // Look for defined custom translations first.
	 CustomSQLErrorCodesTranslation[] customTranslations = this.sqlErrorCodes.getCustomTranslations();
	 if (customTranslations != null) {
	  ......
	 }
	 // Next, look for grouped error codes.
	 if (Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) {
	  logTranslation(task, sql, sqlEx, false);
	  return new BadSqlGrammarException(task, sql, sqlEx);
	 } else if (Arrays.binarySearch(this.sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) {
	  logTranslation(task, sql, sqlEx, false);
	  return new InvalidResultSetAccessException(task, sql, sqlEx);
	 } else if Arrays.binarySearch(this.sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0) {
	  logTranslation(task, sql, sqlEx, false);
	  return new DuplicateKeyException(buildMessage(task, sql, sqlEx), sqlEx);
	 }
	 ......
   }
 }
  ......
  return null;
}
11-30 10:02