JPA Listeners

Run
How to run the sample
The source code for this sample can be found in the javaee7-samples GitHub repository. The first thing we need to do is to get the source by downloading the repository and then go into the samples folder:
git clone git://github.com/javaee-samples/javaee7-samples.git
cd javaee7-samples/jpa/listeners/
Now we are ready to start testing. You can run all the tests in this sample by executing:
mvn test
Or you can run individual tests by executing one of the following:
mvn test -Dtest=JpaListenersTest

Invocation examples of all the available Entity Listeners

JpaListenersTest

In this sample we’re going to query a simple JPA Entity, using the JPA EntityManager and perform a findAll, persist, merge, and remove operations. By calling these operations we want to demonstrate the behaviour of Entity Listener Methods: @PostLoad, @PrePersist, @PostPersist, @PreUpdate, @PostUpdate, @PreRemove, @PostRemove defined on MovieListener:

public class MovieListener {

    public MovieListener();
    public static CountDownLatch entityListenersCountDownLatch;
    public static boolean postLoadInvoked;
    public static boolean prePersistInvoked;
    public static boolean postPersistInvoked;
    public static boolean preUpdateInvoked;
    public static boolean postUpdateInvoked;
    public static boolean preRemoveInvoked;
    public static boolean postRemoveInvoked;

    @PostLoad()
    public void newMovieLoad(Movie movie);

    @PrePersist()
    public void newMovieAlertBefore(Movie movie);

    @PostPersist()
    public void newMovieAlertAfter(Movie movie);

    @PreUpdate()
    public void updateMovieAlertBefore(Movie movie);

    @PostUpdate()
    public void updateMovieAlertAfter(Movie movie);

    @PreRemove()
    public void deleteMovieAlertBefore(Movie movie);

    @PostRemove()
    public void deleteMovieAlertAfter(Movie movie);
}

The following JPA Entity, represents a Movie which has a name and a comma separated list of actors:

@Entity()
@Table(name = "MOVIE_LISTENER")
@XmlRootElement()
@NamedQueries({@NamedQuery(name = "Movie.findAll", query = "SELECT m FROM Movie m"), @NamedQuery(name = "Movie.findById", query = "SELECT m FROM Movie m WHERE m.id = :id"), @NamedQuery(name = "Movie.findByName", query = "SELECT m FROM Movie m WHERE m.name = :name"), @NamedQuery(name = "Movie.findByActors", query = "SELECT m FROM Movie m WHERE m.actors = :actors")})
@EntityListeners(MovieListener.class)
public class Movie implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id()
    @NotNull()
    private Integer id;
    @NotNull()
    @Size(min = 1, max = 50)
    private String name;
    @NotNull()
    @Size(min = 1, max = 200)
    private String actors;

    public Movie();

    public Movie(Integer id);

    public Movie(Integer id, String name, String actors);

    public Integer getId();

    public void setId(Integer id);

    public String getName();

    public void setName(String name);

    public String getActors();

    public void setActors(String actors);

    @Override()
    public boolean equals(Object o);

    @Override()
    public int hashCode();

    @Override()
    public String toString();
}

We’re just going to deploy the application as a web archive. Note the inclusion of the following files:

/META-INF/persistence.xml
/META-INF/create.sql
/META-INF/drop.sql
/META-INF/load.sql

The persistence.xml file is needed of course for the persistence unit definition. A datasource is not needed, since we can now use the new default datasource available in JEE7. We’re also using the new javax.persistence.schema-generation.* propertires to create, populate and drop the database.

@Deployment
public static WebArchive createDeployment() {
    WebArchive war = ShrinkWrap.create(WebArchive.class)
                               .addPackage("org.javaee7.jpa.listeners")
                               .addAsResource("META-INF/persistence.xml")
                               .addAsResource("META-INF/create.sql")
                               .addAsResource("META-INF/drop.sql")
                               .addAsResource("META-INF/load.sql");
    System.out.println(war.toString(true));
    return war;
}

In the test, we’re just going to invoke the different operations in sequence keeping in mind that each invocation might be dependent of the previous invoked operation.

@Test
public void testListeners() throws Exception {
    List<Movie> movies = movieBean.listMovies();    (1)
    assertEquals(4, movies.size());

    assertFalse(prePersistInvoked);
    assertFalse(postPersistInvoked);
    movieBean.createMovie();                        (2)
    assertTrue(prePersistInvoked);
    assertTrue(postPersistInvoked);

    movies = movieBean.listMovies();                (3)
    assertEquals(5, movies.size());
    assertTrue(movies.contains(new Movie(5)));


    assertFalse(preUpdateInvoked);
    assertFalse(postUpdateInvoked);
    movieBean.updateMovie();                        (4)
    assertTrue(preUpdateInvoked);
    assertTrue(postUpdateInvoked);

    movies = movieBean.listMovies();                (5)
    assertEquals(5, movies.size());
    assertEquals("Inception2", movies.get(2).getName());

    assertFalse(preRemoveInvoked);
    assertFalse(postRemoveInvoked);
    movieBean.deleteMovie();                        (6)
    assertTrue(preRemoveInvoked);
    assertTrue(postRemoveInvoked);

    movies = movieBean.listMovies();                (7)
    assertFalse(movies.isEmpty());
    assertEquals(4, movies.size());
    assertFalse(movies.contains(new Movie(3)));

    assertTrue(MovieListener.entityListenersCountDownLatch.await(0, TimeUnit.SECONDS));
}
  1. 4 movies in the database, so @PostLoad method called 4x

  2. On persist both @PrePersist and @PostPersist are called

  3. 5 movies now, so @PostLoad method called 5x

  4. On merge both @PreUpdate and @PostUpdate are called

  5. Still 5 mpvies, so @PostLoad method called again 5x

  6. On remove both @PreRemove and @PostRemove are called

  7. 4 movies now, so @PostLoad method called 4x

Share the Knowledge

Find this sample useful? Share on

There's a lot more about JavaEE to cover. If you're ready to learn more, check out the other available samples.

Help Improve

Find a bug in the sample? Something missing? You can fix it by editing the source, making the correction and sending a pull request. Or report the problem to the issue tracker

Recent Changelog

  • Jul 15, 2014: Removed header license. the licensing is now referenced in the license file in the root of the project by Roberto Cortez
  • Jan 21, 2014: Updated javadoc by Roberto Cortez
  • Jan 16, 2014: Added test for jpa listeners project by Roberto Cortez
  • Nov 09, 2013: Removing try-with-resources to allow any errors to show up on servletoutputstream by Arun Gupta
  • Oct 30, 2013: Fixing the namespace and version by Arun Gupta
  • Sep 17, 2013: Removing netbeans configuration file by Arun Gupta
  • Sep 12, 2013: Fixing namespace from java.sun.com -> xmlns.jcp.org by Arun Gupta
  • Sep 12, 2013: Fixing namespace from java.sun.com -> xmlns.jcp.org by Arun Gupta
  • Sep 12, 2013: Better output message by Arun Gupta
  • Sep 12, 2013: Using cdi 1.1 "all" style beans.xml by Arun Gupta
How to help improve this sample
The source code for this sample can be found in the javaee7-samples GitHub repository. The first thing you need to do is to get the source by downloading the repository and then go into the samples folder:
git clone git://github.com/javaee-samples/javaee7-samples.git
cd javaee7-samples/jpa/listeners/

Do the changes as you see fit and send a pull request!

Good Luck!