How to configure Entity Manager in Jersey 2 - ejb-3.0

I'm working on migrating my existing EJB project from Jersey 1.x to Jersey 2.x.
I'm facing the exception while trying to execute my EJB Queries. After some research I'm able to find out that issue is with the EntityManager configuration.
In Existing Jersey 1.x code, we configure EnitityManager in the below way,
#PersistenceContext(unitName = "datasource")
private EntityManager em;
em.createQuery("SELECT e FROM Employee e WHERE e.userName = :userName").setParameter("userName", userName).getSingleResult();
But my EJB query is not working, resulting in Internal Server Exception.
When I changed the Entity Manager configuration in below way, it worked
private static EntityManager em;
static {
em = Persistence.createEntityManagerFactory("datasource").createEntityManager();
}
My question here is,
1) Is this the right way to do ?
2) is there any specific procedure to configure EntityManager in Jersey 2?
UPDATE:
I learned through this link How do I properly configure an EntityManager in a jersey / hk2 application? and implemented in the following way and it worked
Class implementing Entity Manager Factory:
public class EMFactory implements Factory<EntityManagerFactory> {
private final EntityManagerFactory emf;
public EMFactory () {
emf = Persistence.createEntityManagerFactory("datasource");
}
#Override
public void dispose(EntityManagerFactory emf) {}
#Override
public EntityManagerFactory provide() {
return emf;
}
}
Class implementing Entity Manager:
public class MyEntityManager implements Factory<EntityManager> {
private final EntityManager em;
#Inject
public MyEntityManager (EntityManagerFactory emf) {
em = emf.createEntityManager();
}
#Override
public void dispose(EntityManager em) {
if (em.isOpen()) {
em.close();
}
}
#Override
public EntityManager provide() {
return em;
}
Binded these classes to Abstract Binder:
public class ApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bindFactory(EMFactory .class).to(EntityManagerFactory.class).in(Singleton.class);
bindFactory(MyEntityManager .class).to(EntityManager.class).in(RequestScoped.class);
}
Loaded the Binder and mapped this loader class in web.xml
public class BinderLoader extends ResourceConfig {
public BinderLoader() {
register(new ApplicationBinder());
packages(true, "com.package.resources");
}
}
Used the Entity Manager in the following way:
#Inject
private javax.inject.Provider<EntityManager> emFactory;
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public UserVO getUser(String userName) {
EntityManager em = emFactory.get();
user = (User) em.createQuery("SELECT u FROM User u WHERE u.userName = :userName").setParameter("userName", userName).getSingleResult();
}
But my question is everytime when emFactory.get() is called, new instance is created. This affects my application's performance. Is there any solution to create a single instance of EntityManager and use across where ever needed ?

Related

Unable to enable hibernate filter in spring EntityManager using spring aop

