1、mybatis-spring.jar作用
mybatis-spring.jar可以让mybatis代码无缝地整合到Spring中。使用这个类库中的类,Spring将会加载必要的MyBatis工厂类和session 类,同时也提供了一个简单的方式来注入MyBatis数据映射器和SqlSession到业务层的bean中,而且它也会处理事务。并将MyBatis抛出的PersistenceException异常包装成Spring 的DataAccessException异常(抽象类,数据访问异常)抛出,下面会详细说明。
1.1、配置文件
<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源码解读
2.1、处理流程: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。
2.2、SqlSessionTemplate初始化
// 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.3、SqlSessionInterceptor拦截器
// 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.4、 MyBatisExceptionTranslator对异常的翻译
// 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.5、SQLErrorCodesFactory加载默认和自定义的SqlErrorCodes
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;
}
3、demo关键步骤
自定义SQLErrorCodes文件sql-error-codes.xml并放在classpath即可
<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:
http://www.mybatis.org/spring/zh/index.html#