日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

spring源码分析之spring-jdbc模块详解

發(fā)布時間:2025/4/5 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring源码分析之spring-jdbc模块详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

0 概述

Spring將替我們完成所有使用JDBC API進行開發(fā)的單調(diào)乏味的、底層細節(jié)處理工作。下表描述了哪些是spring幫助我們做好的,哪些是我們要做的。

Action?Spring?You
Define connection parameters.??X
Open the connection.?X?
Specify the SQL statement.??X
Declare parameters and provide parameter values?X
Prepare and execute the statement.X?
Set up the loop to iterate through the results (if any).X?
Do the work for each iteration.?X
Process any exception.?X?
Handle transactions.?X?
Close the connection,statement and resultset.X?

工作模式?
使用Spring進行基本的JDBC訪問數(shù)據(jù)庫有多種選擇。Spring至少提供了三種不同的工作模式:JdbcTemplate, 一個在Spring2.5中新提供的SimpleJdbc類能夠更好的處理數(shù)據(jù)庫元數(shù)據(jù); 還有一種稱之為RDBMS Object的風(fēng)格的面向?qū)ο蠓庋b方式, 有點類似于JDO的查詢設(shè)計。 我們在這里簡要列舉你采取某一種工作方式的主要理由. 不過請注意, 即使你選擇了其中的一種工作模式, 你依然可以在你的代碼中混用其他任何一種模式以獲取其帶來的好處和優(yōu)勢。 所有的工作模式都必須要求JDBC 2.0以上的數(shù)據(jù)庫驅(qū)動的支持, 其中一些高級的功能可能需要JDBC 3.0以上的數(shù)據(jù)庫驅(qū)動支持。?

JdbcTemplate - 這是經(jīng)典的也是最常用的Spring對于JDBC訪問的方案。這也是最低級別的封裝, 其他的工作模式事實上在底層使用了JdbcTemplate作為其底層的實現(xiàn)基礎(chǔ)。JdbcTemplate在JDK 1.4以上的環(huán)境上工作得很好。?

NamedParameterJdbcTemplate - 對JdbcTemplate做了封裝,提供了更加便捷的基于命名參數(shù)的使用方式而不是傳統(tǒng)的JDBC所使用的“?”作為參數(shù)的占位符。這種方式在你需要為某個SQL指定許多個參數(shù)時,顯得更加直觀而易用。該特性必須工作在JDK 1.4以上。?

SimpleJdbcTemplate - 這個類結(jié)合了JdbcTemplate和NamedParameterJdbcTemplate的最常用的功能,同時它也利用了一些Java 5的特性所帶來的優(yōu)勢,例如泛型、varargs和autoboxing等,從而提供了更加簡便的API訪問方式。需要工作在Java 5以上的環(huán)境中。?

SimpleJdbcInsert 和 SimpleJdbcCall - 這兩個類可以充分利用數(shù)據(jù)庫元數(shù)據(jù)的特性來簡化配置。通過使用這兩個類進行編程,你可以僅僅提供數(shù)據(jù)庫表名或者存儲過程的名稱以及一個Map作為參數(shù)。其中Map的key需要與數(shù)據(jù)庫表中的字段保持一致。這兩個類通常和SimpleJdbcTemplate配合使用。這兩個類需要工作在JDK 5以上,同時數(shù)據(jù)庫需要提供足夠的元數(shù)據(jù)信息。?

RDBMS 對象包括MappingSqlQuery, SqlUpdate and StoredProcedure - 這種方式允許你在初始化你的數(shù)據(jù)訪問層時創(chuàng)建可重用并且線程安全的對象。該對象在你定義了你的查詢語句,聲明查詢參數(shù)并編譯相應(yīng)的Query之后被模型化。一旦模型化完成,任何執(zhí)行函數(shù)就可以傳入不同的參數(shù)對之進行多次調(diào)用。這種方式需要工作在JDK 1.4以上。

?

1. 異常處理

? ?異常結(jié)構(gòu)如下:

