我目前正在开发一个Spring MVC应用程序。我已经配置了一个JDBC TransactionManager,我正在使用AOP XML进行声明式事务管理。但是,即使我将该方法配置为以只读= true运行,它仍然提交交易。
数据库:Oracle 10g
我的database-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schem...ring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:com/mybatis/mappers/*.xml" />
</bean>
<!--
the transactional advice (what 'happens'; see the <aop:advisor/> bean
below)
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true" />
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*" read-only="true" rollback-for="RuntimeException"/>
</tx:attributes>
</tx:advice>
<!--
ensure that the above transactional advice runs for any execution of
an operation defined by the FooService interface
-->
<aop:config>
<aop:pointcut id="fooServiceOperation"
expression="execution(* com.service.EmployeeService.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation" />
</aop:config>
</beans>
我的控制器
package com.service;
import java.util.List;
import com.mybatis.dao.EmployeeMapperInterface;
import com.spring.model.Employee;
public class EmployeeService implements EmployeeBaseService{
EmployeeMapperInterface employeeMapper;
public EmployeeMapperInterface getEmployeeMapper() {
return employeeMapper;
}
public void setEmployeeMapper(EmployeeMapperInterface employeeMapper) {
this.employeeMapper = employeeMapper;
}
@Override
public Employee getEmployeeById(long empId){
//retrieve from database
List empList = employeeMapper.getEmployeeWithId(empId);
if(empList != null && empList.size()>0){
return (Employee) empList.get(0);
}
return null;
}
@Override
public long saveEmployee(Employee employee){
long empId = 0l;
if(employee.getEmpId()==0){
empId = new Long( employeeMapper.insertEmployee(employee));
}else{
employeeMapper.updateEmployee(employee);
empId = employee.getEmpId();
}
try {
System.out.println("gonna sleep");
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return empId;
}
如何阻止自动提交?我也注意到即使我没有放任何事务管理代码,代码仍然会提交。注意,然后调用事务建议,因为当我为RuntimeException设置no-rollback-for然后执行1/0时,它会正确提交数据并在我将rollback-for设置为roll时回滚。 我也通过将线程置于休眠状态来尝试查询超时,即使这不起作用,但我认为超时可能是实际查询,所以没关系。 提前谢谢!
答案 0 :(得分:2)
建议read-only
只是建议。当标记为read-only
时,底层事务管理系统不要求阻止写入,这更像是一个优化提示,说这个方法是只读的,所以你不必担心它改变一切。如果在只读事务中进行了更改,某些事务管理器会抱怨,有些则不会。通常,通过JNDI获得的datasource
不会。在任何情况下,您都不应该依赖read-only
建议来防止更改被写回磁盘。
阻止更改被保留的选项包括:
标记交易rollback only
或抛出具有相同效果的例外
在更改对象之前从事务会话中删除/逐出
克隆对象并使用克隆
答案 1 :(得分:1)
DataSourceTransactionManager
以doBegin
方式开始交易
从这个方法DataSourceUtils.prepareConnectionForTransaction
调用。
在此方法中,您可以看到以下代码块:
if (definition != null && definition.isReadOnly()) {
try {
if (logger.isDebugEnabled()) {
logger.debug("Setting JDBC Connection [" + con + "] read-only");
}
con.setReadOnly(true);
}
catch (SQLException ex) {
因此,您可以配置日志记录框架,将DataSourceUtils
类的日志级别设置为DEBUG。
或者你可以在这个地方设置断点并手动调试。
根据此article,我希望您的Oracle连接上会执行SET TRANSACTION READ ONLY
。
从Oracle docs我们可以看到在成功的情况下获得的好处:
默认情况下,Oracle的一致性模型可保证语句级读取一致性,但不保证事务级读取一致性(可重复读取)。如果您想要事务级读取一致性,并且您的事务不需要更新,则可以指定只读事务。在指示您的事务是只读的之后,您可以针对任何数据库表执行任意数量的查询,因为知道只读事务中每个查询的结果与单个时间点一致。
答案 2 :(得分:1)
只读行为严格针对特定于驱动程序。 Oracle驱动程序完全忽略此标志。例如,如果在只读事务中运行,Oracle中执行的相同更新语句将修改数据库,而在HSQL2中,我获得了数据库级别异常。
我知道除了通过api或异常显式回滚以防止Oracle中的提交之外别无他法。这样,您的代码也可以在不同的驱动程序和数据库之间移植。
答案 3 :(得分:0)
答案在Spring MVC Mybatis transaction commit
还提供详细的堆栈跟踪。 总结一下,