# AutoWiring Spring Beans Into Classes Not Managed By Spring Like JPA Entity Listeners

In my previous article JPA Auditing: Persisting Audit Logs Automatically using EntityListeners, I have discussed how we can use Spring Data JPA automate Auditing and automatically create audit logs or history records and update CreatedBy, CreatedDate, LastModifiedBy, LastModifiedDate properties.

So in order to save history records for our File entity, we were trying to auto-wire EntityManager inside our FileEntityListener class and we have come to know that we can not do this.

We can not inject any Spring-managed bean in the EntityListener because EntityListeners are instantiated by JPA before Spring inject anything into it. EntityListeners are not managed Spring so Spring cannot inject any Spring-managed bean e.g. EntityManager in the EntityListeners.

And this case is not just with EntityListeners, you can not auto wire any Spring-managed bean into another class (i.e. utility classes) which is not managed by Spring.

Because it is a very common problem and can also arise with other classes so I tried to come out with a common solution which will not just solve this problem but will also help us getting Spring managed beans in other places.

So I have created one utility class to fetch any bean according to our requirement.

@Service
public class BeanUtil implements ApplicationContextAware {

private static ApplicationContext context;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}

public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}

}


Now to get any a bean in class we will just need call the BeanUtil.getBean(YourClass.class) and pass the class type to it and we will get the bean.

For Example in our case, we were trying to get the EntityManager bean inside FileEntityListener, we can simply do it by writing BeanUtil.getBean(EntityManager.class).

public class FileEntityListener {

private void perform(File target, Action action) {
EntityManager entityManager = BeanUtil.getBean(EntityManager.class);
entityManager.persist(new FileHistory(target, action));
}

}


You can find complete code on this Github Repository and please feel free to provide your valuable feedback.
Next Post Newer Post Previous Post Older Post Home

1. hello, I've tried this and got an NullPointerException for the ApplicationContext object (the static one).

1. Hi Agustinus, you should not get any problems in it.
However you find the complete code on https://github.com/njnareshjoshi/articles/tree/master/spring-data-jpa-auditing

2. probably because ApplicationContext is not YET set when you call for EntityManager

2. In class BeanUtil the context has to be set first otherwise the npe would be thrown when getBean method is invoked. I assume this is the problem that Agustinus has. In your repo I have not found the place where you provide this context to the utility class.

1. BeanUtil class is implementing ApplicationContextAware and overriding setApplicationContext method which automatically get called and set the applicationContext in the BeanUtil. I have executed the repo's code and it working fine, please tell me if you are getting the problem while executing it.

3. For the Multi module spring boot projects where beanUtil class should be place?

1. It depends on the project architecture and from where you want access this code. If you want to access it from multiple modules then BeanUtil class should be in a module which is visible from other modules. Some kind of core module which other module can access.

2. I am getting RuntimeException
No EntityManager with actual transaction available for current thread
do you have any idea why?

3. I am not sure but it seems like some configuration issue, because your entity manager is not intialized yet.

4. Hi, after trying other solutions which did not work for me;
this has worked for me for an entity listener's @PostUpdate method.

Thank you very much.

5. Hi Naresh, I'm having similar issue, where I have Model as separate module. Model has two packages, each package is an entityscan for 2 other modules. and when ran those other 2 modules, I can see the applicationcontext loading for only 1 module on startup and not loading for another module. Not sure how I need to set the architecture for model class such that context is loaded for both the modules on startup.

Below is the package structure of modules, Not sure, if the below is easily understood.

Module-service1
com.test.service1 (Package)
com.test.service1.audit (Package)
AuditRelatedClasses
MainApplication (EntityScan = com.test.model)

Module-service2
com.test.anotherservice2 (Package)
com.test.anotherservice2.audit (Package)
AuditRelatedClasses
MainApplication (EntityScan = com.test.anotherservice.model)

Model
com.test.anotherservice.model
com.test.anotherservice.model.action
Action
BeanUtil
com.test.anotherservice.model.consumer
Consumer
ConsumerEntityListener
ConsumerHistory
com.test.model
com.test.model.action
Action
BeanUtil
com.test.model.action.user
User
UserEntityListener
UserHistory

6. Please let me know how to set the architecture for model module such that when module-service1 is started it can set the application context in BeanUtil class under (com.test.model.action) package.

The context is loading fine for (Module-service2) in (com.test.anotherservice.model.action.BeanUtil) and was able to see and data inserted in Consumer table as I can see audit data in ConsumerHistory.
However, Its not working for User and when tried to insert User, I am getting NullPointerException as the context is in (com.test.model.action.BeanUtil) for User is not set.

Hope you understand the issue I'm facing. Also I'm curious about @PostUpdate, wondering if that could help!

7. Hi Naresh, I apologise for long boring messages above. I have finally figured out what the issue was. The package structure need to be same, which is very important. Package structure that I had in the model module for one of the services does not match with the structure of it corresponding module.

However, I came across another issue, I have 2 custom entitymanagers and audting the same entity. On startup, I could see 2 history tables being created. Is there a way where I can write data into single history table from two custom entitymanagers?