?SQLExceptionTranslator是一個接口,如果你需要在 SQLException和org.springframework.dao.DataAccessException之間作轉(zhuǎn)換,那么必須實現(xiàn)該接口。 轉(zhuǎn)換器類的實現(xiàn)可以采用一般通用的做法(比如使用JDBC的SQLState code),如果為了使轉(zhuǎn)換更準(zhǔn)確,也可以進行定制(比如使用Oracle的error code)。?

SQLErrorCodeSQLExceptionTranslator是SQLExceptionTranslator的默認實現(xiàn)。 該實現(xiàn)使用指定數(shù)據(jù)庫廠商的error code,比采用SQLState更精確。轉(zhuǎn)換過程基于一個JavaBean(類型為SQLErrorCodes)中的error code。 這個JavaBean由SQLErrorCodesFactory工廠類創(chuàng)建,其中的內(nèi)容來自于 “sql-error-codes.xml”配置文件。該文件中的數(shù)據(jù)庫廠商代碼基于 Database MetaData 信息中的DatabaseProductName,從而配合當(dāng)前數(shù)據(jù)庫的使用。?

SQLErrorCodeSQLExceptionTranslator使用以下的匹配規(guī)則:?

首先檢查是否存在完成定制轉(zhuǎn)換的子類實現(xiàn)。通常SQLErrorCodeSQLExceptionTranslator 這個類可以作為一個具體類使用,不需要進行定制,那么這個規(guī)則將不適用。?

接著將SQLException的error code與錯誤代碼集中的error code進行匹配。 默認情況下錯誤代碼集將從SQLErrorCodesFactory取得。 錯誤代碼集來自classpath下的sql-error-codes.xml文件,它們將與數(shù)據(jù)庫metadata信息中的database name進行映射。?

使用fallback翻譯器。SQLStateSQLExceptionTranslator類是缺省的fallback翻譯器。

?

2. config模塊

??NamespaceHandler接口,DefaultBeanDefinitionDocumentReader使用該接口來處理在spring xml配置文件中自定義的命名空間。

在jdbc模塊,我們使用JdbcNamespaceHandler來處理jdbc配置的命名空間,其代碼如下:

public class JdbcNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {registerBeanDefinitionParser("embedded-database", new EmbeddedDatabaseBeanDefinitionParser());registerBeanDefinitionParser("initialize-database", new InitializeDatabaseBeanDefinitionParser());} }

其中,

EmbeddedDatabaseBeanDefinitionParser繼承了AbstractBeanDefinitionParser,解析<embedded-database>元素,并使用EmbeddedDatabaseFactoryBean創(chuàng)建一個BeanDefinition。順便介紹一下用到的軟件包 org.w3c.dom。

軟件包 org.w3c.dom:為文檔對象模型 (DOM) 提供接口,該模型是 Java API for XML Processing 的組件 API。該 Document Object Model Level 2 Core API 允許程序動態(tài)訪問和更新文檔的內(nèi)容和結(jié)構(gòu)。

