文章目录
1.源码项目
2.源码code解析
2.1.源码入口:PlatformTransactionManager
package org.springframework.transaction;
import org.springframework.lang.Nullable;
public interface PlatformTransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
PlatformTransactionManager被很多的类实现
2.1.事务Core模块
2.1.1.JdbcTemplate概述
2.1.1.1.JdbcTemplate概述
2.1.1.1.1.完成了资源的创建以及释放工作,简化对JDBC 的使用
我们以JdbcTemplate.queryForList的方法为例,来进行说明
@Override
public List<Map<String, Object>> queryForList(String sql) throws DataAccessException {
return query(sql, getColumnMapRowMapper());
}
@Override
public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));
}
@Override
@Nullable
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
/**
* 这里是定义了一个方法内的内部类,主要是用于下面的方法的入参使用,也就是说如何处理
* return execute(new QueryStatementCallback());
*
* 其实我们就可以认为一个简单的类,new QueryStatementCallback()对象
* 后面对象的方法调用即可
*
*
*/
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
@Override
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
return rse.extractData(rs);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
@Override
public String getSql() {
return sql;
}
}
return execute(new QueryStatementCallback());
}
@Override
@Nullable
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(obtainDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("StatementCallback", sql, ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
2.1.1.1.2.完成 JDBC 核心处理流程,比如 SQL 语句的创建、执行,而 把 SQL 语句的生成以及查询结果的提取工作留给我们的应用代码
2.1.1.1.3.它可以完成 SQL查询、更新以及调用存储过程,可以对 ResultSet 进行遍历并加以提取。
2.1.1.1.4.还可以捕获 JDBC 异常并将其转换成 org.springframework.dao 包中定义的,通用的,信息更丰富的异常。
2.1.1.2.JdbcTemplate的UML类图
继承JdbcAccessor,实现了JdbcOperations
2.1.2.JdbcOperations:定义了数据库操作的行为接口规范
public interface JdbcOperations {
@Nullable
<T> T execute(ConnectionCallback<T> action) throws DataAccessException;
@Nullable
<T> T execute(StatementCallback<T> action) throws DataAccessException;
void execute(String sql) throws DataAccessException;
@Nullable
<T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException;
void query(String sql, RowCallbackHandler rch) throws DataAccessException;
<T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException;
@Nullable
<T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException;
@Nullable
<T> T queryForObject(String sql, Class<T> requiredType) throws DataAccessException
Map<String, Object> queryForMap(String sql) throws DataAccessException;
<T> List<T> queryForList(String sql, Class<T> elementType) throws DataAccessException;
List<Map<String, Object>> queryForList(String sql) throws DataAccessException;
SqlRowSet queryForRowSet(String sql) throws DataAccessException;
int update(String sql) throws DataAccessException;
int[] batchUpdate(String... sql) throws DataAccessException;
@Nullable
<T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException;
@Nullable
<T> T execute(String sql, PreparedStatementCallback<T> action) throws DataAccessException;
@Nullable
<T> T query(PreparedStatementCreator psc, ResultSetExtractor<T> rse) throws DataAccessException;
@Nullable
<T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException
@Nullable
<T> T query(String sql, Object[] args, int[] argTypes, ResultSetExtractor<T> rse) throws DataAccessException;
@Nullable
<T> T query(String sql, Object[] args, ResultSetExtractor<T> rse) throws DataAccessException
@Nullable
<T> T query(String sql, ResultSetExtractor<T> rse, @Nullable Object... args) throws DataAccessException;
void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException;
void query(String sql, @Nullable PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException;
void query(String sql, Object[] args, int[] argTypes, RowCallbackHandler rch) throws DataAccessException;
void query(String sql, Object[] args, RowCallbackHandler rch) throws DataAccessException;
void query(String sql, RowCallbackHandler rch, @Nullable Object... args) throws DataAccessException;
<T> List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper) throws DataAccessException;
<T> List<T> query(String sql, @Nullable PreparedStatementSetter pss, RowMapper<T> rowMapper) throws DataAccessException;
<T> List<T> query(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper) throws DataAccessException;
<T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException;
<T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;
@Nullable
<T> T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper)
throws DataAccessException;
@Nullable
<T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException;
@Nullable
<T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException;
@Nullable
<T> T queryForObject(String sql, Object[] args, int[] argTypes, Class<T> requiredType)
throws DataAccessException;
@Nullable
<T> T queryForObject(String sql, Object[] args, Class<T> requiredType) throws DataAccessException;
@Nullable
<T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException;
Map<String, Object> queryForMap(String sql, Object[] args, int[] argTypes) throws DataAccessException;
Map<String, Object> queryForMap(String sql, @Nullable Object... args) throws DataAccessException;
<T>List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> elementType)
throws DataAccessException;
<T> List<T> queryForList(String sql, Object[] args, Class<T> elementType) throws DataAccessException;
<T> List<T> queryForList(String sql, Class<T> elementType, @Nullable Object... args) throws DataAccessException;
List<Map<String, Object>> queryForList(String sql, Object[] args, int[] argTypes) throws DataAccessException;
List<Map<String, Object>> queryForList(String sql, @Nullable Object... args) throws DataAccessException;
SqlRowSet queryForRowSet(String sql, Object[] args, int[] argTypes) throws DataAccessException;
SqlRowSet queryForRowSet(String sql, @Nullable Object... args) throws DataAccessException;
int update(PreparedStatementCreator psc) throws DataAccessException;
int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder) throws DataAccessException;
int update(String sql, @Nullable PreparedStatementSetter pss) throws DataAccessException;
int update(String sql, Object[] args, int[] argTypes) throws DataAccessException;
int update(String sql, @Nullable Object... args) throws DataAccessException;
int[] batchUpdate(String sql, BatchPreparedStatementSetter pss) throws DataAccessException;
int[] batchUpdate(String sql, List<Object[]> batchArgs) throws DataAccessException;
int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes) throws DataAccessException;
<T> int[][] batchUpdate(String sql, Collection<T> batchArgs, int batchSize,
ParameterizedPreparedStatementSetter<T> pss) throws DataAccessException;
@Nullable
<T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action) throws DataAccessException;
@Nullable
<T> T execute(String callString, CallableStatementCallback<T> action) throws DataAccessException;
Map<String, Object> call(CallableStatementCreator csc, List<SqlParameter> declaredParameters)
throws DataAccessException;
}
2.1.3.JdbcAccessor:JdbcTemplate的基类和其他JDBC访问的DAO助手已经定义公共属性诸如数据源和异常转换器
1.JdbcAccessor里面的东西就是一些公用的属性和方法:
logger:日志
dataSource:数据源
SQLExceptionTranslator exceptionTranslator;:异常转换器,详细见下面讲解
lazyInit:默认懒加载
public abstract class JdbcAccessor implements InitializingBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private DataSource dataSource;
@Nullable
private SQLExceptionTranslator exceptionTranslator;
private boolean lazyInit = true;
public void setDataSource(@Nullable DataSource dataSource) {
this.dataSource = dataSource;
}
@Nullable
public DataSource getDataSource() {
return this.dataSource;
}
protected DataSource obtainDataSource() {
DataSource dataSource = getDataSource();
Assert.state(dataSource != null, "No DataSource set");
return dataSource;
}
public void setDatabaseProductName(String dbName) {
this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dbName);
}
public void setExceptionTranslator(SQLExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator;
}
public synchronized SQLExceptionTranslator getExceptionTranslator() {
if (this.exceptionTranslator == null) {
DataSource dataSource = getDataSource();
if (dataSource != null) {
this.exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
}
else {
this.exceptionTranslator = new SQLStateSQLExceptionTranslator();
}
}
return this.exceptionTranslator;
}
public void setLazyInit(boolean lazyInit) {
this.lazyInit = lazyInit;
}
public boolean isLazyInit() {
return this.lazyInit;
}
@Override
public void afterPropertiesSet() {
if (getDataSource() == null) {
throw new IllegalArgumentException("Property 'dataSource' is required");
}
if (!isLazyInit()) {
getExceptionTranslator();
}
}
}
2.2异常模块
2.2.1.SQLExceptionTranslator:异常转换器
将一个特定异常SQLException ex转换为DataAccessException(通用数据库层的异常类)
@FunctionalInterface
public interface SQLExceptionTranslator {
@Nullable
DataAccessException translate(String task, @Nullable String sql, SQLException ex);
}
2.2.2.DataAccessException的UML类图:
就相当于数据操作相关的异常
2.2.3.SQLExceptionTranslator UML类图
1.可以看到SQLExceptionTranslator 被三个类实现
我们详细说一下 SQLErrorCodeSQLExceptionTranslator类
2.2.4.SQLErrorCodeSQLExceptionTranslator
@Override
@Nullable
protected DataAccessException doTranslate(String task, @Nullable 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 {
// Try to find SQLException with actual error code, looping through the causes.
// E.g. applicable to java.sql.DataTruncation as of JDK 1.6.
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) {
for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) {
if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0) {
if (customTranslation.getExceptionClass() != null) {
DataAccessException customException = createCustomException(
task, sql, sqlEx, customTranslation.getExceptionClass());
if (customException != null) {
logTranslation(task, sql, sqlEx, true);
return customException;
}
}
}
}
}
// Next, look for grouped error codes.
if (Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new BadSqlGrammarException(task, (sql != null ? sql : ""), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getInvalidResultSetAccessCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new InvalidResultSetAccessException(task, (sql != null ? sql : ""), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DuplicateKeyException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDataIntegrityViolationCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DataIntegrityViolationException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getPermissionDeniedCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new PermissionDeniedDataAccessException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDataAccessResourceFailureCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DataAccessResourceFailureException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getTransientDataAccessResourceCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new TransientDataAccessResourceException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotAcquireLockCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new CannotAcquireLockException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getDeadlockLoserCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new DeadlockLoserDataAccessException(buildMessage(task, sql, sqlEx), sqlEx);
}
else if (Arrays.binarySearch(this.sqlErrorCodes.getCannotSerializeTransactionCodes(), errorCode) >= 0) {
logTranslation(task, sql, sqlEx, false);
return new CannotSerializeTransactionException(buildMessage(task, sql, sqlEx), sqlEx);
}
}
}
// We couldn't identify it more precisely - let's hand it over to the SQLState fallback translator.
if (logger.isDebugEnabled()) {
String codes;
if (this.sqlErrorCodes != null && this.sqlErrorCodes.isUseSqlStateForTranslation()) {
codes = "SQL state '" + sqlEx.getSQLState() + "', error code '" + sqlEx.getErrorCode();
}
else {
codes = "Error code '" + sqlEx.getErrorCode() + "'";
}
logger.debug("Unable to translate SQLException with " + codes + ", will now try the fallback translator");
}
return null;
}
1.我们可以看到Arrays.binarySearch(this.sqlErrorCodes.getBadSqlGrammarCodes(), errorCode) >= 0
这里我们注意下,对于具体什么异常,是通过sqlErrorCodes这个对象中的不同的异常类型Code个数去判断的;
所以我们讲,java是面向对象的,对于异常也是,通过对不同的异常进行封装成不同的异常对象;
后续然后我们进行判断;
2.关于ErrorCodes的存储和加载,见SQLErrorCodesFactory工厂类
2.2.5.SQLErrorCodesFactory:
public class SQLErrorCodesFactory {
/**
* The name of custom SQL error codes file, loading from the root
* of the class path (e.g. from the "/WEB-INF/classes" directory).
*/
public static final String SQL_ERROR_CODE_OVERRIDE_PATH = "sql-error-codes.xml";
/**
* The name of default SQL error code files, loading from the class path.
*/
public static final String SQL_ERROR_CODE_DEFAULT_PATH = "org/springframework/jdbc/support/sql-error-codes.xml";
private static final Log logger = LogFactory.getLog(SQLErrorCodesFactory.class);
private static final SQLErrorCodesFactory instance = new SQLErrorCodesFactory();
public static SQLErrorCodesFactory getInstance() {
return instance;
}
private final Map<String, SQLErrorCodes> errorCodesMap;
/**
* Map to cache the SQLErrorCodes instance per DataSource.
*/
private final Map<DataSource, SQLErrorCodes> dataSourceCache = new ConcurrentReferenceHashMap<>(16);
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;
}
@Nullable
protected Resource loadResource(String path) {
return new ClassPathResource(path, getClass().getClassLoader());
}
public SQLErrorCodes getErrorCodes(String databaseName) {
Assert.notNull(databaseName, "Database product name must not be null");
SQLErrorCodes sec = this.errorCodesMap.get(databaseName);
if (sec == null) {
for (SQLErrorCodes candidate : this.errorCodesMap.values()) {
if (PatternMatchUtils.simpleMatch(candidate.getDatabaseProductNames(), databaseName)) {
sec = candidate;
break;
}
}
}
if (sec != null) {
checkCustomTranslatorRegistry(databaseName, sec);
if (logger.isDebugEnabled()) {
logger.debug("SQL error codes for '" + databaseName + "' found");
}
return sec;
}
// Could not find the database among the defined ones.
if (logger.isDebugEnabled()) {
logger.debug("SQL error codes for '" + databaseName + "' not found");
}
return new SQLErrorCodes();
}
public SQLErrorCodes getErrorCodes(DataSource dataSource) {
Assert.notNull(dataSource, "DataSource must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Looking up default SQLErrorCodes for DataSource [" + identify(dataSource) + "]");
}
// Try efficient lock-free access for existing cache entry
SQLErrorCodes sec = this.dataSourceCache.get(dataSource);
if (sec == null) {
synchronized (this.dataSourceCache) {
// Double-check within full dataSourceCache lock
sec = this.dataSourceCache.get(dataSource);
if (sec == null) {
// We could not find it - got to look it up.
try {
String name = JdbcUtils.extractDatabaseMetaData(dataSource, "getDatabaseProductName");
if (StringUtils.hasLength(name)) {
return registerDatabase(dataSource, name);
}
}
catch (MetaDataAccessException ex) {
logger.warn("Error while extracting database name - falling back to empty error codes", ex);
}
// Fallback is to return an empty SQLErrorCodes instance.
return new SQLErrorCodes();
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("SQLErrorCodes found in cache for DataSource [" + identify(dataSource) + "]");
}
return sec;
}
public SQLErrorCodes registerDatabase(DataSource dataSource, String databaseName) {
SQLErrorCodes sec = getErrorCodes(databaseName);
if (logger.isDebugEnabled()) {
logger.debug("Caching SQL error codes for DataSource [" + identify(dataSource) +
"]: database product name is '" + databaseName + "'");
}
this.dataSourceCache.put(dataSource, sec);
return sec;
}
@Nullable
public SQLErrorCodes unregisterDatabase(DataSource dataSource) {
return this.dataSourceCache.remove(dataSource);
}
private String identify(DataSource dataSource) {
return dataSource.getClass().getName() + '@' + Integer.toHexString(dataSource.hashCode());
}
/**
* Check the {@link CustomSQLExceptionTranslatorRegistry} for any entries.
*/
private void checkCustomTranslatorRegistry(String databaseName, SQLErrorCodes errorCodes) {
SQLExceptionTranslator customTranslator =
CustomSQLExceptionTranslatorRegistry.getInstance().findTranslatorForDatabase(databaseName);
if (customTranslator != null) {
if (errorCodes.getCustomSqlExceptionTranslator() != null && logger.isWarnEnabled()) {
logger.warn("Overriding already defined custom translator '" +
errorCodes.getCustomSqlExceptionTranslator().getClass().getSimpleName() +
" with '" + customTranslator.getClass().getSimpleName() +
"' found in the CustomSQLExceptionTranslatorRegistry for database '" + databaseName + "'");
}
else if (logger.isInfoEnabled()) {
logger.info("Using custom translator '" + customTranslator.getClass().getSimpleName() +
"' found in the CustomSQLExceptionTranslatorRegistry for database '" + databaseName + "'");
}
errorCodes.setCustomSqlExceptionTranslator(customTranslator);
}
}
}
2.2.5.1.SQL_ERROR_CODE_DEFAULT_PATH:数据库的error-code配置
1.SQL_ERROR_CODE_DEFAULT_PATH:默认的不听数据库的error-code配置;
2.本质上都会加载到IOC容器之中;
3.它们将与数据库 metadata 信息中的 database name 进行映射。
2.2.5.2.SQL_ERROR_CODE_OVERRIDE_PATH:自定义的数据库的error-code配置
1.如果有自定义的数据库error-code配置可以放到我们resources的根目录下面;会一同加载下面的配置数据;