考虑一下我有一个方法以ActiveRecord模式样式执行一些工作和日志记录机制:
@Transactional public void doSmt() { try { someOperation(); // can throw exception Logger.logIntoDb(); // if everything is OK } catch {Exception e} { Logger.logIntoDbWithException(e.getMessage()); // log error into db throw new MyCustomException(e); } } public static void logIntoDbWithException(String message) { final LogEntry logEntry = new LogEntry(); logEntry.setDate(new Date()); logEntry.setMessage(message); logEntry.persist(); }
在失败的情况下,我想保留一条错误消息,但是如果我抛出异常,catch子句中的事务将被回滚并且我的LogEntry将不会保留。我看到的唯一方法是手动调用flush()after persist()。
flush()
persist()
有没有更干净的解决方案来执行此操作?
谢谢。
UPD:
由于我有一个执行持久化的静态方法,因此我需要对接受的答案应用以下技巧:
public static void logIntoDbWithException(String message) { new Runnable() { @Transactional(propagation = Propagation.REQUIRES_NEW) public void run() { final LogEntry logEntry = new LogEntry(); logEntry.setDate(new Date()); logEntry.setMessage(message); logEntry.persist(); } }.run(); }
首先,调用flush()不会给您带来任何好处:flush()不会执行任何操作,并且由于您在同一事务中记录错误,因此插入将回滚。
因此,您需要启动一个新的“嵌套”事务来记录错误。
public class A { @Autowired private B b; @Transactional public void doSmt() { try { someOperation(); // can throw exception b.logIntoDb(); // if everything is OK } catch {Exception e} { b.logIntoDbWithException(e.getMessage()); // log error into db throw new MyCustomException(e); } } } public class B{ //outer transaction is still active public void logIntoDb(String message) { final LogEntry logEntry = new LogEntry(); logEntry.setDate(new Date()); logEntry.setMessage(message); logEntry.persist(); } // 'outer' transaction will be suspended until this method completes // this new transaction will commit/rollback independently of the outer transaction @Transactional(propagation = Propagation.REQUIRES_NEW) public void logIntoDbWithException(String message) { final LogEntry logEntry = new LogEntry(); logEntry.setDate(new Date()); logEntry.setMessage(message); logEntry.persist(); } }