Attr:Attr 接口表示 Element 對象中的屬性。
CDATASection: CDATA 節(jié)用于轉(zhuǎn)義文本塊,該文本塊包含的字符如果不轉(zhuǎn)義則會被視為標(biāo)記。
CharacterData: CharacterData 接口使用屬性集合和用于訪問 DOM 中字符數(shù)據(jù)的方法擴展節(jié)點。
Comment: 此接口繼承自 CharacterData 表示注釋的內(nèi)容,即起始 '<!--' 和結(jié)束 '-->' 之間的所有字符。
Document: Document 接口表示整個 HTML 或 XML 文檔。
DocumentFragment: DocumentFragment 是“輕量級”或“最小”Document 對象。
DocumentType: 每個 Document 都有 doctype 屬性,該屬性的值可以為 null,也可以為 DocumentType 對象。
DOMConfiguration: 該 DOMConfiguration 接口表示文檔的配置,并維護一個可識別的參數(shù)表。
DOMError: DOMError 是一個描述錯誤的接口。
DOMErrorHandler: DOMErrorHandler 是在報告處理 XML 數(shù)據(jù)時發(fā)生的錯誤或在進行某些其他處理(如驗證文檔)時 DOM 實現(xiàn)可以調(diào)用的回調(diào)接口。
DOMImplementation: DOMImplementation 接口為執(zhí)行獨立于文檔對象模型的任何特定實例的操作提供了許多方法。
DOMImplementationList: DOMImplementationList 接口提供對 DOM 實現(xiàn)的有序集合的抽象,沒有定義或約束如何實現(xiàn)此集合。
DOMImplementationSource: 此接口允許 DOM 實現(xiàn)程序根據(jù)請求的功能和版本提供一個或多個實現(xiàn),如下所述。
DOMLocator: DOMLocator 是一個描述位置(如發(fā)生錯誤的位置)的接口。
DOMStringList: DOMStringList 接口提供對 DOMString 值的有序集合的抽象,沒有定義或約束此集合是如何實現(xiàn)的。
Element: Element 接口表示 HTML 或 XML 文檔中的一個元素。
Entity: 此接口表示在 XML 文檔中解析和未解析的已知實體。
EntityReference: EntityReference 節(jié)點可以用來在樹中表示實體引用。
NamedNodeMap: 實現(xiàn) NamedNodeMap 接口的對象用于表示可以通過名稱訪問的節(jié)點的集合。
NameList NameList 接口提供對并行的名稱和名稱空間值對(可以為 null 值)的有序集合的抽象,無需定義或約束如何實現(xiàn)此集合。
Node: 該 Node 接口是整個文檔對象模型的主要數(shù)據(jù)類型。
NodeList: NodeList 接口提供對節(jié)點的有序集合的抽象,沒有定義或約束如何實現(xiàn)此集合。
Notation: 此接口表示在 DTD 中聲明的表示法。
ProcessingInstruction: ProcessingInstruction 接口表示“處理指令”,該指令作為一種在文檔的文本中保持特定于處理器的信息的方法在 XML 中使用。
Text: 該 Text 接口繼承自 CharacterData,并且表示 Element 或 Attr 的文本內(nèi)容(在 XML 中稱為 字符數(shù)據(jù))。
TypeInfo: TypeInfo 接口表示從 Element 或 Attr 節(jié)點引用的類型,用與文檔相關(guān)的模式指定。
UserDataHandler: 當(dāng)使用 Node.setUserData() 將一個對象與節(jié)點上的鍵相關(guān)聯(lián)時,當(dāng)克隆、導(dǎo)入或重命名該對象關(guān)聯(lián)的節(jié)點時應(yīng)用程序可以提供調(diào)用的處理程序。

?3. core模塊

? ? ?3.1?NativeJdbcExtractor 從線程池中的封裝的對象中提取出本地的jdbc對象,其結(jié)構(gòu)如下:

其實現(xiàn)原理如下(以c3po為例):

/*** Retrieve the Connection via C3P0's {@code rawConnectionOperation} API,* using the {@code getRawConnection} as callback to get access to the* raw Connection (which is otherwise not directly supported by C3P0).* @see #getRawConnection*/@Overrideprotected Connection doGetNativeConnection(Connection con) throws SQLException {if (con instanceof C3P0ProxyConnection) {C3P0ProxyConnection cpCon = (C3P0ProxyConnection) con;try {return (Connection) cpCon.rawConnectionOperation(this.getRawConnectionMethod, null, new Object[] {C3P0ProxyConnection.RAW_CONNECTION});}catch (SQLException ex) {throw ex;}catch (Exception ex) {ReflectionUtils.handleReflectionException(ex);}}return con;}

上述代碼通過調(diào)用C3P0的rawConnectionOperation?api來獲取Connection,使用getRawConnection的回調(diào)方法來獲取原生的Connection(C3P0不直接支持原生的Connection)。

NativeJdbcExtractorAdapter是NativeJdbcExtractor的一個簡單實現(xiàn),它的getNativeConnection()方法檢查ConnectionProxy鏈,并且代理doGetNativeConnection方法。Spring的TransactionAwareDataSourceProxy和LazyConnectionDataSourceProxy使用ConnectionProxy。目標(biāo)Connection置于本地連接池中,被子類實現(xiàn)的doGetNativeConnection的方法去掉封裝獲取到原生的Connection。其實現(xiàn)如下:

