mybatis-spring.jar对SQLErrorCodes的扩展点

一、mybatis-spring.jar做用

mybatis-spring.jar能够让mybatis代码无缝地整合到Spring中。使用这个类库中的类,Spring将会加载必要的MyBatis工厂类和session 类,同时也提供了一个简单的方式来注入MyBatis数据映射器和SqlSession到业务层的bean中,并且它也会处理事务。并将MyBatis抛出的PersistenceException异常包装成Spring 的DataAccessException异常(抽象类,数据访问异常)抛出,下面会详细说明。html

1.一、配置文件java

<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>

二、SqlSessionTemplate源码解读

2.一、处理流程:SqlSessionTemplate内部持有一个mybatis的DefaultSqlSession(使用JDK动态代理生成的类,sqlSessionProxy),经过此代理类能够对SqlSession执行的异常进行包装以后再抛出,SqlSessionTemplate在建立时会传入一个exceptionTranslator(MyBatisExceptionTranslator)。MyBatisExceptionTranslator内部引用了SQLExceptionTranslator(SQLErrorCodeSQLExceptionTranslator),SQLExceptionTranslator在初始化时会根据DataSource选择对应的SQLErrorCodes。SQLErrorCodes由SQLErrorCodesFactory在类加载时初始化,SQLErrorCodesFactory先去加载org/springframework/jdbc/support/sql-error-codes.xml文件中定义的SQLErrorCodes,再去加载classpath,/WEB-INF/classes/sql-error-codes.xml下用户自定的SQLErrorCodes。spring

2.二、SqlSessionTemplate初始化sql

// 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));
  }

2.三、SqlSessionInterceptor拦截器session

// 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);
      }
    }
  }

2.四、 MyBatisExceptionTranslator对异常的翻译mybatis

// 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);
    }
  }

2.五、SQLErrorCodesFactory加载默认和自定义的SqlErrorCodesapp

protected SQLErrorCodesFactory() {
    Map<String, SQLErrorCodes> errorCodes;

	try {
	  DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
	  lbf.setBeanClassLoader(getClass().getClassLoader());
	  XmlBeanDefinitionReader bdr = new XmlBeanDefinitionReader(lbf);

	  // Load default SQL error codes.
	  Resource resource = loadResource(SQL_ERROR_CODE_DEFAULT_PATH);
	  if (resource != null && resource.exists()) {
	 	bdr.loadBeanDefinitions(resource);
	  } else {
		logger.warn("Default sql-error-codes.xml not found (should be included in spring.jar)");
	  }
      // Load custom SQL error codes, overriding defaults.
	  resource = loadResource(SQL_ERROR_CODE_OVERRIDE_PATH);
	  if (resource != null && resource.exists()) {
	    bdr.loadBeanDefinitions(resource);
		logger.info("Found custom sql-error-codes.xml file at the root of the classpath");
	  }

	  // Check all beans of type SQLErrorCodes.
	  errorCodes = lbf.getBeansOfType(SQLErrorCodes.class, true, false);
	  if (logger.isInfoEnabled()) {
	    logger.info("SQLErrorCodes loaded: " + errorCodes.keySet());
		}
	  }
        catch (BeansException ex) {
  	   logger.warn("Error loading SQL error codes from config file", ex);
	   errorCodes = Collections.emptyMap();
        }
  	   this.errorCodesMap = errorCodes;
  }

三、demo关键步骤

定义SQLErrorCodes文件sql-error-codes.xml并放在classpath便可ide

<bean id="MySQL" class="org.springframework.jdbc.support.SQLErrorCodes">
	<property name="badSqlGrammarCodes">
		<value>xxxx</value>
	</property>
	<property name="duplicateKeyCodes">
		<value>xxxx</value>
	</property>
</bean>

 

Ref:oop

http://www.mybatis.org/spring/zh/index.html#ui

相关文章
相关标签/搜索