1. 程式人生 > >Spring整合hibernate4:事務管理

Spring整合hibernate4:事務管理

Spring和Hibernate整合後,通過Hibernate API進行資料庫操作時發現每次都要opensession,close,beginTransaction,commit,這些都是重複的工作,我們可以把事務管理部分交給spring框架完成。

配置事務(xml方式)

使用spring管理事務後在dao中不再需要呼叫beginTransaction和commit,也不需要呼叫session.close(),使用API  sessionFactory.getCurrentSession()來替代sessionFactory.openSession()

 1 @Repository
 2
public class UserDaoImpl implements UserDao { 3 @Autowired 4 private SessionFactory sessionFactory; 5 6 public User findUserById(int id) { 7 Session session = sessionFactory.getCurrentSession(); 8 User user = (User)session.get(User.class, id); 9 session.delete(user);
10 11 return user; 12 } 13 }

採用getCurrentSession()建立的session會繫結到當前執行緒中,而採用openSession()建立的session則不會。

採用getCurrentSession()建立的session在commit或rollback時會自動關閉,而採用openSession()建立的session必須手動關閉。

使用getCurrentSession()需要在hibernate.cfg.xml檔案中加入如下配置:

* 如果使用的是本地事務(jdbc事務)

<property name="hibernate.current_session_context_class">thread</property>

* 如果使用的是全域性事務(jta事務)

<property name="hibernate.current_session_context_class">jta</property>

如果採用的時Hibernate4,使用getCurrentSession()必須配置事務,否則無法取到session

applicationContext.xml配置

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans
 3     xmlns="http://www.springframework.org/schema/beans"
 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5     xmlns:p="http://www.springframework.org/schema/p"
 6     xmlns:context="http://www.springframework.org/schema/context"
 7     xmlns:aop="http://www.springframework.org/schema/aop"
 8     xmlns:tx="http://www.springframework.org/schema/tx"
 9     xmlns:jpa="http://www.springframework.org/schema/data/jpa"
10     xmlns:cache="http://www.springframework.org/schema/cache"
11     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
12                         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
13                         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
14                         http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
15                         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
16                         http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
17     
18     <context:component-scan base-package="dao"/>
19     <context:component-scan base-package="service"/>
20     <context:component-scan base-package="test"/>
21     
22     <context:property-placeholder location="classpath:dbcp.properties"/>
23     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
24         <property name="driverClassName" value="${driverClassName}" />
25         <property name="url" value="${url}" />
26         <property name="username" value="${mysqlusername}" />
27         <property name="password" value="${mysqlpassword}" />
28         <property name="maxActive" value="${maxActive}" />
29         <property name="maxIdle" value="${maxIdle}" />
30         <property name="minIdle" value="${minIdle}" />
31         <property name="maxWait" value="${maxWait}" />
32         <property name="initialSize" value="${initialSize}" />
33         <property name="logAbandoned" value="${logAbandoned}" />
34         <property name="removeAbandoned" value="${removeAbandoned}" />
35         <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
36         <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
37         <property name="numTestsPerEvictionRun" value="${numTestsPerEvictionRun}" />
38     </bean>
39     
40     <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
41         <property name="dataSource" ref="dataSource" />
42         
43         <property name="hibernateProperties">
44             <props>
45                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
46                 <prop key="hibernate.show_sql">true</prop>
47                 <prop key="current_session_context_class">thread</prop>
48             </props>
49         </property>
50         
51         <property name="packagesToScan">
52             <list>
53                 <value>po</value>
54             </list>
55         </property>
56     </bean>
57 
58     
59     <!--hibernate4必須配置為開啟事務 否則 getCurrentSession()獲取不到--> 
60     <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
61         <property name="sessionFactory" ref="sessionFactory"></property>  
62     </bean>
63     
64     <tx:advice id="txAdvice" transaction-manager="txManager">
65         <tx:attributes>
66             <tx:method name="find*" propagation="REQUIRED" />
67             <tx:method name="*" read-only="true"/>
68         </tx:attributes>
69     </tx:advice>
70     
71     <aop:config proxy-target-class="true">
72         <!-- <aop:advisor advice-ref="txAdvice" pointcut="execution(* dao.*.*(..))"/> -->
73         <aop:pointcut expression="execution(* dao.*.*(..))" id="pointcut"/>
74         <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
75     </aop:config>
76 
77 </beans>

Spring中Propagation類的事務屬性詳解:

PROPAGATION_REQUIRED:支援當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。 

PROPAGATION_SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行。 

PROPAGATION_MANDATORY:支援當前事務,如果當前沒有事務,就丟擲異常。 

PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。

PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 

PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則丟擲異常。 

PROPAGATION_NESTED:支援當前事務,如果當前事務存在,則執行一個巢狀事務,如果當前沒有事務,就新建一個事務。

配置事務(宣告方式)