I'm trying to enable a hibernate filter through spring EntityManager by tyring to pointcut a service implementation method annotated with custom annotation #TenantAware and add #Around advise to that method. I want to enable custom filter which adds a differentiator where tenant_id = :tenantId on all entities that extend a BaseEntity. Hence I created the custom annotation and using it on #Transactional methods where it is required. It is intercepting the method successfully but the variable values when I log them are showing up empty and neither is the filter being set.
The project is a spring-boot 2 application and I'm using spring aop for creating the aspect. I'm using Hibernate 5 as the JPA implementation provider.
Load time weaving of the SimpleJpaRepository.class is not possible since it does not expose a noarg constructor.
This is my TenantFilterAdvisor class.
package org.foo.bar.advisors;
#Aspect
#Slf4j
#Component
public class TenantFilterAdvisor {
#PersistenceContext
private EntityManager entityManager;
public TenantFilterAdvisor() {
log.debug("###########################################################################");
log.debug("###################### Tenant Advisor Filter Started ######################");
log.debug("###########################################################################");
}
#Pointcut(value = "#annotation(org.foo.bar.TenantAware)")
public void methodAnnotatedWithTenantAware() {
}
#Pointcut(value = "execution(public * * (..))")
public void allPublicMethods() {
}
#Around(value = "methodAnnotatedWithTenantAware() && allPublicMethods()")
public Object enableTenantFilter(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.debug("###########################################################################");
log.debug("###################### Before enabling tenant filter ######################");
log.debug("###########################################################################");
if (null != entityManager) {
log.debug("Tenant filter name: ", "tenantFilter");
log.debug("Tenant filter property: ", "tenantId");
log.debug("Setting tenant id to: ", new Long(10));
Session session = entityManager.unwrap(Session.class);
Filter filter = session.enableFilter("tenantFilter");
filter.setParameter("tenantId", new Long(10));
}
Object result = proceedingJoinPoint.proceed();
// Code to disable the hibernate filter goes here.
log.debug("###########################################################################");
log.debug("###################### After disabling tenant filter ######################");
log.debug("###########################################################################");
return result;
}
}
The relevant part of service interface and implementation class is
public interface InventoryService {
Inventory getInventoryById(Long id);
}
#Service
public class InventoryServiceImpl implements InventoryService {
#Autowired
private InventoryRepository repo;
#Override
#Transactional
#TenantAware
public Inventory getInventoryById(Long id) {
LOG.debug("getInventoryById() called with: id = {}", id);
final Optional<Inventory> inventoryOp = repo.findById(id);
if (inventoryOp.isPresent()) {
return inventoryOp.get();
} else {
throw new InventoryNotFoundException(String.format(MESSAGE_INVENTORY_NOT_FOUND_FOR_ID, id));
}
}
}
The repository interface is
#Repository
#Transactional(readOnly = true)
public interface InventoryRepository extends BaseRepository<Inventory, Long> {
}
The BaseRepository interface extends JpaRepository.
And the aspect configuration class is
#Configuration
#ComponentScan(basePackages = {"org.foo.bar.advisors"})
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class AOPConfig {
}
And finally the relevant MappedSuperClass which is inherited by other classes has the filter defined as
#Getter
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#FilterDef(
name = "tenantFilter",
parameters = #ParamDef(name = "tenantId", type = "long")
)
#Filter(name = "tenantFilter", condition = "tenant_id = :tenantId")
public abstract class BaseTransactionalEntity extends BaseEntity {
#Column(name = "tenant_id", nullable = false)
private Long tenantId;
}
Here is the cutom annotation class if you need the detail
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#Inherited
public #interface TenantAware {
}
I need the hibernate filter to be enabled in session and disabled after the proceeding join point completes the execution. But it is not so. What am I missing?
As explained in the Hibernate Reference Guide filters only apply to entity queries not to direct fetching. In your code you are doing a direct fetch through findById which translates to entityManager.find and is thus a direct fetch.
You could override the Spring JPA repository and reimplement the findById to be an entity query instead of a direct fetch, to workaround this issue.
An alternative (and proven to be working) way without AOP is using TransactionManagerCustomizers:
#Configuration
public class HibernateFilterConfig {
#Bean
#ConditionalOnMissingBean
public PlatformTransactionManager transactionManager(
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
JpaTransactionManager transactionManager = new JpaTransactionManager() {
#Override
#NonNull
protected EntityManager createEntityManagerForTransaction() {
final EntityManager entityManager = super.createEntityManagerForTransaction();
Session session = entityManager.unwrap(Session.class);
session.enableFilter("tenantFilter").setParameter("tenantId", new Long(10));
return entityManager;
}
};
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
}

How to inject a Spring bean into JpaRepositoryFactoryBean

I have my own custom Spring Data common repository in order to provide common behavior to all Spring Data repositories. And all I need is to modify EntityManager when repository is being created. But I can't inject a Spring bean into JpaRepositoryFactoryBean due to the bean is created via new operator.
public class BasicJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {
#Autowired
private SomeService service; // - it does not work
#Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
// do some logic here
service.doSmth();
return new CommonRepositoryFactory<>(em);
}
private static class CommonRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private final EntityManager em;
public CommonRepositoryFactory(EntityManager em) {
super(em);
this.em = em;
}
#SuppressWarnings("unchecked")
protected Object getTargetRepository(RepositoryMetadata metadata) {
JpaEntityInformation entityInformation = getEntityInformation(metadata.getDomainType());
return new CommonRepositoryImpl(entityInformation, em);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return CommonRepositoryImpl.class;
}
}
}
Implement a setter in that class or one that extends from it.

Eclipse Link Multitenancy not working

