网站建设图标素材免费,网页认证怎么认证,企业网站的规划与设计,做网站公司赚不赚钱2019独角兽企业重金招聘Python工程师标准 使用DataSourceUtils进行Connection的管理 由上节代码可知#xff0c;JdbcTemplate在获取Connection的时候#xff0c;并不是直接调用DataSource的getConnection(),而是调用了如下的代码#xff1a; Connection con … 2019独角兽企业重金招聘Python工程师标准 使用DataSourceUtils进行Connection的管理 由上节代码可知JdbcTemplate在获取Connection的时候并不是直接调用DataSource的getConnection(),而是调用了如下的代码 Connection con DataSourceUtils.getConnection(getDataSource()); 为什么要这么做呢 实际上如果对于一个功能带一的JdbcTemplate来说调用如下的代码就够了 Connection con dataSource.getConnection(); 只不过spring所提供的JdbcTemplate要关注更多的东西所以在从dataSource取得连接的时候需要多做一些事情。 org.springframework.jdbc.datasource.DataSourceUtils所提供的方法用来从指定的DataSource中获取或者释放连接它会将取得的Connection绑定到当前的线程以便在使用Spring所提供的统一事务抽象层进行事务管理的时候使用。 为什么要使用NativeJdbcExtractor 在execute()方法中可以看到 if (this.nativeJdbcExtractor ! null this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {conToUse this.nativeJdbcExtractor.getNativeConnection(con);} if (this.nativeJdbcExtractor ! null) {stmtToUse this.nativeJdbcExtractor.getNativeStatement(stmt);} 通过该处理获取的将是相应的驱动程序所提供的实现类而不是相应的代理对象。 JdbcTemplate内部定义了一个NativeJdbcExtractor类型的实例变量 /** Custom NativeJdbcExtractor */private NativeJdbcExtractor nativeJdbcExtractor; 当我们想用驱动对象所提供的原始API的时候可以通过JdbcTemplate的如下代码 public void setNativeJdbcExtractor(NativeJdbcExtractor extractor) {this.nativeJdbcExtractor extractor;}这样将会获取真正的目标对象而不是代理对象。 spring默认提供面向Commons DBCP、C3P0、Weblogic、Websphere等数据源的NativeJdbcExtractor的实现类 CommonsDbcpNativeJdbcExtractor为Jakarta Commons DBCP数据库连接池所提供的NativeJdbcExtractor实现类 C3P0NativeJdbcExtractor为C3P0数据库连接池所提供的NativeJdbcExtractor实现类 WebLogicNativeJdbcExtractor为Weblogic所准备的NativeJdbcExtractor实现类 WebSphereNativeJdbcExtractor为WebSphere所准备的NativeJdbcExtractor实现类 控制JdbcTemplate的行为 JdbcTemplate在使用Statement或者PreparedStatement等进行具体的数据操作之前会调用如下的代码 protected void applyStatementSettings(Statement stmt) throws SQLException {int fetchSize getFetchSize();if (fetchSize 0) {stmt.setFetchSize(fetchSize);}int maxRows getMaxRows();if (maxRows 0) {stmt.setMaxRows(maxRows);}DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());} 这样便可以设置Statement每次抓取的行数 等等。 SQLException到DataAccessException的转译 因为JdbcTemplate直接操作的是JDBC API所以它需要捕获在此期间可能发生的SQLException处理的宗旨是将SQLException 转译到spring的数据访问异常层次体系以统一数据访问异常的处理方式这个工作主要是交给了SQLExceptionTranslator该 接口的定义如下 package org.springframework.jdbc.support;import java.sql.SQLException;import org.springframework.dao.DataAccessException;/**** author Rod Johnson* author Juergen Hoeller* see org.springframework.dao.DataAccessException*/
public interface SQLExceptionTranslator {DataAccessException translate(String task, String sql, SQLException ex);} 该接口有两个主要的实现类SQLErrorCodeSQLExceptionTranslator和SQLStateSQLExceptionTranslator如下所示 SQLExceptionSubclassTranslator是Spring2.5新加的实现类主要用于JDK6发布的将JDBC4版本中新定义的异常体系转化为spring的异常体系对于之前的版本该类派不上用场。 SQLErrorCodeSQLExceptionTranslator会基于SQLExcpetion所返回的ErrorCode进行异常转译。通常情况下根据各个数据库提供商所提供的ErrorCode进行分析要比基于SqlState的方式要准确的多。默认情况下JdbcTemplate会采用SQLErrorCodeSQLExceptionTranslator进行SQLException的转译当ErrorCode无法提供足够的信息的时候会转而求助SQLStateSQLExceptionTranslator。 如果JdbcTemplate默认的SQLErrorCodeSQLExceptionTranslator无法满足当前异常转译的需要我们可以扩展SQLErrorCodeSQLExceptionTranslator使其支持更多的情况有两种方法进行扩展提供其子类或者在classpath下提供相应的配置文件 我们先大致看一下SQLErrorCodeSQLExceptionTranslator的大致调用规则然后再从代码层面上研究下r进行转译的大致的流程如下 1、SQLErrorCodeSQLExceptionTranslator定义了如下的自定义异常转译的方法 protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) {return null;} 程序流程首先会检查该自定义转译的方法是否能够对当前的SQLException进行转译如果可以直接返回DataAccessException类型如果为null表示无法转译程序将执行下一步由上面代码可以看到该方法直接返回null所以流程要进入下一步。 2、使用org.springframework.jdbc.support.SQLErrorCodesFactory所加载的SQLErrorCodes进行异常转译其中SQLErrorCodesFactory加载SQLErrorCodes的流程为 1使用org/springframework/jdbc/support/sql-error-codes.xml路径下记载了各个数据库提供商的配置文件提取相应的SQLErrorCodes。 2如果发现当前应用的根目录下存在名称为sql-error-codes.xml的配置文件则加载该文件并覆盖默认的ErrorCodes定义。 3、如果基于ErrorCode的异常转译还是没法搞定的话SQLErrorCodeSQLExceptionTranslator只能求助于SQLStateSQLExceptionTranslator或者SQLExceptionSubclassTranslator 下面从代码层面上剖析之 假若JdbcTemplate的如下模板方法在执行的过程中发生了异常 public Object execute(StatementCallback action) throws DataAccessException {Assert.notNull(action, Callback object must not be null);Connection con DataSourceUtils.getConnection(getDataSource());Statement stmt null;try {Connection conToUse con;if (this.nativeJdbcExtractor ! null this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {conToUse this.nativeJdbcExtractor.getNativeConnection(con);}stmt conToUse.createStatement();applyStatementSettings(stmt);Statement stmtToUse stmt;if (this.nativeJdbcExtractor ! null) {stmtToUse this.nativeJdbcExtractor.getNativeStatement(stmt);}Object result action.doInStatement(stmtToUse);handleWarnings(stmt);return result;}catch (SQLException ex) {// Release Connection early, to avoid potential connection pool deadlock// in the case when the exception translator hasnt been initialized yet.JdbcUtils.closeStatement(stmt);stmt null;DataSourceUtils.releaseConnection(con, getDataSource());con null;throw getExceptionTranslator().translate(StatementCallback, getSql(action), ex);}finally {JdbcUtils.closeStatement(stmt);DataSourceUtils.releaseConnection(con, getDataSource());}} 会执行catch块中的 throw getExceptionTranslator().translate(StatementCallback, getSql(action), ex); getExceptionTranslator()如下定义 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;} dataSource不为null所以创建了SQLErrorCodeSQLExceptionTranslator,看下其构造方法 public SQLErrorCodeSQLExceptionTranslator(DataSource dataSource) {this();setDataSource(dataSource);} this()代码为 public SQLErrorCodeSQLExceptionTranslator() {if (JdkVersion.getMajorJavaVersion() JdkVersion.JAVA_16) {setFallbackTranslator(new SQLExceptionSubclassTranslator());}else {setFallbackTranslator(new SQLStateSQLExceptionTranslator());}} 如果JDK版本大于或等于6备份了一个SQLExceptionSubclassTranslator类型的Translator否则备份一个SQLStateSQLExceptionTranslator setDataSource(DataSource dataSource)通过SQLErrorCodesFactory创建一个SQLErrorCodes类型的变量 public void setDataSource(DataSource dataSource) {this.sqlErrorCodes SQLErrorCodesFactory.getInstance().getErrorCodes(dataSource);} SQLErrorCodesFactory采用了单例模式在其构造方法中依然利用了BeanFactory传入的文件为xml bean配置文件 protected SQLErrorCodesFactory() {Map errorCodes null;try {DefaultListableBeanFactory lbf new DefaultListableBeanFactory();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.EMPTY_MAP;}this.errorCodesMap errorCodes;} 可知首先会读取org.springframework.jdbc.support下的sql-error-codes.xml文件如果classpath下也有该文件则覆盖之, 这样便生成了sqlErrorCodes getExceptionTranslator().translate(StatementCallback, getSql(action), ex)的方法如下所示 public DataAccessException translate(String task, String sql, SQLException ex) {Assert.notNull(ex, Cannot translate a null SQLException);if (task null) {task ;}if (sql null) {sql ;}DataAccessException dex doTranslate(task, sql, ex);if (dex ! null) {// Specific exception match found.return dex;}// Looking for a fallback...SQLExceptionTranslator fallback getFallbackTranslator();if (fallback ! null) {return fallback.translate(task, sql, ex);}// We couldnt identify it more precisely.return new UncategorizedSQLException(task, sql, ex);} doTranslate(task, sql, ex)让子类实现在这个例子中即是SQLErrorCodeSQLExceptionTranslator代码如下 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;}// Check SQLErrorCodes with corresponding error code, if available.if (this.sqlErrorCodes ! null) {String errorCode null;if (this.sqlErrorCodes.isUseSqlStateForTranslation()) {errorCode sqlEx.getSQLState();}else {errorCode Integer.toString(sqlEx.getErrorCode());}if (errorCode ! null) {// Look for defined custom translations first.CustomSQLErrorCodesTranslation[] customTranslations this.sqlErrorCodes.getCustomTranslations();if (customTranslations ! null) {for (int i 0; i customTranslations.length; i) {CustomSQLErrorCodesTranslation customTranslation customTranslations[i];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, 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.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 couldnt identify it more precisely - lets hand it over to the SQLState fallback translator.if (logger.isDebugEnabled()) {String codes null;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;} 可知假如该方法返回的是nulltranslate方法会调用SQLExceptionSubclassTranslator或者SQLStateSQLExceptionTranslator的translate的方法转译这个异常。 在SQLErrorCodeSQLExceptionTranslator转译异常的过程中我们可以在两个地方插入自定义的转译异常 1、在customTranslate(String task, String sql, SQLException sqlEx)方法中通过子类化SQLErrorCodeSQLExceptionTranslator重写该方法。 2、在classpath下提供sql-error-codes.xml文件。 下面是使用这两种方式进行自定义转译的具体实施情况。 1、扩展SQLErrorCodeSQLExceptionTranslator 该方法最直接有效却不够方便需要子类化并且覆写它的customTranslate方法 package com.google.spring.jdbc;import java.sql.SQLException;import org.springframework.dao.DataAccessException;
import org.springframework.dao.UncategorizedDataAccessException;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;public class SimpleSQLErrorCodeSQLExceptinTranslator extends SQLErrorCodeSQLExceptionTranslator
{Overrideprotected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) {if(sqlEx.getErrorCode()111){StringBuilder builder new StringBuilder();builder.append(unexpected data access exception raised when executing );builder.append(task);builder.append( with SQL);builder.append(sql);return new UnknownUncategorizedDataAccessException(builder.toString(),sqlEx);}return null;}private class UnknownUncategorizedDataAccessException extends UncategorizedDataAccessException{public UnknownUncategorizedDataAccessException(String msg, Throwable cause) {super(msg, cause);}}
} 在这里假设当数据库返回的错误代码为111的时候将抛出UnknownUncategorizedDataAccessException类型的异常或者是其它自定义的DataAccessException除此之外返回null以保证其它的异常转译依然采用超类的逻辑进行。 为了能使自定义的转译其作用我们需要让JdbcTemplate使用我们的SimpleSQLErrorCodeSQLExceptinTranslator而不是默认的SQLErrorCodeSQLExceptionTranslator所以需要如下代码所示将SimpleSQLErrorCodeSQLExceptinTranslator设置给JdbcTemplate ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext2.xml);JdbcTemplate jdbc (JdbcTemplate)applicationContext.getBean(jdbc);DataSource dataSource (DataSource)applicationContext.getBean(dataSource);SimpleSQLErrorCodeSQLExceptinTranslator simpleSQLErrorCodeSQLExceptinTranslator new SimpleSQLErrorCodeSQLExceptinTranslator();simpleSQLErrorCodeSQLExceptinTranslator.setDataSource(dataSource);jdbc.setExceptionTranslator(simpleSQLErrorCodeSQLExceptinTranslator);在classpath下放置一个sql-error-codes.xml文件格式要与默认的文件格式相同。 实际上它就是一个基本的基于DTD的Spring IOC容器的配置文件只不过class是固定的。该配置文件对每个数据库类型均提供了一个org.springframework.jdbc.support.SQLErrorCodes的定义。假若我们有另外一个数据库AnotherDb,要扩展该转译我们有两种方式 1、 ?xml version1.0 encodingUTF-8?
!DOCTYPE beans PUBLIC -//SPRING//DTD BEAN 2.0//EN http://www.springframework.org/dtd/spring-beans-2.0.dtdbeansbean idAnotherDB classorg.springframework.jdbc.support.SQLErrorCodesproperty namedatabaseProductNamevalueAnotherDB*/value/propertyproperty namebadSqlGrammarCodesvalue001/value/propertyproperty namedataIntegrityViolationCodesvalue002/value/propertyproperty namedataAccessResourceFailureCodesvalue0031,0032/value/propertyproperty nametransientDataAccessResourceCodesvalue004/value/propertyproperty namedeadlockLoserCodesvalue0051,0052/value/property/bean
/beans 2、设置customTranslations属性 ?xml version1.0 encodingUTF-8?
!DOCTYPE beans PUBLIC -//SPRING//DTD BEAN 2.0//EN http://www.springframework.org/dtd/spring-beans-2.0.dtdbeansbean idAnotherDB classorg.springframework.jdbc.support.SQLErrorCodesproperty namedatabaseProductNamevalueAnotherDB*/value/propertyproperty namecustomTranslationslistbean classorg.springframework.jdbc.support.CustomSQLErrorCodesTranslationproperty nameerrorCodes111/propertyproperty nameexceptionClassorg.springframework.dao.IncorrectResultSizeDataAccessException/property/bean/list/property/bean
/beans 至此spring的异常转译部分全部分析完毕! 转载于:https://my.oschina.net/u/218421/blog/38576