使用Spring 3.1和Hibernate做持久層
原文連結如下: http://www.baeldung.com/2011/12/02/the-persistence-layer-with-spring-3-1-and-hibernate/
特別說明:感謝大家積極參與【iTran樂譯】第2期!
這是關於Spring持久化系列文章的第一篇。這篇文章將關注使用Spring 3.1和Hibernate來配置和實現持久化層。關於如何一步一步建立基於java配置的Spring上下文和專案的基礎Maven pom,參看這裡。
Spring持久化系列:
**Part 2** – [Simplifying the Data Access Layer with Spring and Java Generics][2]
**Part 3** – [The Persistence Layer with Spring 3.1 and JPA][3]
**Part 4** – [The Persistence Layer with Spring Data JPA][4]
**Part 5** – [Transaction configuration with JPA and Spring 3.1][5]
沒有Spring Templates
從Spring 3.0 and Hibernate 3.0.1開始,我們不再需要使用Spring的*HibernateTemplat*e來管理Hibernate Session,而我們或許可以利用contextual sessions(直接被Hibernate管理的session,他們在一個事務範圍是保持活動的)。
因此,現在的最佳時間就是直接使用Hibernate API而不是HibernateTemplate,這樣便可以有效地將DAO層的實現與Spring完全解耦。
不用template做異常翻譯
HibernateTemplate的職責之一就是異常翻譯,即將低層次的Hibernate異常(它們與Hibernate API耦合緊密)翻譯成高層次,泛化的Spring異常。(譯註:HibernateTemplate即將具體的ORM框架的異常泛化,達到具體ORM與ORM呼叫程式碼的解耦)。
現在即使不用template去幹這些事,異常翻譯仍可以通過為DAO加上@Repository註釋來實現。因為,所有每個被@Repository註釋標記的bean都會被Spring上下午委派一個PersistenceExceptionTranslator作為這個bean的postprocessor。(譯註:postprocessor是Spring bean的一個機制,即呼叫每次呼叫完方法後,都會執行一次postprocessor,此處當你的DAO執行完Hibernate API之後,postprocessor會自動翻譯Hiberante產生的異常)
異常翻譯是通過代理實現的,為了讓Spring能夠建立一個包裹DAO類的代理,DAO類一定不能被宣告為final。
不用template管理Hibernate Session
當Hiberante支援contextual session後,HibernateTemplate基本已經過時了;實際上,這個類的javadoc已經被更新如下提示:
注意: 從Hibernate 3.0.1後, Hibernate事務處理(transactional Hibernate access)程式碼也可
以以簡單的Hiberante方式書寫。因此,對於新開始的專案來說,可以考慮採用標準的Hibernate3
風格訪問資料物件而不是基於{@link org.hibernate.SessionFactory#getCurrentSession()}
基於JAVA註釋的Spring
通過在配置檔案中建立一個Spring factory bean(AnnotationSessionFactoryBean),我們可以建立和管理Hibernate SessionFactory。這會啟用通過classpath掃描自動偵測實體類(entity classes)的功能。注意:使用這個功能需要 Hibernate 3.2+ 和JDK 1.5+。
一個可選的方案是為session factory bean手動指定所有被註釋標記的實體類(entity classes),通過使用setAnnotatedClasses方法。
@Configuration
@EnableTransactionManagement
public class PersistenceHibernateConfig{
@Bean
public AnnotationSessionFactoryBean alertsSessionFactory(){
AnnotationSessionFactoryBean sessionFactory = new AnnotationSessionFactoryBean();
sessionFactory.setDataSource( this.restDataSource() );
sessionFactory.setPackagesToScan( new String[ ] { "org.rest" } );
sessionFactory.setHibernateProperties( this.hibernateProperties() );
return sessionFactory;
}
@Bean
public DataSource restDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName( this.driverClassName );
dataSource.setUrl( this.url );
dataSource.setUsername( "restUser" );
dataSource.setPassword( "restmy5ql" );
return dataSource;
}
@Bean
public HibernateTransactionManager transactionManager(){
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory( this.alertsSessionFactory().getObject() );
return txManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
}
Also, note that cglib must be on the classpath for Java @Configuration classes to work; to better understand the need for cglib as a dependency, see this article. 同樣要注意,為了能使@Configuration類能工作,cglib一定要再classpath裡找得到。想更好地理解依賴cglib的需求,參看這篇文章。
The Spring XML configuration Spring XML配置
同樣的配置(使用XML方式)
基於XML的配置和基於JAVA註釋的配置有一些小的區別。在XML中,一個指向其他bean的引用既可以指向bean或者指向一個用於生成這個bean的工廠。然而在java中,編譯器不允許那樣做,所以SessionFactory首先會從它的工廠裡取出,然後傳遞給transaction manager:
transactionManager.setSessionFactory( this.alertsSessionFactory().getObject() );
Hibernate屬性檔案
通過如下Hibernate屬性配置,實現與Spring的協作:
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=false
注意:MySQL方言只是配置在這裡只是做一個參考,任何Hiberate支援的方言都可以使用。
潛在異常
事務工廠
Hibernate建立事務的協議是通過TransactionFactory介面指定的。為了讓Spring能夠完全管理事務,這個協議的預設實現(JDBCTransactionFactory )會被其相對的預設的Spring實現(SpringTransactionFactory)替換。
它可以通過手動在Hibernate屬性檔案中實現。(這樣做有點多餘): *transaction.factory_class=org.springframework.orm.hibernate3.SpringTransactionFactory*
當前的Session Context
當Hibernate SessionFactory在Spring的上下文裡被它的factory bean建立後,它會建立CurrentSessionContext。這個協定是為了支援current session概念,它的實現通過分析“*hibernate.current_session_context_class*” Hibernate屬性來決定。
將這個屬性設定為“managed”意味著使用為contextual sessions使用受管理的實現(ManagedSessionContext )。這樣就假設了current session正被一個外部實體(external entity)所管理。在我們的Spring上下文裡,那樣會失敗:
org.springframework.orm.hibernate3.HibernateSystemException:
No session currently bound to execution context
在Hibernate配置裡,將屬性設定為“thread”將允許thread-bound strategy;它同樣會與Spring事務管理衝突並造成如下結果:
org.springframework.orm.hibernate3.HibernateSystemException:
persist is not valid without active transaction
為了讓Spring管理事務,這個屬性是必須的“org.springframework.orm.hibernate3.SpringSessionContext”;因為這個設定同樣是預設的,所以顯示的屬性定義將被刪除。
DAO
每一個DAO都給予一個引數化的,抽象的,支援常用泛化操作的DAO類:
public abstract class AbstractHibernateDAO< T extends Serializable >{
private final Class< T > clazz;
@Autowired
SessionFactory sessionFactory;
public void setClazz( final Class< T > clazzToSet ){
this.clazz = clazzToSet;
}
public T findOne( final Long id ){
Preconditions.checkArgument( id != null );
return (T) this.getCurrentSession().get( this.clazz, id );
}
public List< T > findAll(){
return this.getCurrentSession()
.createQuery( "from " + this.clazz.getName() ).list();
}
public void save( final T entity ){
Preconditions.checkNotNull( entity );
this.getCurrentSession().persist( entity );
}
public void update( final T entity ){
Preconditions.checkNotNull( entity );
this.getCurrentSession().merge( entity );
}
public void delete( final T entity ){
Preconditions.checkNotNull( entity );
this.getCurrentSession().delete( entity );
}
public void deleteById( final Long entityId ){
final T entity = this.getById( entityId );
Preconditions.checkState( entity != null );
this.delete( entity );
}
protected final Session getCurrentSession(){
return this.sessionFactory.getCurrentSession();
}
}
這裡有些有趣的事情 - 在之前的討論,抽象DAO不繼承任何Spring template(比如HibernateTemplate)。然而,Hibernate SessionFactory被直接注入到DAO中,然後它將會扮演主要Hibernate API的角色,通過contextual Session可以看到:
this.sessionFactory.getCurrentSession();
同樣注意:為用作泛化操作,實體類在建構函式裡被傳遞:
@Repository
public class FooDAO extends AbstractHibernateDAO< Foo > implements IFooDAO{
public FooDAO(){
setClazz(Foo.class );
}
}
Maven配置
除了之前文章定義的Maven配置以外,還需要加入以下依賴:spring-orm和hibernate-core:
注意對於MySQL被放在這裡作為一個參考( 一個驅動需要配置一個資料來源),任何Hibernate支援的資料庫都可以使用。
總結
這篇文章涵蓋了使用Hibernate和Spring 3.1做持久層的配置和實現(包含使用XML方式和java註釋方式)。討論了停止依賴Spring template做DAO層的理由和配置Spring管理事務和Hibernate Session的陷阱。最後給出了一個輕量級的,乾淨的DAO實現(在編譯時幾乎不依賴Spring)。你可以從github上下載完整的專案。
相關文章
- Spring 持久層整合Spring
- Spring 高階特性------資料持久層Spring
- SSH框架查詢方法(struts2 Spring 3.1 Hibernate 4.1)框架Spring
- 使用Spring Data JPA實現持久化層的簡化開發Spring持久化
- FLEX和spring、hibernate的整合FlexSpring
- Java資料持久層Java
- 使用註解來構造IoC容器 & 基於Spring、Hibernate的通用DAO層與Service層的實現Spring
- Hibernate 持久化物件的狀態持久化物件
- Hibernate持久化物件的狀態持久化物件
- Java 持久層框架之 MyBatisJava框架MyBatis
- 在Java SE下測試CDI Bean和持久層 - relationJavaBean
- Spring 使用Aop 做切面日誌,和許可權。Spring
- Hibernate註解(一)之持久化實體持久化
- 河青的持久層框架hqbatis框架BAT
- Java持久層框架Mybatis入門Java框架MyBatis
- AutoCRUD - PHP 下的透明持久層PHP
- Spring3.1新特性(轉)Spring
- Spring 整合 HibernateSpring
- [摘]Spring讓Hibernate使用者受益良多Spring
- K8S中如何使用Glusterfs做持久化儲存?K8S持久化
- 想用hibernate作資料持久的工作,可行嗎?
- MyBatis--優秀的持久層框架MyBatis框架
- Spring事物管理和hibernate事物管理的疑問Spring
- 讀discuzx3.1 資料庫層筆記資料庫筆記
- 持久層Mybatis3底層原始碼分析,原理解析MyBatisS3原始碼
- redis 是如何做持久化的Redis持久化
- 自己動手寫一個持久層框架框架
- MicroStream + Helidon高效能Java持久層ROSJava
- 框架(Spring、Struts2和Hibernate三者)整合框架Spring
- 在spring和hibernate框架下如何實現DDD思想?Spring框架
- JPA/Hibernate/Spring Data概念Spring
- Spring boot 四 JPA HibernateSpring Boot
- sh_Spring整合HibernateSpring
- 使用Hector和Scala持久化Cassandra資料庫持久化資料庫
- 使用 Hibernate 和 H2 的多租戶Spring Boot開源原始碼專案Spring Boot原始碼
- hibernate學習(六) flush()和clean()區別和使用
- SpringCloud使用Sentinel,Sentinel持久化,Sentinel使用nacos持久化SpringGCCloud持久化
- RxCache 整合 Android 的持久層框架 greenDAO、RoomAndroid框架OOM