@Overridepublic Connection getNativeConnection(Connection con) throws SQLException {if (con == null) {return null;}Connection targetCon = DataSourceUtils.getTargetConnection(con);Connection nativeCon = doGetNativeConnection(targetCon);if (nativeCon == targetCon) {// We haven't received a different Connection, so we'll assume that there's// some additional proxying going on. Let's check whether we get something// different back from the DatabaseMetaData.getConnection() call.DatabaseMetaData metaData = targetCon.getMetaData();// The following check is only really there for mock Connections// which might not carry a DatabaseMetaData instance.if (metaData != null) {Connection metaCon = metaData.getConnection();if (metaCon != null && metaCon != targetCon) {// We've received a different Connection there:// Let's retry the native extraction process with it.nativeCon = doGetNativeConnection(metaCon);}}}return nativeCon;}

? 3.2 RowMapper

?

?

3.3 元數(shù)據(jù)metaData模塊

本節(jié)中spring應(yīng)用到工廠模式,結(jié)合代碼可以更具體了解。

CallMetaDataProviderFactory創(chuàng)建CallMetaDataProvider的工廠類,其代碼如下:

/** List of supported database products for procedure calls */public static final List<String> supportedDatabaseProductsForProcedures = Arrays.asList("Apache Derby","DB2","MySQL","Microsoft SQL Server","Oracle","PostgreSQL","Sybase");/** List of supported database products for function calls */public static final List<String> supportedDatabaseProductsForFunctions = Arrays.asList("MySQL","Microsoft SQL Server","Oracle","PostgreSQL");/*** Create a CallMetaDataProvider based on the database metadata* @param dataSource used to retrieve metadata* @param context the class that holds configuration and metadata* @return instance of the CallMetaDataProvider implementation to be used*/static public CallMetaDataProvider createMetaDataProvider(DataSource dataSource, final CallMetaDataContext context) {try {return (CallMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource, new DatabaseMetaDataCallback() {@Overridepublic Object processMetaData(DatabaseMetaData databaseMetaData) throws SQLException, MetaDataAccessException {String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());boolean accessProcedureColumnMetaData = context.isAccessCallParameterMetaData();if (context.isFunction()) {if (!supportedDatabaseProductsForFunctions.contains(databaseProductName)) {if (logger.isWarnEnabled()) {logger.warn(databaseProductName + " is not one of the databases fully supported for function calls " +"-- supported are: " + supportedDatabaseProductsForFunctions);}if (accessProcedureColumnMetaData) {logger.warn("Metadata processing disabled - you must specify all parameters explicitly");accessProcedureColumnMetaData = false;}}}else {if (!supportedDatabaseProductsForProcedures.contains(databaseProductName)) {if (logger.isWarnEnabled()) {logger.warn(databaseProductName + " is not one of the databases fully supported for procedure calls " +"-- supported are: " + supportedDatabaseProductsForProcedures);}if (accessProcedureColumnMetaData) {logger.warn("Metadata processing disabled - you must specify all parameters explicitly");accessProcedureColumnMetaData = false;}}}CallMetaDataProvider provider;if ("Oracle".equals(databaseProductName)) {provider = new OracleCallMetaDataProvider(databaseMetaData);}else if ("DB2".equals(databaseProductName)) {provider = new Db2CallMetaDataProvider((databaseMetaData));}else if ("Apache Derby".equals(databaseProductName)) {provider = new DerbyCallMetaDataProvider((databaseMetaData));}else if ("PostgreSQL".equals(databaseProductName)) {provider = new PostgresCallMetaDataProvider((databaseMetaData));}else if ("Sybase".equals(databaseProductName)) {provider = new SybaseCallMetaDataProvider((databaseMetaData));}else if ("Microsoft SQL Server".equals(databaseProductName)) {provider = new SqlServerCallMetaDataProvider((databaseMetaData));}else {provider = new GenericCallMetaDataProvider(databaseMetaData);}if (logger.isDebugEnabled()) {logger.debug("Using " + provider.getClass().getName());}provider.initializeWithMetaData(databaseMetaData);if (accessProcedureColumnMetaData) {provider.initializeWithProcedureColumnMetaData(databaseMetaData, context.getCatalogName(), context.getSchemaName(), context.getProcedureName());}return provider;}});}catch (MetaDataAccessException ex) {throw new DataAccessResourceFailureException("Error retreiving database metadata", ex);}}

TableMetaDataProviderFactory創(chuàng)建TableMetaDataProvider工廠類,其創(chuàng)建過程如下:

/*** Create a TableMetaDataProvider based on the database metedata* @param dataSource used to retrieve metedata* @param context the class that holds configuration and metedata* @param nativeJdbcExtractor the NativeJdbcExtractor to be used* @return instance of the TableMetaDataProvider implementation to be used*/public static TableMetaDataProvider createMetaDataProvider(DataSource dataSource,final TableMetaDataContext context, final NativeJdbcExtractor nativeJdbcExtractor) {try {return (TableMetaDataProvider) JdbcUtils.extractDatabaseMetaData(dataSource,new DatabaseMetaDataCallback() {@Overridepublic Object processMetaData(DatabaseMetaData databaseMetaData) throws SQLException {String databaseProductName =JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());boolean accessTableColumnMetaData = context.isAccessTableColumnMetaData();TableMetaDataProvider provider;if ("Oracle".equals(databaseProductName)) {provider = new OracleTableMetaDataProvider(databaseMetaData,context.isOverrideIncludeSynonymsDefault());}else if ("HSQL Database Engine".equals(databaseProductName)) {provider = new HsqlTableMetaDataProvider(databaseMetaData);}else if ("PostgreSQL".equals(databaseProductName)) {provider = new PostgresTableMetaDataProvider(databaseMetaData);}else if ("Apache Derby".equals(databaseProductName)) {provider = new DerbyTableMetaDataProvider(databaseMetaData);}else {provider = new GenericTableMetaDataProvider(databaseMetaData);}if (nativeJdbcExtractor != null) {provider.setNativeJdbcExtractor(nativeJdbcExtractor);}if (logger.isDebugEnabled()) {logger.debug("Using " + provider.getClass().getSimpleName());}provider.initializeWithMetaData(databaseMetaData);if (accessTableColumnMetaData) {provider.initializeWithTableColumnMetaData(databaseMetaData, context.getCatalogName(),context.getSchemaName(), context.getTableName());}return provider;}});}catch (MetaDataAccessException ex) {throw new DataAccessResourceFailureException("Error retrieving database metadata", ex);}}

