Spring Boot — Jpa Entity Listener

Prateek
5 min readJun 17, 2021

--

EntityListeners — Specifies the callback listener classes to be used for an entity or mapped superclass. This annotation may be applied to an entity class or mapped superclass.

MappedSuperClass — Designates a class whose mapping information is applied to the entities that inherit from it. A mapped superclass has no separate table defined for it.

A class designated with the MappedSuperclass annotation can be mapped in the same way as an entity except that the mappings will apply only to its subclasses since no table exists for the mapped superclass itself. When applied to the subclasses the inherited mappings will apply in the context of the subclass tables. Mapping information may be overridden in such subclasses by using the AttributeOverride and AssociationOverride annotations or corresponding XML elements.

Important Notes:

You can’t inject or Autowire JpaRepository bean inside the JPA Lifecycle callbacks.

For the @PostPersist, @PostRemove and @PostUpdate operations, the documentation mentions that these events can happen right after the operation occurs, after a flush, or at the end of a transaction.

We should note that the @PreUpdate callback is only called if the data is actually changed — that is if there’s an actual SQL update statement to run. The @PostUpdate callback is called regardless of whether anything actually changed.

If any of our callbacks for persisting or removing an entity throw an exception, the transaction will be rolled back.

Example: Concrete class as a mapped superclass

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Author implements Serializable {
private static final long serialVersionUID = 1L; @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String genre;
private int age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Author{" + "id=" + id + ", name=" + name
+ ", genre=" + genre + ", age=" + age + '}';
}
}

Book.java

import com.bookstore.listener.BookListener;
import java.io.Serializable;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
@EntityListeners(BookListener.class)
public abstract class Book implements Serializable {
private static final long serialVersionUID = 1L; @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String isbn;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" + "id=" + id + ", title=" + title + ", isbn=" + isbn + '}';
}
}

EBook.java

import java.io.Serializable;
import javax.persistence.Entity;
@Entity
public class Ebook extends Book implements Serializable {
private static final long serialVersionUID = 1L; private String format; public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
@Override
public String toString() {
return super.toString() + ", Ebook{" + "format=" + format + '}';
}
}

Paperback.java

import java.io.Serializable;
import javax.persistence.Entity;
@Entity
public class Paperback extends Book implements Serializable {
private static final long serialVersionUID = 1L; private String sizeIn;
private String weightLbs;
public String getSizeIn() {
return sizeIn;
}
public void setSizeIn(String sizeIn) {
this.sizeIn = sizeIn;
}
public String getWeightLbs() {
return weightLbs;
}
public void setWeightLbs(String weightLbs) {
this.weightLbs = weightLbs;
}
@Override
public String toString() {
return super.toString() + ", Paperback{" + "sizeIn=" + sizeIn + ", weightLbs=" + weightLbs + '}';
}
}

BookListener.java

import com.bookstore.entity.Book;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
public class BookListener { @PrePersist
void onPrePersist(Book book) {
System.out.println("BookListener.onPrePersist(): " + book);
}
@PostPersist
void onPostPersist(Book book) {
System.out.println("BookListener.onPostPersist(): " + book);
}
@PostLoad
void onPostLoad(Book book) {
System.out.println("BookListener.onPostLoad(): " + book);
}
@PreUpdate
void onPreUpdate(Book book) {
System.out.println("BookListener.onPreUpdate(): " + book);
}
@PostUpdate
void onPostUpdate(Book book) {
System.out.println("BookListener.onPostUpdate(): " + book);
}
@PreRemove
void onPreRemove(Book book) {
System.out.println("BookListener.onPreRemove(): " + book);
}
@PostRemove
void onPostRemove(Book book) {
System.out.println("BookListener.onPostRemove(): " + book);
}
}

AuthorRepostory.java

import com.bookstore.entity.Author;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public interface AuthorRepository extends JpaRepository<Author, Long> {

@Transactional(readOnly=true)
Author findByName(String name);
}

EBookRepository.java

import com.bookstore.entity.Ebook;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public interface EbookRepository extends JpaRepository<Ebook, Long> {
@Transactional(readOnly = true)
Ebook findByTitle(String title);

@Transactional(readOnly = true)
@Query("SELECT e FROM Ebook e JOIN FETCH e.author")
Ebook fetchByAuthorId(Long id);
}

PaperbackRepository.java

import com.bookstore.entity.Paperback;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public interface PaperbackRepository extends JpaRepository<Paperback, Long> {
@Transactional(readOnly = true)
Paperback findByTitle(String title);

@Transactional(readOnly = true)
@Query("SELECT e FROM Paperback e JOIN FETCH e.author")
Paperback fetchByAuthorId(Long id);
}

BookStoreService.java

