Hibernate interceptor or listener with Spring Boot and Spring Data JPA - spring

I'd like to run some checks prior to saving a collection of children of an object (cascade = all).
I am using Spring Boot and Spring Data JPA and was wondering what approach would be the best: a Hibernate listener or an interceptor. What are the pros/cons of each ? Do you happen to have an example for the one you consider the best approach ?
I have used Hibernate listeners before configured in XML like this:
<property name="eventListeners">
<map>
<entry key="post-update">
<list>
<ref bean="myListener" />
</list>
</entry>
</map>
</property>
on the session factory (older project). But now most of my configs are in annotations (cause Spring Boot) and I want to keep the configs as simple and light as possible, so maybe an interceptor would be a better solution.
Thank you.

I did a lot of looking around on this for myself and thought I'd share what I got working (I included the helpful (non-inline) links at the bottom).
Interceptor
To use an interceptor, you extend the org.hibernate.EmptyInterceptor class and override the methods you want to intercept.
You probably want onSave(...) in your case.
package foo.bar;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import java.io.Serializable;
public class MyInterceptor extends EmptyInterceptor {
#Override
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
// do your checks here
return false;
}
}
You have to register your interceptor with Spring/Hibernate.
You can do this in your application.properties or application.yml.
spring:
jpa:
properties:
hibernate.ejb.interceptor: foo.bar.MyInterceptor
The upsides to an interceptor are that it is (potentially) less code and relatively simple configuration.
The downsides are that you can only have one for your entire application and the API can be confusing to work with.
Event Listener
For events, you implement one of Hibernate's org.hibernate.event.spi.*Listener interfaces.
You probably want the org.hibernate.event.spi.PreInsertEventListener in your case.
You have to register your event in the EventListenerRegistry.
To do this, you can make your class a #Component, #Autowire the EntityManagerFactory into your class, and create a #PostConstruct method to register your class.
package foo.bar;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PreInsertEvent;
import org.hibernate.event.spi.PreInsertEventListener;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
#Component
public class MyEventListener implements PreInsertEventListener {
#Autowired
private EntityManagerFactory entityManagerFactory;
#PostConstruct
private void init() {
SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(this);
}
#Override
public boolean onPreInsert(PreInsertEvent preInsertEvent) {
// do your checks here
return false;
}
}
The upsides to listeners are that you can have as many as you want, the API is nicer than the interceptor's, and the code and the configuration are all in one place.
The downside is that the configuration is longer and more involved.
https://docs.jboss.org/hibernate/orm/3.5/reference/en-US/html/events.html
How to use Spring managed Hibernate interceptors in Spring Boot?
https://dzone.com/articles/spring-managed-hibernate-event-listeners

Hello,
First of all you can check the: https://www.baeldung.com/database-auditing-jpa where every options is explained in detail.
I would personally recommend Hibernate Interceptor, easy to use and understand. Depending on the complexity of the project, in most cases it will do.
In order to configure this in your application you simply need to add: spring.jpa.properties.hibernate.ejb.interceptor = path.to.interceptor (in application.properties). The interceptor itself should be #Component.
As long as the interceptor doesn't actually use any beans. Otherwise it is a bit more complicated but I would be more than happy to offer the solution.
Don't forget to add in application-test.properties, an EmptyInterceptor to not use the logging system (or whatever you want to use it for) in tests (which wouldn't be very helpful).
Hope this was of use to you.
As a final note: always update your Spring / Hibernate versions (use the latest as possible) and you will see that most code will become redundant as newer versions try to reduce the configurations as much as possible.

Related

Ordering Spring proxies without AspectJ

#Service
#Transactional
public CarService {
#Autowired private CarRepository carRepository;
#Cachable("cars")
public List<Car> getCars() {
return carRepository.getAll();
}
}
Suppose I have a code like this, where both #Transactional and #Cachable are presented. How can I guarantee that proxy chain spring will go through will be proxyForCache-proxyForTransaction? I.e. transaction won't be created, if my application already has cached result. I've seen a lot of examples, where people offer to implement Ordered interfaface on a class-level, namely, on #Aspect level. But problem is we don't use AspectJ in our project. Is there any way to order spring proxies without creating additional classes or at least using of AspectJ?