?3.4?使用SqlParameterSource提供參數(shù)值?
使用Map來指定參數(shù)值有時候工作得非常好,但是這并不是最簡單的使用方式。Spring提供了一些其他的SqlParameterSource實現(xiàn)類來指定參數(shù)值。 我們首先可以看看BeanPropertySqlParameterSource類,這是一個非常簡便的指定參數(shù)的實現(xiàn)類,只要你有一個符合JavaBean規(guī)范的類就行了。它將使用其中的getter方法來獲取參數(shù)值。

SqlParameter 封裝了定義sql 參數(shù)的對象。

CallableStateMentCallback,PrePareStateMentCallback,StateMentCallback,ConnectionCallback回調(diào)類分別對應(yīng)JdbcTemplate中的不同處理方法。

3.5 simple實現(xiàn)

4. DataSource

? ? ? spring通過DataSource獲取數(shù)據(jù)庫的連接。Datasource是jdbc 規(guī)范的一部分,它通過ConnectionFactory獲取。一個容器和框架可以在應(yīng)用代碼層中隱藏連接池和事務(wù)管理。

? ? ?當(dāng)使用spring的jdbc層,你可以通過JNDI來獲取DataSource,也可以通過你自己配置的第三方連接池實現(xiàn)來獲取。流行的第三方實現(xiàn)由apache Jakarta Commons dbcp和c3p0.

TransactionAwareDataSourceProxy作為目標(biāo)DataSource的一個代理, 在對目標(biāo)DataSource包裝的同時,還增加了Spring的事務(wù)管理能力, 在這一點上,這個類的功能非常像J2EE服務(wù)器所提供的事務(wù)化的JNDI DataSource。?

