我有一个与事务性边界有关的问题,我无法弄清楚出了什么问题.
@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代码,但这也没有用.
基本上,无论是否引发异常,我都想更新对象的属性.
有任何想法吗 ??
最佳答案
而且它不起作用.因为您从类的另一种方法调用updateSomeProperty(obj1)并尝试更改默认的事务行为(从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添加spring-aspects依赖项:
<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版本很烂:
<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>
您还可以尝试在春季使用加载时间筛选功能,但这是
更加难以配置(这是我的看法),不建议在生产环境中使用它(如我在几篇文章中所读).但是,如果您决定使用它,则可以在Web和Spring参考手册中找到很多信息.
如果您想在不使用maven的情况下使用编译时耗损,那么我不知道如何配置它. (我只用Maven测试过).您可以尝试在Web上找到此类信息,但我不建议这样做,因为使用Maven可以更轻松地处理依赖项(在本示例中,添加必要的插件).
这是我用于测试的示例:
>一些界面:
公共接口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块中抛出了异常(我将其作为运行时异常来执行,因为我不需要在上层类中处理它).这对于正确的外部事务回滚是必需的.