Eclipse Link Multitenancy is not working properly.
Example Entity (the schema is being created by liquibase):
#Entity
#Table(name = "ENTITIES")
#Multitenant(MultitenantType.SINGLE_TABLE)
#TenantDiscriminatorColumn(name = "TENANT_ID", contextProperty = "eclipselink.tenant-id")
public class EntityClass
To set the multitenancy property on entity managers I use an aspect, like following:
#Around("execution(* javax.persistence.EntityManagerFactory.*(..))")
public Object invocate(ProceedingJoinPoint joinPoint) throws Throwable {
final Object result = joinPoint.proceed();
if (result instanceof EntityManager) {
EntityManager em = (EntityManager) result;
final String tenantId = TenantContext.getCurrentTenantId();
LOG.debug("Set EntityManager property for tenant {}.", tenantId);
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT,
tenantId);
return em;
}
return result;
}
When I start the Spring Boot application this works perfectly. To have tenant information available during integration tests, I defined an annotation:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface AsTenant {
String value();
}
To bind this value, I use a TestExecutionListener:
#Override
public void beforeTestMethod(TestContext testContext) throws Exception {
final Method testMethod = testContext.getTestMethod();
final AsTenant asTenantAnnotation = testMethod
.getAnnotation(AsTenant.class);
if (asTenantAnnotation != null) {
TenantContext.setCurrentTenantId(asTenantAnnotation.value());
}
}
By debugging I can clearly say that the TestExectionListener is called before any EM is created and that the property is properly set for the EMs. When persisting anything to the database, Eclipse Link does not set a value for the column.
Maybe anybody can help me out with this, I have no Idea why EclipseLink Multitenancy is not working.
Ok, I got it working. If anybody ever faces a similar problem, here is my solution to it.
If using transactions, the context property for the tenant discrimination has to be set after the transaction is started (http://www.eclipse.org/eclipselink/documentation/2.5/solutions/multitenancy002.htm).
EntityManager em = createEntityManager(MULTI_TENANT_PU);
em.getTransaction().begin();
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "my_id");
To realise this in Spring Boot/Data environment, I customized Spring's JpaTransactionManager. This stays in addition to the Aspect in the Question, since there is not Transaction for SELECT queries.
public class MultitenantJpaTransactionManager extends JpaTransactionManager {
/* (non-Javadoc)
* #see org.springframework.orm.jpa.JpaTransactionManager#doBegin(java.lang.Object, org.springframework.transaction.TransactionDefinition)
*/
#Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
super.doBegin(transaction, definition);
final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(getEntityManagerFactory());
final EntityManager em = emHolder.getEntityManager();
final String tenantId = TenantContext.getCurrentTenantId();
if (tenantId != null) {
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, tenantId);
}
}
}
This is easily wired via JpaConfiguration:
/**
* Configures Eclipse Link as JPA Provider.
*/
#Configuration
#EnableTransactionManagement
#AutoConfigureAfter({ DataSourceAutoConfiguration.class })
public class JpaConfiguration extends JpaBaseConfiguration {
#Bean
#Override
public PlatformTransactionManager transactionManager() {
return new MultitenantJpaTransactionManager();
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
EclipseLinkJpaVendorAdapter adapter = new EclipseLinkJpaVendorAdapter();
return adapter;
}
#Override
protected Map<String, Object> getVendorProperties() {
HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put(PersistenceUnitProperties.WEAVING, detectWeavingMode());
return properties;
}
private String detectWeavingMode() {
return InstrumentationLoadTimeWeaver.isInstrumentationAvailable()
? "true" : "static";
}
}
Disclaimer: This does not answer the above query but provides an alternative.
Using bytecode instrumentation, I have created a java example on Multi-Tenancy (Table per Tenant) with Eclipse Link and Spring Data. This idea is chosen to utilize the complete power of Spring Data.
One can execute MultiTenantTest to see it working.
The idea is open-sourced and is available at Maven Central
Steps:
1.Include dependency
<dependency>
<groupId>org.bitbucket.swattu</groupId>
<artifactId>jpa-agent</artifactId>
<version>2.0.2</version>
</dependency>
2.Create a class as shown below. Package, Class and method has to be exactly same.
package org.swat.jpa.base;
import javax.persistence.EntityManager;
public class EntityManagerFactoryListener {
/**
* This method is called by JPA Agent.
*
* #param entityManager the entity manager
*/
public static void afterCreateEntityManager(EntityManager entityManager) {
//Business logic to set appropriate values in entityManager
final String tenantId = TenantContext.getCurrentTenantId();
if (tenantId != null) {
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, tenantId);
}
}
}
3.Add javaagent when starting java
-javaagent:{path-to-jpa-agent-jar}

