考虑我有一个方法在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()
之后手动调用persist()
。
是否有更干净的解决方案来执行此操作?
感谢。
UPD:
由于我有一个执行持久化的静态方法,我需要对接受的答案应用以下hack:
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();
}
答案 0 :(得分:4)
首先,调用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();
}
}