日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring JDBC最佳实践(2)

發布時間:2023/12/19 javascript 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring JDBC最佳实践(2) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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 hasn't 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 couldn't 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 couldn't identify it more precisely - let's 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;}

可知假如該方法返回的是null,translate方法會調用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 version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"><beans><bean id="AnotherDB" class="org.springframework.jdbc.support.SQLErrorCodes"><property name="databaseProductName"><value>AnotherDB*</value></property><property name="badSqlGrammarCodes"><value>001</value></property><property name="dataIntegrityViolationCodes"><value>002</value></property><property name="dataAccessResourceFailureCodes"><value>0031,0032</value></property><property name="transientDataAccessResourceCodes"><value>004</value></property><property name="deadlockLoserCodes"><value>0051,0052</value></property></bean> </beans>


2、設置customTranslations屬性:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"><beans><bean id="AnotherDB" class="org.springframework.jdbc.support.SQLErrorCodes"><property name="databaseProductName"><value>AnotherDB*</value></property><property name="customTranslations"><list><bean class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation"><property name="errorCodes">111</property><property name="exceptionClass">org.springframework.dao.IncorrectResultSizeDataAccessException</property></bean></list></property></bean> </beans>

至此,spring的異常轉譯部分全部分析完畢!

轉載于:https://my.oschina.net/u/218421/blog/38576

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的Spring JDBC最佳实践(2)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。