javascript
Spring JDBC-Spring对DAO的支持
- 概述
- Spring的DAO理念
- 統一的異常體系
- 統一的數據訪問模板
- 使用模板和回調機制
- 模板類
- 數據源
- 配置數據源
- DBCP數據源
- C3P0數據源
- 獲取JNDI數據源
- Spring的數據源實現類
- 配置數據源
- 總結
概述
Spring對多個持久化技術提供了集成支持,包括Hibernate、MyBatis、JPA、JDO。 此外Spring還提供了一個簡化JDBC API操作的Spring JDBC框架。
Spring面向DAO制定了一個通用的異常體系,屏蔽了持久化技術的異常,使業務層和具體的持久化技術實現解耦。
另外,Spring提供了模板類簡化各種持久化技術的使用。
通用的異常體系和模板類是Spring整合各種持久化技術的不二法門。
Spring的DAO理念
DAO(DATA Acces Object)是用于訪問數據的對象,雖然大多數情況下存儲在數據庫中,但是也可以存放在文件或者LDAP(輕量目錄訪問協議,Lightweight Directory Access Protocol)中。 DAO不但屏蔽了數據存儲最重介質的不同,也屏蔽了具體的實現技術的不同。
早起,JDBC是主流選擇,近些年,數據庫持久化技術得到了長足的發展。 只要為數據訪問定義好DAO接口,并使用具體的實現技術實現DAO接口的功能,就可以在不同的實現技術之間平滑的切換。
我們來舉個例子 : 如下是一個典型的DAO應用實例,在USerDAO中定義訪問User數據對象的接口方法,業務層通過UserDao操作數據,并使用具體的持久化技術實現USerDao接口方法,這樣就實現了業務層和具體的持久化技術之間的解耦
提供DAO抽象層的好處:
- 首先可以很容易的構造模擬對象,方便單元測試的開展
- 其次在使用切面會有更多的選擇,可以使用JDK動態代理,又可以使用CGLib動態代理
Spring本質上希望以統一的方式整合底層的持久化技術,即以統一的方式進行調用及事務管理,避免讓具體的實現侵入到業務層的代碼中。 由于每種持久化技術都有各自的異常體系,所以Spring提供了統一的異常體系,使不同異常體系的阻抗得以消弭,方便定義出和具體實現技術無關的DAO接口,以及整合到相同的事務管理體系中。
統一的異常體系
統一的異常體系是整合不同的持久化技術的關鍵。 Spring提供了一套和實現技術無關的、面向DAO層語義的異常體系,并通過轉換器將不同持久化技術的異常轉換成Spring的異常
很多正統API或者框架中,檢查型異常被過多的使用,以致在使用API時,代碼中充斥了大量的try/cath樣板式的代碼。這樣就導致一堆異常處理的代碼喧賓奪主侵入到業務代碼中,破壞了代碼的整潔和優雅。
Spring在org.springframework.dao包中提供了一套完備優雅的DAO異常體系,
這些異常都繼承于DataAccessException , 而DataAccessException本身又繼承于NestedRuntimeException, NestedRuntimeException異常以嵌套的方式封裝了源異常。 因此,雖然不同的持久化技術的特定異常被轉換到Spring的DAO異常體系中,但原始的異常信息并不會丟失,我們依然可以通過getCaucse()方法獲取原始的異常信息。
JDBC/Mybatis的異常轉換器為SQLErrorCodeSQLExceptionTranslator
Spring以分類手法建立了異常分類目錄 ,如下所示(為第一層次的異常類,每個異常類下都可能有眾多子異常類),一方面可以使開發者從底層細如針麻的技術細節中脫離出來,另一方面可以選擇感興趣的異常加以處理。
統一的數據訪問模板
Spring為支持持久化技術分別提供了模板訪問的方式,降低了使用各種持久化技術的難度,因此可以大幅度的提供開發效率。
使用模板和回調機制
我們先看一段使用JDBC進行數據操作的簡單代碼,已經盡可能的簡化了整個處理的過程
public void saveUser(User user) {Connection connection = null;PreparedStatement stmt = null;try {// 獲取連接connection = DriverManager.getConnection(url, user, password);// 啟動事物connection.setAutoCommit(false);// 業務操作stmt = connection.prepareStatement("insert into user(id ,name) values(?,?)");stmt.setLong(1, user.getUserId());stmt.setString(2, user.getUserName());// 執行提交stmt.execute();// 提交事務connection.commit();} catch (Exception e) {// 回滾connection.rollback();} finally {// 釋放資源stmt.close();connection.close();}}如上代碼所述,JDBC訪問數據按照如下流程
- 獲取連接
- 開啟事務(如果有需要)
- 執行具體的數據訪問操作
- 提交/回滾事務
- 關閉資源
我們可以看到只有具體的業務操作才是我們關心的, Spring將這些相同的數據訪問流程固化到模板中,并將數據訪問中固定和變化的部分分開,同時保證模板類是線程安全的,以便多個數據訪問線程共享同一個模板實例。變化的部分通過回調接口開放出來,用于定義數據訪問和結果返回的操作。 (如下圖)
這樣我們只需要編寫回調接口,并調用模板類進行數據訪問,就可以得到我們期待的結果:數據訪問成功執行,前置和后置的樣板化工作也按照順序正確執行,在提供開發效率的同時保證了資源使用的正確性,徹底消除了因為忘記進行資源釋放而引起的資源泄漏問題。
模板類
Spring為各種支持的持久化技術都提供了簡化操作的模板和回調,在回調中編寫具體的數據操作邏輯,使用模板執行數據操作,在Spring中這是典型的數據操作模式。
我們來了解下Spring為不同的持久化技術所提供的模板類
| JDBC/Mybatis | org.springframework.jdbc.core.JdbcTemplate |
| Hibernate | org.springframework.orm.hibernate3/4/5.HibernateTemplate |
| JPA | org.springframework.orm.jpa.JpaTemplate |
| JDO | org.springframework.orm.jdo.JdoTemplate |
如果直接使用模板類,則演需要在DAP中定義一個模板對象并提供數據資源。 Spring為每種持久化技術都提供了支持列,支持類中已完成了這樣的功能。 這樣我們只需要擴展這些支持類,就可以直接編寫實際的數據訪問邏輯,因此更加方便。
| JDBC/Mybatis | org.springframework.jdbc.core.JdbcDaoSupport |
| Hibernate | org.springframework.orm.hibernate3/4/5.HibernateDaoSupport |
| JPA | org.springframework.orm.jpa.JpaDaoSupport |
| JDO | org.springframework.orm.jdo.JdoDaoSupport |
這些類都是繼承于dao.support.DaoSupport類, DaoSupport實現了InitializingBean接口,在afterPropertiesSet()接口中檢查模板對象和數據源是否被正確設置,否則將拋出異常。
所有的支持類都是abstract,其目的是希望被繼承使用,而非直接使用
數據源
在Spring中,不但可以通過JNDI獲取應用服務器的數據源,也可以在Spring容器中配置數據源。 此外還可以通過代碼的方式創建一個數據源,以便進行無容器依賴的單元測試。
配置數據源
Spring在第三方依賴包中包含了2個數據源的實現類包
- Apache的DBCP
- C2P0
我們可以在Spring配置文件中利用二者中的任何一個配置數據源。
DBCP數據源
因篇幅原因,另開一篇 Apache-DBCP數據庫連接池解讀
簡略配置如下:
<bean id="dataSourcePR" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"p:driverClassName="${jdbc.driverClassNamePR}"p:url="${jdbc.urlPR}"p:username="${jdbc.usernamePR}"p:password="${jdbc.passwordPR}" />BasicDataSource提供了close()方法關閉數據源,所以必須設定destroy-method=”close”屬性,以便Spring容器關閉時,數據源能夠正常關閉。
假設數據庫為MySQL,如果配置不當,會發生經典的“8小時為” 。
原因是MySQL在默認情況下發現一個連接空閑時間超過8小時,則會在數據庫端自動關閉這個連接。 而數據源并不知道這個連接已經被數據庫關閉了,當它將這個無用的連接返回個某個DAO時,DAO就會拋出無法獲取Connection的異常。
如果采用DBCP默認配置,由于testOnBorrow默認為true,數據源在將連接交給DAO之前,會事先檢查這個連接是否良好,如果連接有問題(在數據庫端被關閉),則回取一個其他的連接給DAP,并不會有“8小時問題”。 但是這樣每次都檢查有效性,在高并發的應用中會帶來性能問題。
一種推薦的高效方式是: 將testOnBorrow設置為false,而將testWhielIdel設置為true,再設置好 timeBetweenEvictionRunsMillis的值。 這樣DBCP將通過一個后臺線程定時的對空閑連接進行檢測,當發現無用的空閑連接(那些被數據庫關閉的連接)時,就會將它們清掉,只要將timeBetweenEvictionRunsMillis設置為小于8小時,那些被MySQL關閉的空閑連接就可以被清除出去,從而避免“8小時問題”
當然,MySQL本身可以通過調整interactive-timeout(以秒為單位)配置參數,更改空閑連接的過期時間, 所以在設置timeBetweenEvictionRunsMillis值時,必須首先獲知MySQL空閑連接的最大過期時間。
C3P0數據源
因篇幅原因,另開一篇 C3P0-數據庫連接池解讀
一個簡單的配置
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass"> <value>oracle.jdbc.driver.OracleDriver</value> </property> <property name="jdbcUrl"> <value>jdbc:oracle:thin:@ip:port:instanceName</value> </property> <property name="user"> <value>artisan</value> </property> <property name="password"> <value>artisan</value> </property> </bean>獲取JNDI數據源
Java Naming and Directory Interface (Java命名和目錄服務接口)
如果應用配置在高性能的應用服務器比如weblogic/websphere等,則可能希望使用應用本身提供的數據源。 應用服務器的數據源使用JNDI開放調用者使用,Spring為此專門提供了引用JNDI數據源的JndiObjectFactoryBean,我們來看一個簡單的配置
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiName" value="java:comp/env/jdbc/自定義name"/> </bean>通過jndiName指定引用的JNDI數據源名稱
Spring為JavaEE資源提供了一個jee命名空間,通過jee命名空間,可以有效的簡化Java EE資源的引用, 如下所示:
<beans xmlns=http://www.springframework.org/schema/beans xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:jee=http://www.springframework.org/schema/jee xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"> <jee:jndi-lookup id="dataSource" jndi-name=" java:comp/env/jdbc/自定義name"/> </beans>Spring的數據源實現類
Spring本身也提供了一個簡單的數據源實現類org.springframework.jdbc.datasource.DriverManagerDataSource
這個類實現了javax.sql.DataSource接口, 但 它并沒有提供池化連接的機制,每次調用getConnection()獲取新連接時,只是簡單地創建一個新的連接。
因此,這個數據源類比較適合在單元測試 或簡單的獨立應用中使用,因為它不需要額外的依賴類。
下面,我們來看一下DriverManagerDataSource的簡單使用。
DriverManagerDataSource ds = new DriverManagerDataSource (); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3309/sampledb"); ds.setUsername("artisan"); ds.setPassword("123456"); Connection actualCon = ds.getConnection();當然,我們也可以通過配置的方式直接使用DriverManagerDataSource。 如下
<!-- Initialization for data source --><bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"><property name = "driverClassName" value = "com.mysql.jdbc.Driver"/><property name = "url" value = "jdbc:mysql://localhost:3309/sampledb"/><property name = "username" value = "artisan"/><property name = "password" value = "123456"/></bean>總結
不管采用何種持久化技術,都需要定義數據源。Spring附帶了兩個數據源的實現類包,你可以自行選擇進行定義。 在實際部署時,我們可能會直接采用應用服 務器本身提供的數據源, 這時,則可以通過JndiObjectFactoryBean或jee命名空間引用JNDI中的數據源
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Spring JDBC-Spring对DAO的支持的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring-AOP 基于Schema配
- 下一篇: Apache-DBCP数据库连接池解读