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