我遇到与交易边界有关的问题,我无法弄清楚出了什么问题。
@Transactional( propagation = Propagation.REQUIRED )
Class A {
void methodA() {
try {
new B().callMethodB(obj)
} catch(Exception e) {
updateSomeProperty(obj1)
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
void updateSomeProperty(Object obj1) {
obj1.setProperty(1);
obj1.save();
}
}
Class B {
public void callMethodB(Object obj) throws Exception {
throws new Exception();
}
}
问题是当抛出错误时我的对象没有更新。我也尝试从方法updateSomeProperty
中解雇SQL代码,但这也没有用。
基本上我想更新对象的属性,无论是否抛出异常 任何想法??
答案 0 :(得分:2)
它应该不起作用。因为您从类的另一个方法调用updateSomeProperty(obj1)并尝试更改默认的Transactional行为(从REQUIRED到REQUIRED_NEW)。但它不会起作用。这就是为什么在发生异常时将回滚所有更改的原因。
默认情况下,Spring为接口创建代理,而@Transactional注释仅应用于公共方法。这个方法应该从“外部”调用。如果你将从类中的另一个方法调用它们,那么@Transactional注释将不起作用。
您还可以更改xml中事务的默认设置(查看属性proxy-target-class和mode)。但我从未改变过这种情况,也不记得它应该如何运作。
<tx:annotation-driven transaction-manager="txManager" mode="..." proxy-target-class="..."/>
修改强>
顺便说一下。这是一个非常好的article about transaction pitfalls。它对我帮助很大。关于交易的文章也很少。编辑2:
再次问好。我认为我找到了解决问题的方法。至少我测试了这个,它对我很有用。 我建议你将事务模式更改为“AspectJ”并使用AspectJ编译时间来筛选项目。这将使您可以通过更改事务行为(对于已启动的嵌套事务)从一个类中的另一个方法调用私有事务方法。在这种情况下,您可以在嵌套事务中提交一些更改,同时回滚外部事务。为此,您需要执行以下步骤:
1)在事务定义中更改事务模式: - 如果你使用xml配置,那么:
<tx:annotation-driven transaction-manager="txManager" mode="aspectj"/>
如果您使用java配置:
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ,proxyTargetClass = false)
2)将aspectj依赖项添加到pom:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
3)将弹簧方面依赖性添加到pom:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>3.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
4)添加maven插件,启用编译时间:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>${compiler.version}</source>
<target>${compiler.version}</target>
<Xlint>ignore</Xlint>
<complianceLevel>${compiler.version}</complianceLevel>
<encoding>UTF-8</encoding>
<verbose>false</verbose>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<!-- <goal>test-compile</goal> -->
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
5)另外我在我的pom中有maven编译器插件,这就是为什么我认为你也可以添加它:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerVersion>${compiler.version}</compilerVersion>
<fork>true</fork>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
*注意:我使用jdk版本1.7+。我的编译器和aspectj的版本是sush:
<compiler.version>1.7</compiler.version>
<aspectj.version>1.6.12</aspectj.version>
我也有其他库的这些版本(但我认为这不是必需的):
<org.springframework.version>3.1.0.RELEASE</org.springframework.version>
<org.hibernate.version>4.1.0.Final</org.hibernate.version>
<org.springdata.version>1.0.2.RELEASE</org.springdata.version>
你也可以尝试在春天使用加载时间,但确实如此 更难配置(这是我的意见),不建议在生产中使用(我在几篇文章中读到)。但是如果你决定使用它,你可以在网络和春季参考文献中找到很多信息。
如果你想在没有maven的情况下使用编译时间,那么我不知道如何配置它。 (我只用maven测试过)。您可以尝试在Web中找到此类信息,但我不建议这样做,因为使用maven可以更容易地处理依赖项(并且在此示例的情况下 - 添加必要的插件)。
以下是我用于测试的示例:
某些界面:
public interface TestClassInterface {
void testMethod();
}
实现此接口的一些测试类:
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class) @零件 公共类TestClass实现了TestClassInterface {
@Autowired
private SpringDataFooDAO fooDao;
public void testMethod() {
try {
Foo foo = fooDao.findOne(2L);
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
foo.setName("should be rolled back");
new ExceptionThrower().doSomething("default string");
} catch(Exception e) {
updateSomeProperty(1L, "Changed name");
throw new RuntimeException(e);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
private void updateSomeProperty(long id, String newFooName) {
System.out.println(" --- ");
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
// Update property of test object.
Foo foo = fooDao.findOne(id);
foo.setName(newFooName);
}
}
另一个具有抛出异常的方法的类:
公共类ExceptionThrower {
public void doSomething(Object obj) throws Exception {
throw new Exception();
}
}
请注意,我从catch块中重新抛出异常(我将此作为运行时异常,因为我不需要在上层类中处理它)。这对于正确的外部事务回滚是必要的。
答案 1 :(得分:1)
查看spring reference了解如何使用@Transactional。 @Transactional在使用弹簧代理时会附带很多**条件,你需要先了解它们才能在代码中应用它。
在代理模式(默认设置)下,只有外部方法调用 通过代理进入是截获的。这意味着 自调用,实际上是目标对象调用中的一个方法 目标对象的另一种方法,不会导致实际的 即使调用的方法被标记,运行时的事务也是如此 @Transactional。
考虑使用AspectJ模式(参见下表中的mode属性) 如果你希望自我调用包含在事务中 好。在这种情况下,首先不会有代理; 相反,目标类将被编织(即,它的字节代码将 被修改)以便将@Transactional变为运行时行为 任何一种方法。
答案 2 :(得分:0)
您可以尝试指定noRollbackFor = RuntimeException.class或您想要的任何其他类,并希望它可以让您更新数据库。即@Transactional(noRollbackFor = RuntimeException.class)
答案 3 :(得分:0)
上述安排非常适合开始新交易并做其他与原始交易不同的事情。
在我的情况下出现问题的一件事是,在另一个事务中,我自己抛出一个异常导致第二个事务再次回滚..
So The thing is beware off exception in the transaction because they ensure that the database state rolls back. It is for what they are meant for.
由于
答案 4 :(得分:0)
创建自定义异常并使用它来抛出,同时请在定义自定义异常时使用@ApplicationException(rollback = false)作为类级别注释。
e.g。
@ApplicationException(rollback=false)
public CustomException extends Exception{