AspectJ pointcut on method in Spring CrudRepository

I'm using Spring's CrudRepository in combination with the annotation #RepositoryRestResource to implement a simple CRUD-app that can be used throught a RESTful API. I now want to add an AspectJ pointcut on my repository, so that some functionalities will be executed whenever a CRUD-method from the interface is called.
First, I extend Spring's CrudRepository to add some custom functionalities in my own interface:
#RepositoryRestResource(collectionResourceRel = "customers", path = "customers")
public interface CustomerRestRepository extends CrudRepository<Customer, Integer>{
Customer findOneByGuid(#Param("customerGuid") String customerGuid);
//Other custom methods.
}
Everything is working fine and I'm able to call this method via my REST client. I do not have to implement the interface CustomerRestRepository since Spring is doing the job as a miracle in behind. This is one of the crucial advantages of extending Springs's CrudRepository.
The problem, I'm facing now, is to add an AspectJ pointcut on this custom method findOneByGuid() that will, for example, log every call of the method after it's execution.
What I've tried by so far is:
#Aspect
public aspect AfterCustomerCrudAspect {
#Pointcut(
"execution(* com.x.y.z.CustomerRestRepository.findOneByGuid(..))")
public void customerCrudMethod() {}
#AfterReturning("customerCrudMethod()")
public void doSomething() {
//Do something
}
}
I've also tried:
1) execution(* com.x.y.z.CustomerRestRepository+.findOneByGuid(..))
2) execution(* org.springframework.data.repository.Repository+.*(..))
3) within(com.x.y.z.CustomerRestRepository)
4) annotation(RepositoryRestResource)
...and many others I do not remember. All with the same frustrating result: The advice is never applied.
By the way, I do not face any exceptions and if I try execution(* *.*(..)), the advice is working well - but, of course, not limited to the method findOneByGuid(). Thus, I think my code is correct in general.
I know that it is not possible to set pointcuts on interfaces. But since I do not have to implement the interface CustomerRestRepository by my own to get things working, I need to find a way to set a pointcut on an interface's method - or to find some other solution.
Well, one possible solution to that would be to implement the interface CustomerRestRepository. But then I've to do all the implementation work for the repository by my own and skip using the advantages of Spring's CrudRepository.
Thus, my question is, if there is a possibility to set a AspectJ pointcut on methods in a Spring CrudRepository.
Many thanks in advance for all the answers.
Well, I solved my problem in a different way.
Sometimes, things are less complicated than expected. Adding an AspectJ pointcut on a Spring CRUD-repository to execute some functionalities, whenever an entity is changed was not the best idea. (And at the best of my knowledge, it is not possible at all.)
There is a much more easier way to implement my requirement: The package javax.persistence provides the annotation #EntityListeners that suites perfectly to this job. So, annotate the entity class with the listener and implement the needed functionalities within the listener class:
#Entity
#EntityListeners(CustomerEntityListener.class)
//#Table, #NamedQueries and other stuff ...
public class Customer implements Serializable {
...
}
Implementation of the EntityListener:
public class CustomerEntityListener {
#PostPersist
public void customerPostPersist(Customer customer) {
//Add functionalities
}
}
The EntityListeneralso provides annotation for #PostUpdate, #PostRemove and so on - visit this site for more information.

spring/tomcat-jdbc pool - new connection listener

