I used Java Spring to create code that basically stores a specific value in a DB.
And I want to leave a success log if the value saving is successful. Initially, I left a log at the bottom like the code below.
@Transactional
public void save(Long id, String target) {
repository.save(id, target);
log.info("save success! -> id : {}, target : {}", id, target);
}
However, at the moment of logging, the values were not yet reflected in the DB, so if something went wrong with the logic later, the log would be logged but the values would not actually be saved. So I realized that I was logging in the wrong place.
here is a question: As a workaround, I was planning to use a TransactionalEventListener
to take logs after the DB commit (i.e. after the value is actually saved) and looked for information.
However, I've had a hard finding examples of using listeners for this purpose, so I'm wondering if using TransactionalEventListener
for this purpose is the right way to go, and if not, what other ways are there to collect reliable logs like this?
Thanks for your help
I used Java Spring to create code that basically stores a specific value in a DB.
And I want to leave a success log if the value saving is successful. Initially, I left a log at the bottom like the code below.
@Transactional
public void save(Long id, String target) {
repository.save(id, target);
log.info("save success! -> id : {}, target : {}", id, target);
}
However, at the moment of logging, the values were not yet reflected in the DB, so if something went wrong with the logic later, the log would be logged but the values would not actually be saved. So I realized that I was logging in the wrong place.
here is a question: As a workaround, I was planning to use a TransactionalEventListener
to take logs after the DB commit (i.e. after the value is actually saved) and looked for information.
However, I've had a hard finding examples of using listeners for this purpose, so I'm wondering if using TransactionalEventListener
for this purpose is the right way to go, and if not, what other ways are there to collect reliable logs like this?
Thanks for your help
Because of the @Transactional
annotation, the ACID
properties are maintained for this method. This means that the
ultimate database commit happens after the method runs successfully without any error. However, as the logger is part of
your method, it prints the log before committing the data into the database.
You can handle this in multiple ways. One of them is using @Aspect
another one, you can use
@TransactionalEventListener
.
Here is an example for TransactionalEventListener
:
Listener
@Component
@Slf4j
public class TransactionLogEventListener {
@EventListener
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleTransactionEvent(TransactionLogEvent event) {
log.info("save success! -> id : {}, target : {}", event.getId(), event.getTarget());
}
}
Event
@Getter
public class TransactionLogEvent extends ApplicationEvent {
private final Long id;
private final String target;
public TransactionEmailEvent(Object source, Long id, String target) {
super(source);
this.id = id;
this.target = target;
}
}
// .. Component
// ... other codes
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Transactional
public void save(Long id, String target) {
repository.save(id, target);
applicationEventPublisher.publishEvent(new TransactionLogEvent(this, id, target));
}
TransactionalEventListener
is a good option and tuhin47 has nicely explained how to use it.
But since your case is quite simple — just logging, I’d like to suggest another approach that I find simpler.
Instead of TransactionalEventListener
you could register after commit logic using TransactionSynchronizationManager.registerSynchronization
Define something like this:
public class TransactionUtil {
public static void runAfterCommit(Runnable action) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
action.run();
}
});
}
}
And then use it:
@Transactional
public void save(Long id, String target) {
repository.save(id, target);
runAfterCommit(() -> log.info("save success! -> id : {}, target : {}", id, target))
}
I like this approach because
Disadvantages of the TransactionSynchronizationManager
approach that I have encountered
TransactionalEventListener
but still manageable