【Spring】【筆記】《Spring In Action》第5章 資料庫處理
阿新 • • 發佈:2019-02-07
5.1 Spring資料訪問原理
DAO 資料訪問物件(data access object)。
DAO提供了資料讀取和寫入到資料庫中的一種方式。他們應該以介面的方式釋出功能,而應用程式的其他部分就可以通過介面來進行訪問了。
實現了鬆耦合程式碼
5.1.1 Spring資料訪問異常體系
不與特定的持久化方式相關聯。這意味著可以使用Spring丟擲一致的異常,而不用關心所選擇的持久化方案。
Spring的資料訪問異常:
Spring的模板類處理資料訪問的固定部分——事務控制、管理資源、異常處理。應用程式相關的資料訪問(建立語句、繫結引數以及整理結果集)在回撥的實現中處理
針對不同的持久化平臺,Spring提供了多個可選的模板
使用資料訪問模板只需將其配置為Spring上下文中的Bean並將其織入到應用程式的DAO中。或者使用Spring的DAO支援類進一步簡化應用程式的DAO配置。
5.1.3 DAO支援類
基於模板-回撥設計,Spring提供了DAO支援類,而將業務自己的DAO類作為它的子類。當編寫應用程式自己的DAO實現時,可以繼承自DAO支援類並呼叫模板獲取方法來直接訪問底層的資料訪問模板。Spring不僅提供了多個數據模板實現類,還為每種模板提供了對應的DAO支援類。
5.2 配置資料來源
無論選擇哪一種SpringDAO的支援方式,都需要配置一個數據源的引用。Spring提供了在Spring上下文中配置資料來源Bean的多種方式,包括:
完全可以在應用程式之外對資料來源進行管理,應用程式只需在訪問資料庫的時候查詢資料來源就可以了。
利用Spring,可以像使用SpringBean那樣配置JNDI中資料來源的引用,並將其裝配到需要的類中。jee名稱空間下的<jee:jndi-lookup>元素可以用於檢索JNDI中的任何物件包括資料來源,並將其用於SpringBean中。
eg: <jee:jndi-lookup id="dataSource"
jndi-name="/jdbc/SpitterDS"
resource-ref="true"/>
jndi-name指定JNDI中資源的名稱。如果只設置了jndi-name屬性,那麼就會根據指定的名稱查詢資料來源。但是如果應用程式執行在Java應用程式伺服器中,則需要將resource-ref屬性設定為true,這樣給定的jndi-name將會自動新增java:comp/env字首
5.2.2 使用資料來源連線池
Spring並沒有提供資料來源連線池實現
Spring推薦使用DBCP(Jakarta Commons Database Connection Pooling)
DBCP包含了多個提供連線池功能的資料來源,其中BasicDataSource是最常用的,因為它易於在Spring中配置,而且類似於Spring自帶的DriverManagerDataSource。
例如:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost/spitter/spitter"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
<property name="initialSize" value="5"/>
<property name="maxActive" value="10"/>
</bean>
前4個屬性是配置BasicDataSource所必須的。屬性driverClassName制定了JDBC驅動類的全限定類名。此外還有多個屬性用來配置資料來源連線池。
5.2.3基於JDBC驅動的資料來源
Spring提供了兩種資料來源物件,均位於org.springframework.jdbc.datasource
從JNDI獲取實體管理器工廠
如果將Spring應用程式部署在應用伺服器中,Spring可能已經建立了EntityManagerFactory並將其置於JNDI中等待查詢使用。這種情況下,可以使用Spring的jee名稱空間下的<jee:jndi-lookup>元素來獲取對EntityManagerFactory 的引用:
<jee:jndi-lookup id="emf" jndi-name="persistence/spitterPU"/>
5.5.2 編寫基於JPA的DAO
Spring對JPA繼承也提供了JpaTemplate模板以及對應的支援類JpaDaoSupport。但是基於模板的JPA已經被棄用了(類似Hibernate)。
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Persistencecontext;
import org.springframework.dao.DataaccessException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotaion.Transactional;
@Repository("spitterDao")
@Transactional
public class JpaspitterDao implements SpitterDao {
private static final String RECENT_SPITTLES = "SELECT s FROM Spittle s";
private static final String ALL_SPITTERS = "SELECT s FROM Spitter s";
private static final String SPITTER_FOR_USERNAME = "SELECT s FROM Spitter s WHERE s.username = :username";
private static final String SPITTLES_BY_USERNAME = "SELECT S FROM Spittle s WHERE s.spitter.username = :username";
@PersistenceContext
private EntityManager em;
public void addSpitter(Spitter spitter) {
em.persist(spitter);
}
public Spitter getSpitterById(long id) {
return em.find(Spitter.class,id);
}
public void saveSpitter(Spitter spitter) {
em.merge(spitter);
}
}
@PersistenceContext註解表明需要將一個EntityManager注入到所標註的成員上。為了在Spring中實現EntityManager注入,需要在Spring應用上下文中配置一個PersistenceAnotationBeanPostProcessor
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
@Repository與開發Hibernate上下文Session的DAO是一隻的。由於沒有模板類來處理異常,所以需要為DAO新增@Repository註解,這樣PersistenceExceptionTranslationPostProcessor就會知道要將這個Bean產生的異常轉換成Spring的同一資料訪問。要使用PersistenceExceptionTranslationPostProcessor,需要將其作為一個Bean裝配到spring中
<bean class="org.springframework.dao.annotaion.PersistenceExceptionTranslationPostProcessor"/>
第五章小結
Spring對JDBC和ORM的框架的支援簡化了各種持久化機制都存在的樣板程式碼,這是我們只需關注與應用程式相關的資料訪問即可。
Spring簡化資料訪問方式之一就是管理資料庫連線的生命週期和ORM框架的Session。通過這種方式,持久化機制的管理對應用程式程式碼時完全透明的。
Spring能夠捕獲框架的特定異常並將其轉換成異常體系中的非檢查型異常。包括將JDBC丟擲的SQLException轉換為含義更豐富的異常
- CannotAcquireLockException
- CannotSerializeTransactionException
- CleanupFailureDataAccessException
- ConcurrencyFailureException
- DataAccessException
- DataAccessResourceFailureException
- DataIntegrityViolationException
- DataRetrievalFailureException
- DeadlockLoserDataAccessException
- EmptyResultDataAccessException
- IncorrectResultSizeDataAceessException
- InvalidDataAccessApiUsageException
- InvalidDataAccessResourceUsageException
- OptimisticLockingFailureException
- PermissionDeniedDataAccessException
- PessimisticLockingFailureException
- PessimisticLockingFailureException
- TypeMismatchDataAccessException
- UncategorizedDataAccessException
DAO模組 | DAO回撥 |
1.準備資源 2.開始事務 | 3.在事務中執行 |
5.提交/回滾事務 6.關閉資源和處理錯誤 | 4.返回資料 |
模板類(org.springframework.*) | 用途 |
jca.cci.core.CciTemplate | JCA CCI連線 |
jdbc.core.JdbcTemplate | JDBC連線 |
jdbc.core.namedparam.NamedParameterJdbcTemplate | 支援命名引數的JDBC連線 |
jdbc.core.simple.SimpleJdbcTemplate | 通過Java 5簡化後的JDBC連線 |
orm.hibernate.HibernateTemplate | Hibernate 2.x的Session |
orm.hibernate3.HibernateTemplate | Hibernate 3.x的Session |
orm.ibatis.SqlMapClientTemplate | iBATIS SqlMap客戶端 |
orm.jdo.JdoTemplate | Java資料物件(Java Data Object)實現 |
orm.jpa.JpaTemplate | Java持久化API的實體管理器 |
DAO支援類(org.springframework.*) | 為誰提供DAO支援 |
jca.cci.support.CcciDaoSupport | JCA CCI連線 |
jdbc.core.support.JdbcDaoSupport | JDBC連線 |
jdbc.core.namedparam.NamedParameterJdbcDaoSupport | 帶有命名引數的JDBC連線 |
jdbc.core.simple.SimpleJdbcDaoSupport | 用Java5進行簡化的JDBC連線 |
orm.hibernate.support.HibernateDaoSupport | Hibernate2.x的Session |
orm.hibernate3.support.HibernateDaoSupport | Hibernate3.x的Session |
orm.ibatis.support.SqlMapClientDaoSupport | iBaTIS SqlMap客戶端 |
orm.jdo.support.JdoDaoSupport | Java資料物件(Java Data Object)實現 |
orm.jpa.support.JpaDaoiSupport | Java持久化API的試題管理器 |
- 通過JDBC驅動程式定義的資料來源
- 通過JNDI查詢的資料來源
- 連線池的資料來源
JNDI(Java Naming and Directory Interface,Java命名和目錄介面)是SUN公司提供的一種標準的Java命名系統介面,JNDI提供統一的客戶端API,通過不同的訪問提供者介面JNDI服務供應介面(SPI)的實現,由管理者將JNDI API對映為特定的命名服務和目錄系統,使得Java應用程式可以和這些命名服務和目錄服務之間進行互動 |
所需jar:commons-dbcp.jar、commons-pool.jar |
池配置屬性 | 所指定的內容 |
initialSize | 池啟動時建立的連線數量 |
maxActive | 同一時間可從池中分配的最多連線數,設定為0表示無限制 |
maxIdle | 池裡不會被釋放的最多控線連線,設定為0表示無限制 |
maxOpenPreparedStatements | 在同一時間能夠從語句池中分配的預處理語句的最大數量,設定為0表示無限制 |
maxWait | 在丟擲 異常之前,池等待連接回收的最大時間(當沒有可用連線時)。如果設定為-1,表示無限等待 |
minEvictableIdleTimeMillis | 連線在池中保持空閒而不被回收的最大時間 |
minIdle | 在不建立新連線的情況下,池中保持空閒的最小連線數 |
poolPreparedStatements | 是否對預處理語句進行池管理(布林值) |
- DriverManagerDataSource:在每個連線請求時都會返回一個新建的連線。與DBCP的BasicDataSource不同,由DriverManagerDataSource提供的連線並沒有進行池化管理
- SingleConnectionDataSource:在每個連線請求時都會返回同一個連線。可以視為只有一個連線的池,但不是嚴格意義上的連線池資料來源
- JDBCTemplate:最基本的Spring JDBC模板,這個模板支援最簡單的JDBC資料庫訪問功能以及簡單的索引引數查詢
- NamedParameterJdbcTemplate:使用該模板類執行查詢時,可以將查詢值以命名引數的形式繫結到SQL中,而不是使用簡單的索引引數
- SimpleJdbctemplate:該模板類利用Java 5的一些特性,如自動裝箱、泛型以及可變引數列表來簡化JDBC模板的使用。
- 延遲載入(Lazy loading):藉助於延遲載入,可以只抓取需要的資料。
- 預先抓取(Eager fetching):與延遲載入相對。藉助預先抓取,可以使用一個查詢獲取完整的關聯物件。
- 級聯(Cascading):更改資料庫中的表會同時修改其他表
- Spring宣告式事務的繼承支援
- 透明的異常處理
- 執行緒安全的、輕量級的模板類
- DAO支援類
- 資源管理
- 應用程式管理型別(Application-managed):當應用程式向實體管理器工廠直接請求實體管理器時,工廠會建立一個實體管理器。在這種模式下,程式要負責開啟或關閉試題管理器並在事務中對其進行控制。這種方式的試題管理器適合於不執行在Java EE容器中的獨立應用程式。
- 容器管理型別(Container-managed):試題管理器由JavaEE建立和管理。應用程式根本不予試題管理器工廠打交道。相反試題管理器直接通過注入或JNDI來獲取。容器負責配置實體管理器工廠。這種型別的實體管理器最適合用於JavaEE容器,在這種情況下會希望在persistence.xml指定的JPA配置之外保持一些自己對JPA的控制。
- LocalEntityManagerFactoryBean生成應用程式管理型別的EntityManagerFactory;
- LocalContainerEntityManagerFactoryBean生成容器管理型別的EntityManagerFactory
- EclipseLinkJpaVendorAdapter
- HibernateJpaVendorAdapter
- OpenJpaVendorAdapter
- TopLinkJpaVendorAdapter
資料庫平臺 | 屬性database的值 |
IBM DB2 | DB2 |
Apache Derby | DERBY |
H2 | H2 |
Hypersonic | HSQL |
Informix | INFORMIX |
MySQL | MYSQL |
Oracle | ORACLE |
PostgresQL | POSTGRESQL |
Microsoft SQL Server | SQLSERVER |
Sybase | SYBASE |