Spring+Hibernate整合時關於Hibernate事務管理的問題
阿新 • • 發佈:2019-02-12
org.springframework.dao.InvalidDataAccessApiUsageException: Write
operations are not allowed in read-only mode (FlushMode.MANUAL):
Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
這是我在學習Spring整合Hibernate時遇到最頭疼的問題,初學者解決很困難,主要是因為在 Spring 配置Hibernate事務的方法有很多種(有tx標籤,代理,註釋等方法),所有關於解決的方法有很多,我開始也是一頭霧水。下面是我測試的三種方式.
測試專案的結構
Spring通過Bean建立物件,一種面向介面程式設計的思想;測試專案中,業務層實現類中注入了Dao層, 具體由DAO層實現類和資料庫互動。
利用tx標籤配置事務
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd " >
<!--配置Bean-->
<bean id="stockServiceBean" class="com.jxust.service.impl.StockServiceImpl" >
<property name="stockDao" ref="stockDaoBean" />
</bean>
<bean id="stockDaoBean" class="com.jxust.dao.impl.StockDaoImpl" >
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
.
.
<!-- 省略了其他一些配置-->
.
.
<!-- 配置事務容器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置事務通知屬性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 定義事務傳播屬性 -->
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="new*" propagation="REQUIRED" />
<tx:method name="set*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="change*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="load*" propagation="REQUIRED" read-only="true" />
<tx:method name="*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 定義事務入口 需要aspectjweaver-1.x.x.jar包-->
<aop:config>
<aop:pointcut id="allDaoMethod" expression="execution(* com.jxust.dao.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="allDaoMethod" />
</aop:config>
</beans>
除了配置Bean和
<aop:config>
之外,其他的都是大同小異
<aop:pointcut id="allDaoMethod" expression="execution(* com.jxust.dao.*.*(..))" />
expression 需要配置成,dao層實現類的路徑,因為這裡是指向各種增刪改查,和資料庫互動的類。(*號的寫法能匹配很多到類)
*
號後要空一格,否則會出現下面的異常
expecting 'name pattern' at character position 29
execution(*com.jxust.dao.*.*(..))
^
利用代理配置事務
使用代理,指的是,具體執行資料庫互動的Dao層實現類的Bean,交給代理Bean管理,因為代理Bean繼承了定義事務規則的抽象Bean,所有能對Dao層實現類進行事務管理。
<bean id="stockServiceBean" class="com.jxust.service.impl.StockServiceImpl" >
<property name="stockDao" ref="userDaoProxy" />
</bean>
使用代理後,業務層實現類中,注入的Dao層,就要是這個代理Bean了,userDaoProxy 為代理Bean的Id。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd ">
<!--配置Bean-->
<bean id="stockServiceBean" class="com.jxust.service.impl.StockServiceImpl" >
<property name="stockDao" ref="userDaoProxy" />
</bean>
<bean id="stockDaoBean" class="com.jxust.dao.impl.StockDaoImpl" >
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!--利用代理配置事務。 -->
<!-- 配置事務容器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 定義事務規則 -->
<bean id="transactionProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="sav*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- 定義事務入口 -->
<bean id="userDaoProxy" parent="transactionProxy">
<property name="target" ref="stockDaoBean"></property>
</bean>
</beans>
事務註解的方式
開啟事務註解方式,配置最簡單,也方便檢視具體方法是否使用了事務。
<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 開啟事務註解 -->
<tx:annotation-driven transaction-manager="transactionManager" />
在業務層的實現類中,要使用@Transactional
註解
package com.jxust.service.impl;
import org.springframework.transaction.annotation.Transactional;
import com.jxust.dao.StockDao;
import com.jxust.model.Stock;
import com.jxust.service.StockService;
/**
* 股票業務層實現類
* @author Peng
* @Date2016年11月28日下午2:37:07
*/
@Transactional
public class StockServiceImpl implements StockService {
//注入Dao層
private StockDao stockDao;
public void setStockDao(StockDao stockDao) {
this.stockDao = stockDao;
}
.
.
.
@Transactional註解
屬性 | 型別 | 描述 |
---|---|---|
value | String | 可選的限定描述符,指定使用的事務管理器 |
propagation | enum: Propagation | 可選的事務傳播行為設定 |
isolation | enum: Isolation | 可選的事務隔離級別設定 |
readOnly | boolean | 讀寫或只讀事務,預設讀寫 |
timeout | int (in seconds granularity) | 事務超時時間設定 |
rollbackFor | Class物件陣列,必須繼承自Throwable | 導致事務回滾的異常類陣列 |
rollbackForClassName | 類名陣列,必須繼承自Throwable | 導致事務回滾的異常類名字陣列 |
noRollbackFor | Class物件陣列,必須繼承自Throwable | 不會導致事務回滾的異常類陣列 |
noRollbackForClassName | 類名陣列,必須繼承自Throwable | 不會導致事務回滾的異常類名字陣列 |
@Transactional 可以作用於介面、介面方法、類以及類方法上。當作用於類上時,該類的所有 public 方法將都具有該型別的事務屬性,同時,我們也可以在方法級別使用該標註來覆蓋類級別的定義。
@Transactional(readOnly = true)
public class FooServiceImpl implements FooService {
public Foo getFoo(String fooName) {
// do something
}
//方法上註解屬性會覆蓋類註解上的相同屬性
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}