需要在xml配製中設定<tx:annotation-driven transaction-manager="transactionManager"

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans
 3     xmlns="http://www.springframework.org/schema/beans"
 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5     xmlns:p="http://www.springframework.org/schema/p"
 6     xmlns:context="http://www.springframework.org/schema/context"
 7     xmlns:aop="http://www.springframework.org/schema/aop"
 8     xmlns:tx="http://www.springframework.org/schema/tx"
 9     xmlns:jpa="http://www.springframework.org/schema/data/jpa"
10     xmlns:cache="http://www.springframework.org/schema/cache"
11     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
12                         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
13                         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
14                         http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
15                         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
16                         http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
17     
18     <context:component-scan base-package="dao"/>
19     <context:component-scan base-package="service"/>
20     <context:component-scan base-package="test"/>
21     
22     <context:property-placeholder location="classpath:dbcp.properties"/>
23     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
24         <property name="driverClassName" value="${driverClassName}" />
25         <property name="url" value="${url}" />
26         <property name="username" value="${mysqlusername}" />
27         <property name="password" value="${mysqlpassword}" />
28         <property name="maxActive" value="${maxActive}" />
29         <property name="maxIdle" value="${maxIdle}" />
30         <property name="minIdle" value="${minIdle}" />
31         <property name="maxWait" value="${maxWait}" />
32         <property name="initialSize" value="${initialSize}" />
33         <property name="logAbandoned" value="${logAbandoned}" />
34         <property name="removeAbandoned" value="${removeAbandoned}" />
35         <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
36         <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
37         <property name="numTestsPerEvictionRun" value="${numTestsPerEvictionRun}" />
38     </bean>
39     
40     <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
41         <property name="dataSource" ref="dataSource" />
42         
43         <property name="hibernateProperties">
44             <props>
45                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
46                 <prop key="hibernate.show_sql">true</prop>
47                 <prop key="current_session_context_class">thread</prop>
48             </props>
49         </property>
50         
51         <property name="packagesToScan">
52             <list>
53                 <value>po</value>
54             </list>
55         </property>
56     </bean>
57 
58     
59     <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
60         <property name="sessionFactory" ref="sessionFactory"></property>  
61     </bean>
62     <tx:annotation-driven transaction-manager="txManager"/>
63     
64 </beans>

事物註解方式: @Transactional

當標於類前時,標示類中所有方法都進行事物處理,以下程式碼在service層進行事務處理(給Service層配置事務是比較好的方式,因為一個Service層方法操作可以關聯到多個DAO的操作。在Service層執行這些Dao操作,多DAO操作有失敗全部回滾,成功則全部提交。)

 1 @Service
 2 @Transactional
 3 public class UserServiceImpl implements UserService {
 4     @Autowired
 5     private UserDao userDao;
 6     
 7     public User getUserById(int id) {
 8         return userDao.findUserById(id);
 9     }
10 }

當類中某些方法不需要事物時:

 1 @Service
 2 @Transactional
 3 public class UserServiceImpl implements UserService {
 4     @Autowired
 5     private UserDao userDao;
 6     
 7     @Transactional(propagation = Propagation.NOT_SUPPORTED)
 8     public User getUserById(int id) {
 9         return userDao.findUserById(id);
10     }
11 }

@Transactional(propagation=Propagation.REQUIRED) 

如果有事務, 那麼加入事務, 沒有的話新建一個(預設情況下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) 
容器不為這個方法開啟事務
@Transactional(propagation=Propagation.REQUIRES_NEW) 
不管是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
@Transactional(propagation=Propagation.MANDATORY) 
必須在一個已有的事務中執行,否則丟擲異常
@Transactional(propagation=Propagation.NEVER) 
必須在一個沒有的事務中執行,否則丟擲異常(與Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) 
如果其他bean呼叫這個方法,在其他bean中宣告事務,那就用事務.如果其他bean沒有宣告事務,那就不用事務.

事物超時設定:
@Transactional(timeout=30) //預設是30秒

事務隔離級別:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
讀取未提交資料(會出現髒讀, 不可重複讀) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)
讀取已提交資料(會出現不可重複讀和幻讀)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重複讀(會出現幻讀)
@Transactional(isolation = Isolation.SERIALIZABLE)
序列化

MYSQL: 預設為REPEATABLE_READ級別
SQLSERVER: 預設為READ_COMMITTED

髒讀 : 一個事務讀取到另一事務未提交的更新資料
不可重複讀 : 在同一事務中, 多次讀取同一資料返回的結果有所不同, 換句話說, 
後續讀取可以讀到另一事務已提交的更新資料. 相反, "可重複讀"在同一事務中多次
讀取資料時, 能夠保證所讀資料一樣, 也就是後續讀取不能讀到另一事務已提交的更新資料
幻讀 : 一個事務讀到另一個事務已提交的insert資料