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层实现类和数据库交互。
其他文件没有给出,项目实例博客:http://blog.csdn.net/peng_hong_fu/article/details/53384010
源代码:http://pan.baidu.com/s/1nvdf0Id
利用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
}
}
参考
spring中使用parent属性来减少配置:https://my.oschina.net/u/1984151/blog/295996
Spring中如何配置Hibernate事务 http://blog.csdn.net/jianxin1009/article/details/9202907
Spring配置事务中的 transactionAttributes 各属性含义 http://blog.csdn.net/jianxin1009/article/details/9202907
Spring框架笔记 http://blog.csdn.net/peng_hong_fu/article/details/53089012