-
Notifications
You must be signed in to change notification settings - Fork 92
Interceptors
To have tighter control on the persistence lifecycle of an entity, you can rely on Interceptors. Each interceptor is called upon a lifecycle event and modifies the entity state.
Below is the list of all possible lifecycle events:
- PRE_INSERT
- POST_INSERT
- PRE_UPDATE
- POST_UPDATE
- PRE_DELETE
- POST_DELETE
- POST_LOAD
Below are listed all operations that can be intercepted:
Operation | Possible Events |
---|---|
`insert()` | PRE_INSERT/POST_INSERT |
`insertOrUpdate()` | PRE_INSERT/POST_INSERT or PRE_UPDATE/POST_UPDATE |
`update()` | PRE_UPDATE/POST_UPDATE |
`delete()` | PRE_DELETE/POST_DELETE |
`find()` | POST_LOAD |
`sliceQuery()` | POST_LOAD |
`typedQuery()` | POST_LOAD |
`rawTypedQuery()` | POST_LOAD |
Please note that
getProxy()
cannot be intercepted by the event POST_LOAD because the proxy object is empty so it is not sensible to have functional logic upon its creation (since all fields are NULL except the primary key)
To use interceptors, you must provide your own implementation of the interface Interceptor<T>
:
public interface Interceptor<T>
{
public void onEvent(T entity);
public List<Event> events();
}
public enum Event
{
PRE_INSERT, POST_INSERT, PRE_UPDATE, POST_UPDATE, PRE_DELETE, POST_DELETE, POST_LOAD;
}
The onEvent(T entity)
method should be implemented to perform functional logic. The whole raw entity (not proxy) is provided as method argument.
The List<Event> events()
method defines a list of events this interceptor will be used for
Example:
public class UserInterceptor extends Interceptor<User>
{
public void onEvent(User entity) {
if(entity.getBiography() == null) {
entity.setBiography("TO DO");
}
}
public List<Event> events() {
return Arrays.asList(PRE_INSERT,PRE_UPDATE);
}
}
The above UserInterceptor
will update the biography to "TO DO" if it is not set before updating/saving the entity.
To register your interceptors with Achilles, you should pass them as parameter to the configuration parameter map:
...
List<Interceptor<?>> interceptors = Arrays.asList(userInterceptor);
configMap.put(EVENT_INTERCEPTORS,interceptors);
...
There are some gotchas when using interceptors:
Indeed when you craft your own CQL query using the typedQuery()
API, you may not select all fields to be retrieved. Consequently the entity will have some null fields when the POST_LOAD interceptors are applied. For saftety and to avoid the dreadful NullPointerException
, it is strongly recommended to perform null checks in the code of your interceptors.
This recommendation also applies to find()
operation since some fields may be null in Cassandra
It is absolutely a bad idea to modify the primary key in your interceptors. In most cases there is no sensible reason to do so.
To avoid NullPointerException
, Achilles will enforce null check (but not value check) on the primary key after each interceptor invocation.
For a batch context, since flushing is done only when endBatch()
is called, interceptors invocation is also delayed to flush time. You need to take it into account when designing your interceptor if you don't want any unexpected behavior.
Clarifying example:
batch.startBatch();
// PRE_INSERT and POST_INSERT interceptors detected, if any, but not invoked
User managedUser = batch.insert(new User(10L));
managedUser.setAge(24L);
// PRE_UPDATE and POST_UPDATE interceptors detected, but not invoked
batch.update(managedUser);
// Flush time, PRE_INSERT, POST_INSERT, PRE_UPDATE and POST_UPDATE interceptors are invoked
batch.endBatch();
-
Bootstraping Achilles at runtime
- Runtime Configuration Parameters
-
Manager
-
Consistency Level
-
Cassandra Options at runtime
-
Lightweight Transaction (LWT)
-
JSON Serialization
-
Interceptors
-
Bean Validation (JSR-303)