import com.bookstore.repository.AuthorRepository;
import com.bookstore.entity.Author;
import com.bookstore.entity.Paperback;
import com.bookstore.entity.Ebook;
import com.bookstore.repository.EbookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.bookstore.repository.PaperbackRepository;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class BookstoreService {
private final AuthorRepository authorRepository;
private final PaperbackRepository paperbackRepository;
private final EbookRepository ebookRepository;
@Transactional
public void persistAuthorWithBooks() {
Author author = new Author();
author.setName("Alicia Tom");
author.setAge(38);
author.setGenre("Anthology");
Paperback paperback = new Paperback();
paperback.setIsbn("002-AT");
paperback.setTitle("The beatles anthology");
paperback.setSizeIn("7.5 x 1.3 x 9.2");
paperback.setWeightLbs("2.7");
paperback.setAuthor(author);
Ebook ebook = new Ebook();
ebook.setIsbn("003-AT");
ebook.setTitle("Anthology myths");
ebook.setFormat("kindle");
ebook.setAuthor(author);
authorRepository.save(author);
paperbackRepository.save(paperback);
ebookRepository.save(ebook);
}
@Transactional
public void fetchAndRemovePaperback() {
Paperback paperback = paperbackRepository.findByTitle("The beatles anthology");
paperbackRepository.delete(paperback);
}
@Transactional
public void fetchAndRemoveEbook() {
Ebook ebook = ebookRepository.findByTitle("Anthology myths");
ebookRepository.delete(ebook);
}
}

MainApp.java

import com.bookstore.service.BookstoreService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MainApplication {
@Autowired
private BookstoreService bookstoreService;
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
@Bean
public ApplicationRunner init() {
return args -> {
System.out.println("\n\npersistAuthorWithBooks():");
bookstoreService.persistAuthorWithBooks();
System.out.println("\n\nfetchAndRemovePaperback():");
bookstoreService.fetchAndRemovePaperback();
System.out.println("\n\nfetchAndRemoveEbook():");
bookstoreService.fetchAndRemoveEbook();
};
}
}

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/bookstoredb?createDatabaseIfNotExist=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialectspring.jpa.open-in-view=false#logging.level.org.hibernate.type.descriptor.sql=TRACE

Output Console

persistAuthorWithBooks():
Hibernate: insert into author (age, genre, name) values (?, ?, ?)
BookListener.onPrePersist(): Book{id=null, title=The beatles anthology, isbn=002-AT}, Paperback{sizeIn=7.5 x 1.3 x 9.2, weightLbs=2.7}
Hibernate: insert into paperback (author_id, isbn, title, size_in, weight_lbs) values (?, ?, ?, ?, ?)
BookListener.onPostPersist(): Book{id=1, title=The beatles anthology, isbn=002-AT}, Paperback{sizeIn=7.5 x 1.3 x 9.2, weightLbs=2.7}
BookListener.onPrePersist(): Book{id=null, title=Anthology myths, isbn=003-AT}, Ebook{format=kindle}
Hibernate: insert into ebook (author_id, isbn, title, format) values (?, ?, ?, ?)
BookListener.onPostPersist(): Book{id=1, title=Anthology myths, isbn=003-AT}, Ebook{format=kindle}
fetchAndRemovePaperback():
Hibernate: select paperback0_.id as id1_2_, paperback0_.author_id as author_i6_2_, paperback0_.isbn as isbn2_2_, paperback0_.title as title3_2_, paperback0_.size_in as size_in4_2_, paperback0_.weight_lbs as weight_l5_2_ from paperback paperback0_ where paperback0_.title=?
BookListener.onPostLoad(): Book{id=1, title=The beatles anthology, isbn=002-AT}, Paperback{sizeIn=7.5 x 1.3 x 9.2, weightLbs=2.7}
BookListener.onPreRemove(): Book{id=1, title=The beatles anthology, isbn=002-AT}, Paperback{sizeIn=7.5 x 1.3 x 9.2, weightLbs=2.7}
Hibernate: delete from paperback where id=?
BookListener.onPostRemove(): Book{id=1, title=The beatles anthology, isbn=002-AT}, Paperback{sizeIn=7.5 x 1.3 x 9.2, weightLbs=2.7}
fetchAndRemoveEbook():
Hibernate: select ebook0_.id as id1_1_, ebook0_.author_id as author_i5_1_, ebook0_.isbn as isbn2_1_, ebook0_.title as title3_1_, ebook0_.format as format4_1_ from ebook ebook0_ where ebook0_.title=?
BookListener.onPostLoad(): Book{id=1, title=Anthology myths, isbn=003-AT}, Ebook{format=kindle}
BookListener.onPreRemove(): Book{id=1, title=Anthology myths, isbn=003-AT}, Ebook{format=kindle}
Hibernate: delete from ebook where id=?
BookListener.onPostRemove(): Book{id=1, title=Anthology myths, isbn=003-AT}, Ebook{format=kind

--

--

Prateek
Prateek

Written by Prateek

Java Developer and enthusiast

No responses yet