Note?
該類幾乎很少被用到,除非現(xiàn)有代碼在被調(diào)用的時候需要一個標(biāo)準(zhǔn)的 JDBC DataSource接口實現(xiàn)作為參數(shù)。 這種情況下,這個類可以使現(xiàn)有代碼參與Spring的事務(wù)管理。通常最好的做法是使用更高層的抽象 來對數(shù)據(jù)源進行管理,比如JdbcTemplate和DataSourceUtils等等。

注意:DriverManagerDataSource僅限于測試使用,因為它沒有提供池的功能,這會導(dǎo)致在多個請求獲取連接時性能很差。

5. object模塊

?

6.?JdbcTemplate是core包的核心類。

它替我們完成了資源的創(chuàng)建以及釋放工作,從而簡化了我們對JDBC的使用。 它還可以幫助我們避免一些常見的錯誤,比如忘記關(guān)閉數(shù)據(jù)庫連接。 JdbcTemplate將完成JDBC核心處理流程,比如SQL語句的創(chuàng)建、執(zhí)行,而把SQL語句的生成以及查詢結(jié)果的提取工作留給我們的應(yīng)用代碼。 它可以完成SQL查詢、更新以及調(diào)用存儲過程,可以對ResultSet進行遍歷并加以提取。 它還可以捕獲JDBC異常并將其轉(zhuǎn)換成org.springframework.dao包中定義的,通用的,信息更豐富的異常。?

使用JdbcTemplate進行編碼只需要根據(jù)明確定義的一組契約來實現(xiàn)回調(diào)接口。 PreparedStatementCreator回調(diào)接口通過給定的Connection創(chuàng)建一個PreparedStatement,包含SQL和任何相關(guān)的參數(shù)。 CallableStatementCreateor實現(xiàn)同樣的處理,只不過它創(chuàng)建的是CallableStatement。 RowCallbackHandler接口則從數(shù)據(jù)集的每一行中提取值。?

我們可以在DAO實現(xiàn)類中通過傳遞一個DataSource引用來完成JdbcTemplate的實例化,也可以在Spring的IoC容器中配置一個JdbcTemplate的bean并賦予DAO實現(xiàn)類作為一個實例。 需要注意的是DataSource在Spring的IoC容器中總是配制成一個bean,第一種情況下,DataSource bean將傳遞給service,第二種情況下DataSource bean傳遞給JdbcTemplate bean。

7.?NamedParameterJdbcTemplate類為JDBC操作增加了命名參數(shù)的特性支持,而不是傳統(tǒng)的使用('?')作為參數(shù)的占位符。NamedParameterJdbcTemplate類對JdbcTemplate類進行了封裝, 在底層,JdbcTemplate完成了多數(shù)的工作。

小結(jié):

傳統(tǒng)的JDBC對數(shù)據(jù)庫的操作,有很多重復(fù)的代碼,這樣給程序員帶來了很多額外的工作量,Spring提供了JDBC模板很好的解決了這個問題,由于傳統(tǒng)的方法比較簡單,在這里不介紹了,直接說模板吧。 使用JDBC模板: Spring的JDBC框架能夠承擔(dān)資源管理和異常處理的工作。對于JDBC來說,Spring提供了3個模板類
  • JdbcTemplate:Spring里最基本的JDBC模板,利用JDBC和簡單的索引參數(shù)查詢提供對數(shù)據(jù)庫的簡單訪問。
  • NamedParameterJdbcTemplate:能夠在執(zhí)行查詢時把值綁定到SQL里的命名參數(shù),而不是使用索引參數(shù)。
  • SimpleJdbcTemplate:利用Java 5 的特性,比如自動裝箱、通用(generic)和可變參數(shù)類表來簡化JDBC模板的使用。

?參考文獻:

http://www.360doc.com/content/14/0625/22/834950_389749909.shtml

http://www.360doc.com/content/14/0625/23/834950_389759601.shtml

轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/4462351.html

總結(jié)

以上是生活随笔為你收集整理的spring源码分析之spring-jdbc模块详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。