Detect database changes with spring boot and JPA
November 22, 2020Nick

Detect database changes with spring boot and JPA

Working with JPA EventListener and components from spring boot

Today I had to implement a change logging system for an application. The most changes to log came with UPDATE commands to the database. The idea: detect UPDATE commands by JPA and notify instead of place method-calls all over the code.

Fortunately Spring Boot and JPA provides some Entity-Lifecycle-Listeners:

@EntityListeners(AuditTrailListener.class)
@Entity
public class User {
    //...
}

public class AuditTrailListener {
  private static Log log = LogFactory.getLog(AuditTrailListener.class);
  
  @PrePersist
  @PreUpdate
  @PreRemove
  private void beforeAnyUpdate(User user) {
      log.info("add/update/delete complete for user: " + user.getId());
  }
  
  @PostPersist
  @PostUpdate
  @PostRemove
  private void afterAnyUpdate(User user) {
      log.info("[USER AUDIT] add/update/delete complete for user: " + user.getId());
  }
  
  @PostLoad
  private void afterLoad(User user) {
      log.info("[USER AUDIT] user loaded from database: " + user.getId());
  }
}

source

But for me it was not enough. My AuditTrailListener should be an Spring Boot class annotated with @Component. This together doesn't work. After some experiments I realized that JPA and Spring instantiate it's own objects of the Listener.

On stackoverflow.com I found another way to get my desired result:

@Component
public class MyEventListener implements PreInsertEventListener {
  @Autowired
  private EntityManagerFactory entityManagerFactory;

  @PostConstruct
  private void init() {
      SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
      EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
      registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(this);
  }

  @Override
  public boolean onPreInsert(PreInsertEvent preInsertEvent) {
      return false;
  }
}

source

The EntityManagerFactory has an ServiceRegistry which can be extended with custom EventListeners. The example shows the PreInsertEvent, but there are all Pre and Post Insert/Update/Delete events ready for you.

Nick

Written by Nick

Professional Code Breaker

More ways to Suck at Coding