1. 程式人生 > >Spring+Hibernate整合時關於Hibernate事務管理的問題

Spring+Hibernate整合時關於Hibernate事務管理的問題

這裡寫圖片描述

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
  }
}

參考