#Transactional on Spring shutdown to properly shutdown Hsqldb

The heart of this question is: Is it possible to execute a Transaction from a method triggered by a Spring shutdown hook?
At the moment I have a HyperSqlDbServer class that implements SmartLifeCycle as found in this question:
In a spring bean is it possible to have a shutdown method which can use transactions?
I have a method in that class that is marked transactional that gets invoked as part of the stop method:
#Transactional
public void executeShutdown() {
hsqlDBShutdownService.executeShutdownQuery();
hsqlDBShutdownService.closeEntityManager();
}
The service used in that method is a bit of a hack that I had to do because I could not autowire in the EntityManager to this class:
#Service
public class HsqlDBShutdownService {
#PersistenceContext
private EntityManager entityManager;
#Autowired
private HyperSqlDbServer hyperSqlDbServer;
#Transactional
public void executeShutdownQuery() {
entityManager.createNativeQuery("SHUTDOWN").executeUpdate();
}
#Transactional
public void closeEntityManager() {
entityManager.close();
}
#PostConstruct
public void setHsqlDBShutdownService() {
hyperSqlDbServer.setShutdownService(this);
}
}
You may notice that all I'm really trying to accomplish is invoking the query "SHUTDOWN" before stopping the server. Without this, the hsqldb lock file sticks around on server restart, and the server throws an exception.
The code above produces the following exception:
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:96)
...
So my original question stands, but if anyone has a thought on how I could execute this query another way I'll try that as well.
FYI, I've also tried the #PreDestroy annotation, but get the same TransactionRequiredException.
Edit: For completeness, I am using the JpaTransactionManager and the #Transactional annotations work throughout my project, except on shutdown...
Edit 2: Datasource and transaction manager configuration:
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:persistence.properties")
public class PersistenceConfig implements TransactionManagementConfigurer {
private static final String PASSWORD_PROPERTY = "dataSource.password";
private static final String USERNAME_PROPERTY = "dataSource.username";
private static final String URL_PROPERTY = "dataSource.url";
private static final String DRIVER_CLASS_NAME_PROPERTY = "dataSource.driverClassName";
#Autowired
private Environment env;
#Bean
#DependsOn("hsqlDb")
public DataSource configureDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty(DRIVER_CLASS_NAME_PROPERTY));
dataSource.setUrl(env.getProperty(URL_PROPERTY));
dataSource.setUsername(env.getProperty(USERNAME_PROPERTY));
dataSource.setPassword(env.getProperty(PASSWORD_PROPERTY));
return dataSource;
}
#Bean
#DependsOn("hsqlDb")
public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(configureDataSource());
entityManagerFactoryBean.setPackagesToScan("com.mycompany.model.db");
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, env.getProperty(org.hibernate.cfg.Environment.DIALECT));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_AUTO));
jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, env.getProperty(org.hibernate.cfg.Environment.SHOW_SQL));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Override
#Bean()
#DependsOn("hsqlDb")
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new JpaTransactionManager();
}
}
I found a workaround for shutting down the HsqlDB database, but it involves avoiding the use of Spring's EntityManager and #Transactional as they apparently do not work during server shutdown. My modified HsqlDBShutdownService is below. The key change is that instead of using the EntityManager to invoke the query, I create a new jdbc connection manually, and invoke the query that way. This avoids the requirement for #Transactional:
#Service
public class HsqlDBShutdownService {
#Autowired
private ApplicationContext applicationContext;
#PersistenceContext
private EntityManager entityManager;
#Autowired
private HyperSqlDbServer hyperSqlDbServer;
public void executeShutdownQuery() {
Connection conn = null;
try {
JdbcTemplate jdbcTemplate = new JdbcTemplate(this.applicationContext.getBean(DataSource.class));
conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
conn.setAutoCommit(true);
jdbcTemplate.execute("SHUTDOWN");
} catch(Exception ex) {
ex.printStackTrace();
} finally {
try {
if(conn != null)
conn.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
#Transactional
public void closeEntityManager() {
entityManager.close();
}
#PostConstruct
public void setHsqlDBShutdownService() {
hyperSqlDbServer.setShutdownService(this);
}
}
The server can now restart successfully without leaving Hsqldb lock files around.

Injecting a Spring dependency into a JPA EntityListener

I am trying to inject a Spring dependency into an JPA EntityListener. Here is my listener class:
#Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true)
public class PliListener {
#Autowired
private EvenementPliRepository evenementPliRepository;
#PostPersist
void onPostPersist(Pli pli) {
EvenementPli ev = new EvenementPli();
ev.setPli(pli);
ev.setDateCreation(new Date());
ev.setType(TypeEvenement.creation);
ev.setMessage("Création d'un pli");
System.out.println("evenementPliRepository: " + evenementPliRepository);
evenementPliRepository.save(ev);
}
}
Here is my Entity class:
#RooJavaBean
#RooToString
#RooJpaActiveRecord
#EntityListeners(PliListener.class)
public class Pli implements Serializable{
...
However, my dependency (i.e. evenementPliRepository) is always null.
Can anyone please help?
A hack to inject dependencies on stateless beans, is to define the dependency as "static", create a setter method so that Spring can inject the dependency (assigning it to the static dependency).
Declare the dependency as static.
static private EvenementPliRepository evenementPliRepository;
Create a method so that Spring can inject it.
#Autowired
public void init(EvenementPliRepository evenementPliRepository)
{
MyListenerClass.evenementPliRepository = evenementPliRepository;
logger.info("Initializing with dependency ["+ evenementPliRepository +"]");
}
More details at: http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html
This is actually an old question but I found an alternative solution :
public class MyEntityListener {
#Autowired
private ApplicationEventPublisher publisher;
#PostPersist
public void postPersist(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnCreatedEvent<>(this, target));
}
#PostUpdate
public void postUpdate(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnUpdatedEvent<>(this, target));
}
#PostRemove
public void postDelete(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnDeletedEvent<>(this, target));
}
}
Probably not the best one but better than static variables w/o AOP + weaving.
I annotated the listener with #Component annotation, then created a non static setter to assign the injected Spring bean, it works well
My code looks like :
#Component
public class EntityListener {
private static MyService service;
#Autowired
public void setMyService (MyService service) {
this.service=service;
}
#PreUpdate
public void onPreUpdate() {
service.doThings()
}
#PrePersist
public void onPersist() {
...
}
}
Since Spring V5.1 (and Hibernate V5.3) it should work out of the box as Spring registers as the provider of those classes.
see documentation of SpringBeanContainer
And what about this solution?
#MappedSuperclass
#EntityListeners(AbstractEntityListener.class)
public abstract class AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "creation_date")
private Date creationDate;
#Column(name = "modification_date")
private Date modificationDate;
}
Then the Listener...
#Component
public class AbstractEntityListener {
#Autowired
private DateTimeService dateTimeService;
#PreUpdate
public void preUpdate(AbstractEntity abstractEntity) {
AutowireHelper.autowire(this, this.dateTimeService);
abstractEntity.setModificationDate(this.dateTimeService.getCurrentDate());
}
#PrePersist
public void prePersist(AbstractEntity abstractEntity) {
AutowireHelper.autowire(this, this.dateTimeService);
Date currentDate = this.dateTimeService.getCurrentDate();
abstractEntity.setCreationDate(currentDate);
abstractEntity.setModificationDate(currentDate);
}
}
And the helper...
/**
* Helper class which is able to autowire a specified class. It holds a static reference to the {#link org
* .springframework.context.ApplicationContext}.
*/
public final class AutowireHelper implements ApplicationContextAware {
private static final AutowireHelper INSTANCE = new AutowireHelper();
private static ApplicationContext applicationContext;
private AutowireHelper() {
}
/**
* Tries to autowire the specified instance of the class if one of the specified beans which need to be autowired
* are null.
*
* #param classToAutowire the instance of the class which holds #Autowire annotations
* #param beansToAutowireInClass the beans which have the #Autowire annotation in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
}
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) {
AutowireHelper.applicationContext = applicationContext;
}
/**
* #return the singleton instance.
*/
public static AutowireHelper getInstance() {
return INSTANCE;
}
}
Works for me.
Source:
http://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/
I started to go down the path of using AOP to inject a spring bean into an Entity listener. After a day and a half of research and trying different things I came across this link which stated:
It is not possible to inject spring managed beans into a JPA EntityListener class. This is because the JPA listener mechanism should be based on a stateless class, so the methods are effectively static, and non-context aware. ... No amount of AOP will save you, nothing gets injected to the ‘object’ representing the listener, because the implementations don’t actually create instances, but uses the class method.
At this point I regrouped and stumbled across the EclipseLink DescriptorEventAdapter. Using this information I created a listener class that extended the Descriptor Adapter.
public class EntityListener extends DescriptorEventAdapter {
private String injectedValue;
public void setInjectedValue(String value){
this.injectedValue = value;
}
#Override
public void aboutToInsert(DescriptorEvent event) {
// Do what you need here
}
}
In order to use the class I could have used the #EntityListeners annotation on my entity class. Unfortunately, this method would not allow Spring to control the creation of my listener and as a result would not allow for dependency injection. Instead I added the following 'init' function to my class:
public void init() {
JpaEntityManager entityManager = null;
try {
// Create an entity manager for use in this function
entityManager = (JpaEntityManager) entityManagerFactory.createEntityManager();
// Use the entity manager to get a ClassDescriptor for the Entity class
ClassDescriptor desc =
entityManager.getSession().getClassDescriptor(<EntityClass>.class);
// Add this class as a listener to the class descriptor
desc.getEventManager().addListener(this);
} finally {
if (entityManager != null) {
// Cleanup the entity manager
entityManager.close();
}
}
}
Add a little Spring XML configuration
<!-- Define listener object -->
<bean id="entityListener" class="EntityListener " init-method="init">
<property name="injectedValue" value="Hello World"/>
<property name="entityManagerFactory" ref="emf"/>
</bean>
Now we have a situation where Spring creates a entity listener, injects it with whatever dependencies are needed, and the listener object registers itself with the entity class to which it intends to listen.
I hope this helps.
try use ObjectFactory like this
#Configurable
public class YourEntityListener {
#Autowired
private ObjectFactory<YourBean> yourBeanProvider;
#PrePersist
public void beforePersist(Object target) {
YourBean yourBean = yourBeanProvider.getObject();
// do somthing with yourBean here
}
}
I found this solution in org.springframework.data.jpa.domain.support.AuditingEntityListener from spring-data-jpa.
demo: https://github.com/eclipseAce/inject-into-entity-listener
I tested out the approach suggested in https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/ and worked. Not very clean but does the job. Slightly modified AutowireHelper class for me looked like this:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class AutowireHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private AutowireHelper() {
}
public static void autowire(Object classToAutowire) {
AutowireHelper.applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) {
AutowireHelper.applicationContext = applicationContext;
}
}
Then called this from entity listener like this:
public class MyEntityAccessListener {
#Autowired
private MyService myService;
#PostLoad
public void postLoad(Object target) {
AutowireHelper.autowire(this);
myService.doThings();
...
}
public void setMyService(MyService myService) {
this.myService = myService;
}
}
The problem with JPA Listeners is that:
they are not managed by Spring (so no injections)
they are (or might be) created before Spring's Application Context is ready (so we can't inject beans on a constructor call)
My workaround to deal with the issue:
1) Create Listener class with public static LISTENERS field:
public abstract class Listener {
// for encapsulation purposes we have private modifiable and public non-modifiable lists
private static final List<Listener> PRIVATE_LISTENERS = new ArrayList<>();
public static final List<Listener> LISTENERS = Collections.unmodifiableList(PRIVATE_LISTENERS);
protected Listener() {
PRIVATE_LISTENERS.add(this);
}
}
2) All JPA listeners that we want to be added to Listener.LISTENERS has to extend this class:
public class MyListener extends Listener {
#PrePersist
public void onPersist() {
...
}
...
}
3) Now we can get all listeners and inject beans just after Spring's Application Context is ready
#Component
public class ListenerInjector {
#Autowired
private ApplicationContext context;
#EventListener(ContextRefreshedEvent.class)
public void contextRefreshed() {
Listener.LISTENERS.forEach(listener -> context.getAutowireCapableBeanFactory().autowireBean(listener));
}
}
I believe it is because this listener bean is not under control of Spring. Spring is not instantiating it, how can Spring know how to find that bean and do the injection?
I haven't tried on that, but seems that you can make use of AspectJ Weaver with Spring's Configurable annotation to have Spring control non-Spring-instantiated beans.
http://static.springsource.org/spring/docs/3.1.2.RELEASE/spring-framework-reference/html/aop.html#aop-using-aspectj
Since version 5.3 of Hibernate and version 5.1 of Spring (that's version 2.1 of Spring Boot), there's an easy solution.
No hack, no need to use AOP, no helper classes, no explicit autowiring, no init block to force injection.
You just need to:
Make the listener a #Component and declare the autowired bean, as usual.
Configure JPA in your Spring application to use Spring as the bean provider.
Here's how (in Kotlin)...
1) Entity listener
#Component
class EntityXyzListener(val mySpringBean: MySpringBean) {
#PostLoad
fun afterLoad(entityXyz: EntityXyz) {
// Injected bean is available here. (In my case the bean is a
// domain service that I make available to the entity.)
entityXyz.mySpringBean= mySpringBean
}
}
2) JPA datasource config
Get access to LocalContainerEntityManagerFactoryBean in your application. Then add to jpaPropertyMap the following key-value pair: AvailableSettings.BEAN_CONTAINER => the application context's bean factory.
In my Spring Boot application I already had the code below to configure a datasource (boilerplate code found here for example). I only had to add the line of code that puts the BEAN_CONTAINER property in the jpaPropertyMap.
#Resource
lateinit var context: AbstractApplicationContext
#Primary
#Bean
#Qualifier("appDatasource")
#ConfigurationProperties(prefix = "spring.datasource")
fun myAppDatasource(): DataSource {
return DataSourceBuilder.create().build()
}
#Primary
#Bean(name = ["myAppEntityManagerFactory"])
fun entityManagerFactoryBean(builder: EntityManagerFactoryBuilder): LocalContainerEntityManagerFactoryBean {
val localContainerEntityManagerFactoryBean =
builder
.dataSource(myAppDatasource())
.packages("com.mydomain.myapp")
.persistenceUnit("myAppPersistenceUnit")
.build()
// the line below does the trick
localContainerEntityManagerFactoryBean.jpaPropertyMap.put(
AvailableSettings.BEAN_CONTAINER, SpringBeanContainer(context.beanFactory))
return localContainerEntityManagerFactoryBean
}
Another option:
Create a service to make AplicationContext accessible:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import lombok.Setter;
#Service
class ContextWrapper {
#Setter
private static ApplicationContext context;
#Autowired
public ContextWrapper(ApplicationContext ac) {
setContext(ac);
}
}
Use it:
...
public class AuditListener {
private static final String AUDIT_REPOSITORY = "AuditRepository";
#PrePersist
public void beforePersist(Object object){
//TODO:
}
#PreUpdate
public void beforeUpdate(Object object){
//TODO:
}
#PreRemove
public void beforeDelete(Object object) {
getRepo().save(getAuditElement("DEL",object));
}
private Audit getAuditElement(String Operation,Object object){
Audit audit = new Audit();
audit.setActor("test");
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
audit.setDate(timestamp);
return audit;
}
private AuditRepository getRepo(){
return ContextWrapper.getContext().getBean(AUDIT_REPOSITORY, AuditRepository.class);
}
}
This class is created as a listener from jpa:
...
#Entity
#EntityListeners(AuditListener.class)
#NamedQuery(name="Customer.findAll", query="SELECT c FROM Customer c")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
...
Since the listener is not under Spring's control, it can not access the context bean. I have tried multiple options (#Configurable (...)) and none has worked except to create a class that static access to the context. Already in that dilemma I think that this is an elegant option.
Building on the answer of Paulo Merson, here is a variation of how to set the SpringBeanContainer by utilizing JpaBaseConfiguration. Here are both steps:
Step 1: Define the listener as a Spring component. Note that autowiring works through constructor injection.
#Component
public class PliListener {
private EvenementPliRepository evenementPliRepository;
public PliListener(EvenementPliRepository repo) {
this.evenementPliRepository = repo;
}
#PrePersist
public void touchForCreate(Object target) {
// ...
}
#PostPersist
void onPostPersist(Object target) {
// ...
}
}
Step 2: Set the SpringBeanContainer, which enables autowiring in the listener. SpringBeanContainer JavaDoc might be worth a look.
#Configuration
public class JpaConfig extends JpaBaseConfiguration {
#Autowired
private ConfigurableListableBeanFactory beanFactory;
protected JpaConfig(DataSource dataSource, JpaProperties properties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager) {
super(dataSource, properties, jtaTransactionManager);
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Override
protected Map<String, Object> getVendorProperties() {
Map<String, Object> props = new HashMap<>();
// configure use of SpringBeanContainer
props.put(org.hibernate.cfg.AvailableSettings.BEAN_CONTAINER,
new SpringBeanContainer(beanFactory));
return props;
}
}
The most natural way is, in my opinion, to intervene into the process of instantiating of EntityListener.
This way significantly differs in Hibernate pre-5.3 versions and post-5.3 ones.
1) In Hibernate versions earlier than 5.3 org.hibernate.jpa.event.spi.jpa.ListenerFactory is responsible for EntityListener instantiation. The instantiation of this factory can be intercepted if you provide your own CDI-based javax.enterprise.inject.spi.BeanManager. The CDI interfaces are (unnecessary for Spring DI world) verbose, but it's not difficult to implement Spring BeanFactory-backed CDI Bean manager.
#Component
public class SpringCdiBeanManager implements BeanManager {
#Autowired
private BeanFactory beanFactory;
#Override
public <T> AnnotatedType<T> createAnnotatedType(Class<T> type) {
return new SpringBeanType<T>(beanFactory, type);
}
#Override
public <T> InjectionTarget<T> createInjectionTarget(AnnotatedType<T> type) {
return (InjectionTarget<T>) type;
}
...
// have empty implementation for other methods
}
and the implementation of type-dependent SpringBeanType<T> will look like this:
public class SpringBeanType <T> implements AnnotatedType<T>, InjectionTarget<T>{
private BeanFactory beanFactory;
private Class<T> clazz;
public SpringBeanType(BeanFactory beanFactory, Class<T> clazz) {
this.beanFactory = beanFactory;
this.clazz = clazz;
}
#Override
public T produce(CreationalContext<T> ctx) {
return beanFactory.getBean(clazz);
}
...
// have empty implementation for other methods
}
Now, the only thing left is to inject into Hibernate Configuration Settings our implementation of BeanManager under a property name javax.persistence.bean.manager. There are, probably, many ways to do so, let me bring just one of them:
#Configuration
public class HibernateConfig {
#Autowired
private SpringCdiBeanManager beanManager;
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(){
#Override
public Map<String, Object> getJpaPropertyMap(){
Map<String, Object> jpaPropertyMap = super.getJpaPropertyMap();
jpaPropertyMap.put("javax.persistence.bean.manager", beanManager);
return jpaPropertyMap;
}
};
// ...
return jpaVendorAdapter;
}
}
Just remember that two things have to be Spring beans:
a) SpringCdiBeanManager, so that BeanFactory could be injected/autowired to it;
b) your EntityListener class, so that line return beanFactory.getBean(clazz); will be successful.
2) In Hibernate versions 5.3 and later things are much easier for Spring beans, as #AdrianShum very correctly pointed out. Since 5.3 Hibernate uses org.hibernate.resource.beans.container.spi.BeanContainer concept and there is its ready-to-use implementation for Spring Beans, org.springframework.orm.hibernate5.SpringBeanContainer. In this case, just follow its javadoc.
As others have pointed out, it appears SpringBeanContainer is the way to wire up Spring to Hibernate's ManagedBeanRegistryImpl, which is responsible for creating instances of EntityListeners when Hibernate is creating it's callback objects. Calls to create beans are delegated to SpringBeanContainer which can create Spring beans with both constructor injection and autowiring. For example a EntityListener would look like
public class MyEntityListener {
#Autowired
private AnotherBean anotherBean;
private MyBean myBean;
public InquiryEntityListener(MyBean myBean) {
this.myBean = myBean;
}
public MyEntityListener() {
}
}
Note that the EntityListener does NOT require #Component annotation as this only creates an extra instance which is not used by Hibernate.
However when using SpringBeanContainer there are some important limitations and caveats that must be kept in mind. In our use case, instances of our EntityListener were created during the creation of Hibernate EntityManager. As this happened fairly early during the Spring lifecycle, many beans did not exist at this time. This led to the following discovery:
The SpringBeanContainer will only autowire/constructor bean dependencies that exist at the time when the EntityListener is created. Constructor dependencies that don't exist will cause the default constructor to be called. Essentially there is a race condition when using SpringBeanContainer.
The work around for this is to inject a DefaultListableBeanFactory instance into the EntityListener. Later when the EntityListeners lifecycle methods are called (i.e. #PostLoad, #PostPersist, etc.) instances of the desired bean can be pulled out of the BeanFactory as the beans would've been created by Spring at this point.

Resources