最近我读了一篇有关@Transactional注释的文章。
https://dzone.com/articles/spring-transactional-amp-exceptions
但是我不知道如何在我的应用程序中找到这类问题。但是,我尝试编写测试用例。我正在使用https://www.archunit.org/库
public class TransactionalAnnotationTest {
List<String> names = new ArrayList<>();
@Test
public void testTransactionalWithCheckedExceptionMustSetRollbackFor() {
JavaClasses importedClasses = new ClassFileImporter().importPackages("com.example.demo");
List<String> issuesList = new ArrayList<>();
for (JavaClass clz : importedClasses) {
clz.getMethods().stream().filter(m -> m.isAnnotatedWith(Transactional.class)
&& m.getModifiers().contains(JavaModifier.PUBLIC))
.filter(m -> m.getThrowsClause().containsType(JavaClass.Predicates.assignableTo(Exception.class)))
.filter(m -> !m.getAnnotationOfType(Transactional.class).readOnly()
&& !Arrays.asList(m.getAnnotationOfType(Transactional.class).rollbackFor())
.contains(Throwable.class))
.forEach(m -> {
String name = String.format("%s.%s", clz.getName(), m.getName());
issuesList.add(name);
});
}
if (!issuesList.isEmpty()) {
throw new RuntimeException(issuesList + " method must contains rollbackFor.");
}
}
}
但是,这并未找到所有问题。例如,
AccountService.java
@Service
public class AccountService {
//This will rollback
@Transactional(rollbackFor = Throwable.class)
public void transferMoney(int condition) throws Exception {
Account from = accountRepository.getOne(1L);
Account to = accountRepository.getOne(2L);
accountService2.debitFromAccount(from, 100);
accountService2.creditToAccount(to, 100, condition);
}
//This will not rollback as it does not have @Transactional
public void transferMoney2(int condition) throws Exception {
Account from = accountRepository.getOne(3L);
Account to = accountRepository.getOne(4L);
accountService2.debitFromAccount(from, 200);
accountService2.creditToAccount(to, 200, condition);
}
//This will not rollback as it does have @Transactional but no rollback
@Transactional
public void transferMoney3(int condition) throws Exception {
Account from = accountRepository.getOne(3L);
Account to = accountRepository.getOne(4L);
accountService2.debitFromAccount(from, 200);
accountService2.creditToAccount(to, 200, condition);
}
//This will rollback as the accountService2.transferMoney()
// contains rollback
public void transferMoney4(int condition) throws Exception {
accountService2.transferMoney(condition);
someMethod(condition);
}
//This will not rollback as transferMoney has annotation
// but the method is of same class so that Transactional
// is of no use
public void transferMoney5(int condition) throws Exception {
transferMoney(condition);
someMethod(condition);
}
//This will not rollback as transferMoney has annotation
// but the method is of same class so that Transactional
// is of no use
public void transferMoney6(int condition) throws Exception {
transferMoney(condition);
someMethod2(condition);
}
// does not have any repository code
public void someMethod(int condition) throws Exception {
if(condition == 2) {
throw new Exception("Exception from some method");
}
//some more code can be present
}
// does not have any repository code but unintentionally
// have @Transactional annotation
@Transactional
public void someMethod2(int condition) throws Exception {
if(condition == 2) {
throw new Exception("Exception from some method");
}
//some more code can be present
}
}
AccountService2.java
public class AccountService2 {
@Transactional(rollbackFor = Throwable.class)
public void transferMoney(int condition) throws Exception {
Account from = accountRepository.getOne(1L);
Account to = accountRepository.getOne(2L);
debitFromAccount(from, 100);
creditToAccount(to, 100, condition);
}
public void debitFromAccount(Account from, int amount) {
from.setAmount(from.getAmount()-amount);
accountRepository.save(from);
}
public void creditToAccount(Account to, int amount, int condition) throws Exception {
if(1 == condition) {
throw new Exception("Error during credit");
}
to.setAmount(to.getAmount()+amount);
accountRepository.save(to);
}
}
testTransactionalWithCheckedExceptionMustSetRollbackFor()
测试用例返回
java.lang.RuntimeException: [com.example.demo.service.AccountService.transferMoney3, com.example.demo.service.AccountService.someMethod2] method must contains rollbackFor.
这里com.example.demo.service.AccountService.someMethod2
是错误的,而且
它不返回
[com.example.demo.service.AccountService.transferMoney2, com.example.demo.service.AccountService.transferMoney5, com.example.demo.service.AccountService.transferMoney6]
所以我想知道是否存在任何图书馆或任何其他优雅的方式来发现此类问题。