描述
發生例外時,排除 Hibernate 托管物體在事務中回滾的最佳方法是什么?
我有一個由 Spring Batch 呼叫的服務。Spring Batch 打開并提交了一個我無法控制的事務。在服務程序中,某些物體被更新。但是,當發生例外時,它會傳播給呼叫者,并且所有處理作業都會回滾。這正是我想要的,除了一個特定的物體,它額外用于跟蹤行程狀態以便于監控和其他原因。如果出現錯誤,我想更新該物體以顯示錯誤狀態(在服務類中)。
例子
我試圖通過打開一個單獨的事務并提交更新來實作這一點:
@Service
@Transactional
public class ProcessService {
@Autowired
private EntityManagerFactory emf;
@Autowired
private ProcessEntryRepository repository;
@Override
public void process(ProcessEntry entry) {
try {
entry.setStatus(ProcessStatus.WORK_IN_PROGRESS);
entry.setTimeWorkInProgress(LocalDateTime.now());
entry = repository.saveAndFlush(entry);
createOrUpdateEntities(entry.getData()); // should all be rolled back in case of exception
entry.setStatus(ProcessStatus.FINISHED);
entry.setTimeFinished(LocalDateTime.now());
repository.saveAndFlush(entry);
} catch (Exception e) {
handleException(entry, e); // <- does not work as expected
throw e; // hard requirement to rethrow here
}
}
private void handleException(ProcessEntry entry, Exception exception) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
StatelessSession statelessSession = null;
Transaction transaction = null;
try {
statelessSession = openStatelessSession();
transaction = statelessSession.beginTransaction();
ProcessEntry entryUnwrap = unwrapHibernateProxy(entry); // to get actual Class
@SuppressWarnings("unchecked")
ProcessEntry entryInSession = (ProcessEntry) statelessSession.get(entryUnwrap.getClass(), entryUnwrap.getId());
entryInSession.setStatus(ProcessStatus.FAILED);
entryInSession.setTimeFailed(LocalDateTime.now());
entryInSession.setStatusMessage(exception.getMessage());
save(transaction, entryInSession);
commit(statelessSession, transaction);
catch (Exception e) {
LOGGER.error("Exception occurred while setting FAILED status on: " entry, e);
rollback(transaction);
} finally {
closeStatelessSession(statelessSession);
}
}
public StatelessSession openStatelessSession() {
EntityManagerFactory entityManagerFactory = emf;
if (emf instanceof MultipleEntityManagerFactorySwitcher) {
entityManagerFactory = ((MultipleEntityManagerFactorySwitcher) emf).currentFactory();
}
return ((HibernateEntityManagerFactory) entityManagerFactory).getSessionFactory()
.openStatelessSession();
}
public static Long save(StatelessSession statelessSession, ProcessEntry entity) {
if (!entity.isPersisted()) {
return (Long) statelessSession.insert(entity.getClass().getName(), entity);
}
statelessSession.update(entity.getClass().getName(), entity);
return entity.getId();
}
public static void commit(StatelessSession statelessSession, Transaction transaction) {
((TransactionContext) statelessSession).managedFlush();
transaction.commit();
}
public static void rollback(Transaction transaction) {
if (transaction != null) {
transaction.rollback();
}
}
public static void closeStatelessSession(StatelessSession statelessSession) {
if (statelessSession != null) {
statelessSession.close();
}
}
}
然而,由于某種原因,這種方法完全凍結了應用程式(或者更確切地說,批處理作業永遠不會完成)save-> !statelessSession.update(..)請注意,已知方法、 、等可在其他背景關系中openStatelessSession作業save。我將它們復制到這個例子中。我也嘗試過加載物體,但在這里我當然得到了.rollbackcommitrepositoryStaleObjectException
我還嘗試@Transaction(Propagation.REQUIRES_NEW)了一個單獨的例外處理程式服務,但它保留了物體管理器中的所有更改,而不僅僅是ProcessEntry物體。您可以事先清除會話,但在我的情況下也凍結了。
uj5u.com熱心網友回復:
這很可能是因為您凍結了資料庫,而不是您的應用程式。您正在嘗試插入一條記錄,該記錄在另一個事務完成之前已經嘗試插入另一個事務。這很可能將資料庫從行鎖變為全表鎖。因此,每筆交易都在等待對方關閉,然后才能繼續前進。
您可以嘗試以下方法之一:
1. Fully roll back and complete the first transaction before trying to redo the insert. Since you are still in the exception processing logic holding the exception from being thrown Spring Batch will not have rolled back the transaction for that step yet.
2. Try to do the the extra insert in one of Spring Batch's one rror() listeners.
3. Do the insert into a separate temp table (you can create as the start of the job, and delete at the end of the job.) and use an onComplete() listener for the step to then copy any entries in that temp table to your intended table.
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/419237.html
標籤:
