JPA Entity Listeners

JPA Entity Listeners are a powerful feature of the Java Persistence API (JPA) that allow developers to specify callback methods for certain events that occur in an entity’s lifecycle. These events include pre-persist, post-persist, pre-update, post-update, pre-remove, and post-remove.

Attaching Listeners to an Entity

Firstly, in order to receive callbacks from the Entity lifecycle events, you can add the following annotations to your Entity:

@Entity

public static class EntityExampleCallbacks {

@PrePersist void onPrePersist() {}

@PostPersist void onPostPersist() {}

@PostLoad void onPostLoad() {}

@PreUpdate void onPreUpdate() {}

@PostUpdate void onPostUpdate() {}

@PreRemove void onPreRemove() {}

@PostRemove void onPostRemove() {}

}

And here’s a description of the single callback annotations:

  1. @PrePersist: This method executes before persisting the Entity to the DB. You can use it to perform any necessary pre-persist operations, such as setting a default value or generating a timestamp.
  2. @PostPersist: This method executes after persisting the Entity to the DB. You can use it to perform any necessary post-persist operations, such as updating related entities or sending a notification.
  3. @PreRemove: This method executes before removing the Entity from the DB. You can use it to perform any necessary pre-remove operations, such as deleting related entities or sending a notification.
  4. @PostRemove: This method executes after removing the Entity from the DB. You can use it to perform any necessary post-remove operations, such as cleaning up resources or updating related entities.
  5. @PreUpdate: This method executes before updating the Entity to the DB. You can use it to perform any necessary pre-update operations, such as validating the new values or updating related entities.
  6. @PostUpdate: This method executes after updating the Entity to the DB. You can use it to perform any necessary post-update operations, such as sending a notification or updating related entities.
  7. @PostLoad: This method executes after the entity gets loaded from the database. You can use it to perform any necessary post-load operations, such as initializing lazy-loaded collections or setting default values.

Note that you can apply multiple callback annotations to a method, but you can use each annotation just once per Entity

Besides, you can also define your listeners in an external class, like this:

public class MyEntityListener {

@PrePersist void onPrePersist(Object o) {}

@PostPersist void onPostPersist(Object o) {}

@PostLoad void onPostLoad(Object o) {}

@PreUpdate void onPreUpdate(Object o) {}

@PostUpdate void onPostUpdate(Object o) {}

@PreRemove void onPreRemove(Object o) {}

@PostRemove void onPostRemove(Object o) {}

}

Finally, here’s how to attach a listener to an Entity using the @EntityListeners annotation:

@Entity @EntityListeners(MyEntityListener .class)

public class EntityExampleCallbacks  {

}

What can you do inside an Entity Listener?

Certain types are not safe inside callback methods. For example,
invoking methods on an entity manager or executing queries obtained from an entity manager are not
supported. Much the same if you try to access another Entity which differs from to one to which the lifecycle event applies. However, you can lookup resources via JNDI or use JDBC/JMS API.

A sample JPA project with Listeners

After the theory, we will see a practical example of a JPA Project which uses Listeners to intercept the lifecycle stages of the following Entity:

jpa listeners tutorial

Here is the Customer Class, which uses the CustomerListener as Entity Listener:

@Entity @EntityListeners(CustomerListener.class)
public class Customer implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;
    private String address;

    @Column(name = "phone_number")
    private String phoneNumber;

    // Getters/Setters omitted

}

Next, let’s add the CustomerListener Class, which does some logging when the callback method fires:

public class CustomerListener {
    private static final Logger logger = Logger.getLogger(CustomerListener.class.getName());

    @PrePersist void onPrePersist(Object o) {
        logger.info("Before persisting "+o);
    }
    @PostPersist void onPostPersist(Object o) {
        logger.info("After persisting "+o);
    }
    @PostLoad void onPostLoad(Object o) {
        logger.info("After Load "+o);
    }
    @PreUpdate void onPreUpdate(Object o) {
        logger.info("Before update "+o);
    }
    @PostUpdate void onPostUpdate(Object o) {
        logger.info("After update "+o);
    }
    @PreRemove void onPreRemove(Object o) {
        logger.info("Before remove "+o);
    }
    @PostRemove
    void onPostRemove(Object o) {
        logger.info("After remove "+o);
    }
}

Testing the JPA application

Let’s test the application. Firstly, we will add a new Entity:

curl -X POST http://localhost:8080/jpa-listener/rest/customer -H 'Content-Type: application/json' -d '{"name":"JohnSmith","email":"[email protected]","address":"3170 Broadway","phoneNumber":"33312345678"}'

Here are the Entity Listeners callbacks (notice that before persisting the Entity the Primary Key has not been generated yet):

14:45:23,865 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) Before persisting Customer{id=null, name='JohnSmith', email='[email protected]', address='3170 Broadway', phoneNumber='33312345678'}
14:45:23,909 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) After persisting Customer{id=1, name='JohnSmith', email='[email protected]', address='3170 Broadway', phoneNumber='33312345678'}

Next, let’s find all Customers with a JPA Query:

curl -s http://localhost:8080/jpa-listener/rest/customer | jq

Here are the Entity Listeners callbacks:

14:46:26,551 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) After Load Customer{id=1, name='JohnSmith', email='[email protected]', address='3170 Broadway', phoneNumber='33312345678'}

Next, let’s update a Customer:

curl -X PUT http://localhost:8080/jpa-listener/rest/customer -H 'Content-Type: application/json' -d '{"id":"1","name":"JohnDoe","email":"[email protected]","address":"1105 Rogers St","phoneNumber":"12345678"}'

Here are the Entity Listeners callbacks:

14:47:33,242 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) After Load Customer{id=1, name='JohnSmith', email='[email protected]', address='3170 Broadway', phoneNumber='33312345678'}
14:47:33,243 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) Before update Customer{id=1, name='JohnDoe', email='[email protected]', address='1105 Rogers St', phoneNumber='12345678'}
14:47:33,249 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) After update Customer{id=1, name='JohnDoe', email='[email protected]', address='1105 Rogers St', phoneNumber='12345678'}

Finally, let’s update a Customer:

curl -X DELETE http://localhost:8080/jpa-listener/rest/customer/1

Here are the Entity Listener callbacks:

14:48:36,215 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) After Load Customer{id=1, name='JohnDoe', email='[email protected]', address='1105 Rogers St', phoneNumber='12345678'}
14:48:36,217 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) Before remove Customer{id=1, name='JohnDoe', email='[email protected]', address='1105 Rogers St', phoneNumber='12345678'}
14:48:36,217 INFO  [com.mastertheboss.ejb.CustomerService] (default task-1) Deleted Customer with id1
14:48:36,219 INFO  [com.mastertheboss.model.CustomerListener] (default task-1) After remove Customer{id=1, name='JohnDoe', email='[email protected]', address='1105 Rogers St', phoneNumber='12345678'}

Conclusion

Entity Listeners provide a flexible and convenient way to add behavior to your entities without cluttering up the entity class itself. They can be particularly useful for tasks such as logging, auditing, or modifying the state of other entities in the system.

In summary, JPA Entity Listeners allow developers to specify callback methods for certain events in an entity’s lifecycle, providing a convenient way to add behavior to entities without cluttering up the entity class itself.

Source code: https://github.com/fmarchioni/mastertheboss/tree/master/jpa/jpa-listener

Found the article helpful? if so please follow us on Socials