As we know, in the hibernate persistent lifecycle, a particular object travels from state to state, from transient, persistent, to detached. During processing, it may commit or roll back before it reaches the last state. Sometimes, we need to perform some additional tasks such as cleanup, log or some operations on the object between different states of the persistent life cycle. To perform such activities, hibernate provides a useful and pluggable feature called interceptor.
Interceptor, as the name suggests, is used to intercept any operation. Interceptors apply hooks inside the logic. In hibernate, we have some built-in interceptors that help us intercept our logic.
Generally, an interceptor is used to log monitor data that is input and to validate it. You can also change or overwrite it at runtime. Let's take a look at the next example.
In this recipe, we will discuss the use of a basic interceptor and some methods of intercepting. Here, we will try to save the employee
object and capturing the log while saving an object.
For this recipe, we will create an Employee
class:
Source file: Employee.java
@Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue private long id; @Column(name = "name") private String name; // getters ans setters }
First of all, we need to create an interceptor class that extends EmptyInterceptor
, which implements the org.hibernate.Interceptor
interface. Update the following code:
Source file: CustomInterceptor.java
import org.hibernate.EmptyInterceptor; import org.hibernate.type.Type; public class CustomInterceptor extends EmptyInterceptor { /* Line 3 */ public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { System.out.println("On Save"); System.out.println("entity: " + entity); System.out.println("id: " + id); System.out.println("state: " + Arrays.toString(state)); System.out.println("propertyNames: " + Arrays.toString(propertyNames)); System.out.println("types: " + Arrays.toString(types)); return false; } /* Line 13 */ public void preFlush(Iterator iterator) { System.out.println(" Pre flush"); while (iterator.hasNext()) { System.out.println(iterator.next()); } } /* Line 20 */ public void postFlush(Iterator iterator) { System.out.println(" Post flush"); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }
Source file: InterceptorTest.java
/* Line 1 */ CustomInterceptor interceptor = new CustomInterceptor(); /* Line 2 */ Session session = sessionFactory.withOptions().interceptor(interceptor).openSession(); /* Line 3 */ Transaction tx = null; /* Line 4 */ tx = session.beginTransaction(); /* Line 6 */ Employee employee = new Employee(); /* Line 7 */ employee.setName("Vishal"); /* Line 8 */ session.saveOrUpdate(employee); /* Line 9 */ tx.commit(); /* Line 10 */ session.close(); /* Line 11 */ sessionFactoy.close();
In the previous section, we created two different Java source files. the first one is CustomInterceptor.java
, which overrides some methods of the EmptyInterceptor
superclass, and the other is the InterceptorTest.java
executable class, which shows the code to test the mechanism.
Now, we will consider the code in detail.
This is an executable class and contains the main method.
In Line 1
, we created the object of the CustomInterceptor
class.
Line 2
shows how to provide options to SessionFactory
on runtime; builder design patterns are used to provide the interceptor to the session. The withOptions()
method returns the instance of SessionBuilder
. On top of it, we set an interceptor that is an instance of CustomInterceptor
using the interceptor()
method and then opened a new session.
The code from Line 3
onward creates an employee
object and saves it within the boundary of the transaction.
Here, we extended the EmptyInterceptor
class and implemented the three methods.
The onSave
method is called before the object is saved to the database. Constructor of onSave
is shown in the following code:
boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException
The arguments passed are as follows:
id
fields are set to 0
.null
because we are saving the object.vishal
, as we have only one field in the POJO.state
field; here, the name
field, where the value of this field is stored in the state
object.Another of the methods we implemented is preFlush
. This method is called after commit is completed and just before flush is started. Use of preFlush()
is shown in the following code:
/* Line 13 */ public void preFlush(Iterator iterator) { System.out.println(" Pre flush"); while (iterator.hasNext()) { System.out.println(iterator.next()); } }
In the output, you will find id=1
, which was 0
in the onSave()
method, because the object is saved to the database. The argument iterator returns the object whose id is going to be flushed out.
The last of the three methods is postFlush
. This method is called after commit is completed and just before flush is completed. Use of postFlush()
is shown in the following code:
/* Line 20 */ public void postFlush(Iterator iterator) { System.out.println(" Post flush"); while (iterator.hasNext()) { System.out.println(iterator.next()); } }