我正在使用Spring和Hibernate在我正在处理的一个应用程序中,我遇到了处理事务的问题.
我有一个加载一些实体从数据库中的服务类,修改了一些它们的值,然后(当一切都有效)提交这些更改到数据库.如果新值无效(我只能在设置后检查),我不想保留更改.为了防止Spring/Hibernate保存更改,我在方法中抛出异常.但是会导致以下错误:
Could not commit JPA transaction: Transaction marked as rollbackOnly
Run Code Online (Sandbox Code Playgroud)
这是服务:
@Service
class MyService {
@Transactional(rollbackFor = MyCustomException.class)
public void doSth() throws MyCustomException {
//load entities from database
//modify some of their values
//check if they are valid
if(invalid) { //if they arent valid, throw an exception
throw new MyCustomException();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我调用它的方式:
class ServiceUser {
@Autowired
private MyService myService;
public void method() {
try {
myService.doSth();
} catch (MyCustomException e) {
// ...
}
}
}
Run Code Online (Sandbox Code Playgroud)
我期望发生的事情:没有对数据库的更改,也没有对用户可见的异常. …
我有这种情况:
所以步骤1,2,3,4应该在一个交易中,或者步骤1,2,3,5
我的流程从这里开始(这是一个计划任务):
public class ReceiveMessagesJob implements ScheduledJob {
// ...
@Override
public void run() {
try {
processMessageMediator.processNextRegistrationMessage();
} catch (Exception e) {
e.printStackTrace();
}
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
我在ProcessMessageMediator中的主要功能(processNextRegistrationMessage):
public class ProcessMessageMediatorImpl implements ProcessMessageMediator {
// ...
@Override
@Transactional
public void processNextRegistrationMessage() throws ProcessIncomingMessageException {
String refrenceId = null;
MessageTypeEnum registrationMessageType = MessageTypeEnum.REGISTRATION;
try {
String messageContent = incomingMessageService.fetchNextMessageContent(registrationMessageType);
if (messageContent == null) {
return;
}
IncomingXmlModel …Run Code Online (Sandbox Code Playgroud) 在REQUIRED传播的情况下,当调用方法本身是transactionnal时,如果它们不同,当前方法是否会覆盖封闭的事务属性(例如rollbackFor)?
插图:
Class A {
@Transactional(propagation = Propagation.REQUIRED,
rollbackFor = { SomeException.class})
void foo() {
try {
b.bar();
} catch (OtherException e) {
// is the transaction marked as rollback-only at this point ?
}
}
}
Class B {
@Transactional(propagation = Propagation.REQUIRED,
rollbackFor = { OtherException.class})
void bar() {
[...]
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:
好吧,我想避免琐碎的范围答案,所以我们要清楚,我知道弹簧传播处理.
如果你不是,下面是文档的相关部分,我只想澄清关于我上面例子的第一部分:
PROPAGATION_REQUIRED
当传播设置为PROPAGATION_REQUIRED时,将为应用该设置的每个方法创建逻辑事务范围.每个这样的逻辑事务范围可以单独确定仅回滚状态,外部事务范围在逻辑上独立于内部事务范围.当然,在标准PROPAGATION_REQUIRED行为的情况下,所有这些范围将映射到同一物理事务.因此,内部事务范围中的仅回滚标记集确实会影响外部事务实际提交的机会(正如您所期望的那样).
但是,在内部事务作用域设置仅回滚标记的情况下,外部事务尚未决定回滚本身,因此回滚(由内部事务作用域静默触发)是意外的.此时抛出相应的UnexpectedRollbackException.这是预期的行为,因此事务的调用者永远不会被误导,假设在实际上没有执行提交.因此,如果内部事务(外部调用者不知道)以静默方式将事务标记为仅回滚,则外部调用者仍会调用commit.外部调用者需要接收UnexpectedRollbackException以清楚地指示已执行回滚.
我的问题可以改写为:
逻辑事务范围是否包含事务属性?
我的Spring/Java Web应用程序具有@Transactional可以触及数据库的服务:
@Transactional
public class AbstractDBService { ... }
Run Code Online (Sandbox Code Playgroud)
所需的功能适用于任何未被捕获的 throwable,它会在服务层之外传播,从而导致回滚.有点惊讶这不是默认行为,但经过一些谷歌搜索后尝试:
@Transactional(rollbackFor = Exception.class)
Run Code Online (Sandbox Code Playgroud)
这似乎有效,除非故意吞下异常并且不再重新抛出异常.(特殊情况是当找不到实体时.猜测这可能会被重新设计为不会抛出异常,但期望不可避免地存在其他例子 - 例如,InterruptedException在使用时会想到一个实体Thread.sleep()).然后春天抱怨:
org.springframework.transaction.TransactionSystemException:无法提交JPA事务; 嵌套异常是javax.persistence.RollbackException:事务标记为rollbackOnly ... truncated .. 引起:javax.persistence.RollbackException:在org.hibernate.jpa.internal.TransactionImpl.commit上标记为rollbackOnly的事务(TransactionImpl.java:58 )org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
我在这里错过了什么吗?...有没有办法告诉Spring回滚所有未被捕获的扔掉的东西?