I am using tomcat-jdbc pool in default spring-boot setup. I would like to run some custom Java code each time new JDBC connection is established in the pool and before it is used for the first time. How to do it, and if there are several possibilities which one is the best?
To extend already accepted answer, you can use Spring AOP without full AspectJ if you use pointcut as this one:
#AfterReturning(pointcut = "execution(* org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection())")
public void afterConnectionEstablished() {
...
}
Well, I can think of two options:
Create your own wrapper class - either by extending Tomcat's DataSource class or by implementing Java's DataSource interface and delegating to the wrapped DataSource - and then add the logic you want to the desired methods and register a bean in a #Configuration class by manually instantiating your tomcat-jdbc DataSource (for examples on how to do so, refer to DataSourceConfiguration.Tomcat class) and wrapping it with your class.
Create an aspect and use Spring's AOP support to intercept calls to getConnection. Since DataSourceclass is in the javax package, I think you'll have to use AspectJ, and for some examples refer to this link
My suggestion would be to go with the first option, it should give you fewer headaches, here's a small example how you'd define your wrapper bean:
#Bean
public DataSource dataSource(DataSourceProperties properties) {
return new MyDataSourceWrapper(tomcatDataSourceFrom(properties));
}
private org.apache.tomcat.jdbc.pool.DataSource tomcatDataSourceFrom(
DataSourceProperties properties) {
// manual instantiation like in DataSourceConfiguration.Tomcat class
}

Spring and scheduled/repeated task

I'm using Spring and have created a web application. In my web app I've a rest service. I have one rest method called process which takes in a user's details (from an angular ui) and saves the users details to a database (SQLite)
Basically what I want to do is that when a user initiates a rest call. I want to start a separate thread (of only which one will exist). This thread will poll a database for certain values and emails an administrator if certain values are found. I need the class to be thread safe. See the below. Am I correct in using something like this ?. Do I need an #Async annotation ? Or should i use a TimerTask instead ?
#EnableScheduling
public class DBPoller {
#Scheduled(fixedRate = 5000)
public void checkDatabase() {
//checks the db for certain values
}
}
You must write
#EnableScheduling
in the Main Class of the Application
and the DBPoller class must be an Component of the Spring Framework.
So you must add the Annotation #Component (or #Service) to the Head of the DBPoller Class
It sounds like you do want to use an #Async annotation. #Scheduled won't really achieve the effect you are trying to achieve. #Scheduled would work if you were trying to run this check/email scenario on fixed time intervals, rather than on user request. Luckily the config for both is the same.
<task:annotation-driven scheduler="scheduler"
executor="asyncMethodTaskExecutor" />
<task:scheduler id="scheduler" pool-size="x" />
<bean id="asyncMethodTaskExecutor"
class="org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor">
<property name="threadCount" value="y"/>
<property name="threadNamePrefix" value="AsyncMethodWorkerThread"/>
</bean>
If you have the #EnableScheduling annotation, you don't need to define <task:scheduler id="scheduler" pool-size="x" />, but personally I prefer the XML configuration because if you want to change your thread pool size you only have to edit the XML values and restart your application, not recompile and redeploy the whole thing.
Make sure you change x and y to suitable values. This will depend on how many concurrent users you may have on your system.
You also need to make sure that your class is discoverable to the Spring context, and that this method is implementing an interface so that Spring can generate a proxy of it to actually invoke asynchronously like the below example
public interface AsyncService {
public void checkDatabase();
}
#Service
public class AsyncServiceImpl implements AsyncService {
#Override
#Async
public void checkDatabase(){
//Do your database check here.
}
}
You also need to make sure that the package your service is in can be found by Spring, double check your <context:component-scan> value.
Happy asynchronous execution.

spring annotation manage pojo

I use spring annotation to manage java bean, use #service in service layer, and #autowired when inject service, but now I have a question, how to manage POJO by spring?
for example, I need to return a user for ajax call, so I need to always write like:
User user = new User()......
return user;
So, how I can use like
#autowired User user;
And the User POJO will be:
#component
#scope("prototype")
public class User{}
so each time the user entity will be a new one, and I needn't to new it everytime,
But I failed to write like this, so can spring manage POJO to be a prototype?
Update====================================
Thanks for answering
You have three options that i can think of straigh away. What you want is to be able to create prototypes from within a singleton. So you can either use..
1) AOP Scoped Proxy.
So change your User class annotation to ...
#Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
(this required cglib on your classpath)
or
2) lookup-method
this is a bit more involved and makes things a bit harder to test
or
3) make your class implement ApplicationContextAware and then you can just call getBean on the context when you want a new prototype.
A bit of googling will sort you out anyway but I recommend the first option
Try following code:
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class User
{
......
}
Hope it helps.

Resources