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>
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; }
自定義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