Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

commit changes in try-catch inside @Transactional

Consider I have a method doing some stuff and logging mechanism in ActiveRecord pattern style:

@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();
}

I want to persist an error message in case of failure, but if I'd rethrow exception in catch clause transaction will be rollbacked and my LogEntry will not be persisted. The only way I see is manually call flush() after persist().

Is there is a more clean solution to perform this?

Thanks.

UPD:

Since I have a static method which performs persisting I need to apply following hack to accepted answer:

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();
}
like image 643
fasth Avatar asked Oct 23 '25 22:10

fasth


1 Answers

Firstly, calling flush() is not going to do you any good: flush() does not commit anything and, as you are logging the error in the same transaction, the insert will be rolled back.

You therefore need to start a new 'nested' transaction to log the error.

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();
    }
}
like image 86
Alan Hay Avatar answered Oct 26 '25 14:10

Alan Hay



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!