深入浅出Spring的tx事务基本概念:源码解析

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的根目录下面;会一同加载下面的配置数据;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东山富哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值