1. I am npot sure about your exact application but I am guessing, you are running two entity manager at the same time which means you are running two application at the same time which are connecting to same database. In that case you should use spring.jpa.hibernate.ddl-auto=update in your application.properties file in both application.

8. Thanks for the response Naresh.
I have this(spring.jpa.hibernate.ddl-auto=update) property already set in both applications.

However,
from one application I'm using spring default entitymanager and
from other application I have created custom entityManagerFacoryBean) by creating the below configuration in my class
@EnableJpaRepositories(entityManagerFactoryRef = "customServiceEntityManagerFactory", transactionManagerRef = "customServiceTransactionManager")
@Bean(name = ["customServiceEntityManagerFactory"])
fun customServiceEntityManagerFactory(
builder: EntityManagerFactoryBuilder,
@Qualifier("customServiceDataSource") dataSource: DataSource
): LocalContainerEntityManagerFactoryBean {
return builder
.dataSource(dataSource)
.packages("com.myservice.model")
.persistenceUnit("customservice")
.build()
}
(This code is in Kotlin by the way)

so basically, the above custom entitymanager has the package scan set to com.myservice.model, which is also the same for the other module.

So, whats happening is, I'm audting an entity under that package structure com.myservice.model.user and upon starting both applications, I could see two tables getting created in database for history.

for user entity, it is creating user_history and userhistory tables.

Not sure, why its creating two history tables and how overcome this issue

1. @PP, looks like one entity manager is constructing history table name as user_history and other as userhistory, the later one might be happening with your custom entitymanager.

So you will need to modify the behaviour of our custom entity manager to put underscore in your table names, you can do that by same values to spring.jpa.hibernate.naming.implicit-strategy and spring.jpa.hibernate.naming.physical-strategy properties in both application. In case of cutom entitymanager you will need to set both properties to your entity manager manually.

Or you can put @Table(name = "user_history") on the UserHistory entity class in both application.

9. @Naresh, I'm guessing, your understanding is that both applications are connecting to different tables with same name(having same package structure). Actually, what I'm trying to do is to connect these two modules having
(1 - Spring EntityManager,
2 - Custom EntityManager)
to the same Model, which has Just
- User Entity Class - @Table(name = "user")
- UserHistory Class (After making changes based on your advice, I have set this class - @Table(name . = "user_history")

What happened after the change is,

It created only 1 history table after starting those two modules, which is great! However, I could see additional unwanted columns getting created.

Column Structure is like

Id, modified_date, modified_by, modifieddate, modififedby

So. after starting application with spring entitymanager it created modified_date, modified_by.
After starting second application with custom entitymnanager it created additional columns (modifieddate, modififedby)
When, I tried to edit changes with the application that has custom entitymanager - I got the below error -

Note - I haven't tried setting (spring.jpa.hibernate.naming.implicit-strategy,spring.jpa.hibernate.naming.physical-strategy) properties yet

Caused by: javax.persistence.RollbackException: Error while committing the transaction
at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:77) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:71) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:536) ~[spring-orm-5.0.8.RELEASE.jar:5.0.8.RELEASE]
... 96 common frames omitted
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 2: org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1036) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:338) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:333) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]

10. I have added @Column(name = "modified_by") and @Column(name = "modified_date") to the variables in user_history table. Then when I restarted both the modules(applications). It worked fine, just by creating needed columns (Id, modified_date, modified_by)

However, I stumbled across another issue, Now the below method in BeanUtil that implements ApplicationContextAware is throwing error

public static T getBean(Class beanClass) {
return context.getBean(beanClass);
}

This line return context.getBean(beanClass); is throwing error when I try to get the EntityManager from when tried to update from the application that has customentitymanager.

I tried to change getBean method by sending stringname of my customEntityManager. However, wasn't able to extrat ct EntityManager instance from it.

Below is my code in Kotlin. I tired to convert, which is not working

beanClass - 'customEntityManager' name

fun getBean(beanClass: String): EntityManager {
val response = context!!.getBean(beanClass)
val proxyFactory = ProxyFactory(response)
val proxy = proxyFactory.getProxy()
val lem = proxy as LocalContainerEntityManagerFactoryBean
(throwing error at above line as it couldn't convert to LocalContainerEntityManagerFactoryBean)
println(response)
return context!!.getBean(beanClass) as EntityManager
}

1. It will nice to set spring.jpa.hibernate.naming.implicit-strategy,spring.jpa.hibernate.naming.physical-strategy properties because that will solve your problem without any code change even for future entities.

You have not mentioned which error you are getting while calling return context.getBean(beanClass), but if you have marked customEntityManager  as @Bean` you should not face any problem.

11. If someone is developing a pure JPA solution in a JavaEE application, please do not do this. JBoss 7.2 supports injection the PersistenceContext directly in the Entity listener.

The below has worked beautifully for me.

@PersistenceContext(name = "")
private EntityManager entityManager;

1. @Leonhard Thanks for sharing!