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.
hello, I've tried this and got an NullPointerException for the ApplicationContext object (the static one).
ReplyDeleteHi Agustinus, you should not get any problems in it.
DeleteHowever you find the complete code on https://github.com/njnareshjoshi/articles/tree/master/spring-data-jpa-auditing
probably because ApplicationContext is not YET set when you call for EntityManager
DeleteYeah
DeleteIn 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.
ReplyDelete`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.
DeleteFor the Multi module spring boot projects where beanUtil class should be place?
ReplyDeleteIt 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.
DeleteI am getting RuntimeException
DeleteNo EntityManager with actual transaction available for current thread
do you have any idea why?
I am not sure but it seems like some configuration issue, because your entity manager is not intialized yet.
DeleteHi, after trying other solutions which did not work for me;
ReplyDeletethis has worked for me for an entity listener's @PostUpdate method.
Thank you very much.
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.
ReplyDeleteBelow 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
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.
ReplyDeleteThe 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!
Please help me!!
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.
ReplyDeleteHowever, 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?
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.
DeleteThanks for the response Naresh.
ReplyDeleteI 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
@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.
DeleteSo 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.
Please let me know if this solves your problem?
@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
ReplyDelete(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]
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)
ReplyDeleteHowever, 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)
proxyFactory.addInterface(FactoryBean::class.java)
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
}
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.
DeleteYou 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.
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.
ReplyDeleteThe below has worked beautifully for me.
@PersistenceContext(name = "")
private EntityManager entityManager;
@Leonhard Thanks for sharing!
DeleteAnkara
ReplyDeleteVan
Hakkari
Edirne
Yozgat
M13
ankara
ReplyDeletesakarya
tekirdağ
kastamonu
amasya
04ZPV
amasya evden eve nakliyat
ReplyDeleteeskişehir evden eve nakliyat
ardahan evden eve nakliyat
manisa evden eve nakliyat
karaman evden eve nakliyat
LGS0F6
D4F05
ReplyDeleteTekirdağ Parça Eşya Taşıma
Kars Parça Eşya Taşıma
Adıyaman Parça Eşya Taşıma
Adıyaman Evden Eve Nakliyat
Antep Lojistik
75D00
ReplyDeleteDüzce Lojistik
Sinop Evden Eve Nakliyat
Şırnak Evden Eve Nakliyat
Denizli Şehirler Arası Nakliyat
Referans Kimliği Nedir
Isparta Şehirler Arası Nakliyat
Denizli Lojistik
Binance Referans Kodu
Ordu Lojistik
648ED
ReplyDeleteerzurum canlı sohbet et
Adana Telefonda Kadınlarla Sohbet
parasız görüntülü sohbet uygulamaları
ardahan canli goruntulu sohbet siteleri
eskişehir görüntülü sohbet
Kocaeli Sohbet Uygulamaları
canlı sohbet uygulamaları
Diyarbakır Kadınlarla Görüntülü Sohbet
bursa canlı görüntülü sohbet
DFA6D
ReplyDeletegörüntülü sohbet siteleri
telefonda canlı sohbet
istanbul muhabbet sohbet
rize rastgele görüntülü sohbet
hakkari canli sohbet
osmaniye görüntülü sohbet sitesi
urfa ücretsiz sohbet
Erzincan Bedava Sohbet Uygulamaları
bursa sesli sohbet odası
5A1C5
ReplyDeleteYoutube İzlenme Hilesi
Sweat Coin Hangi Borsada
Parasız Görüntülü Sohbet
Bitcoin Üretme Siteleri
Binance Yaş Sınırı
Coin Kazanma Siteleri
Star Atlas Coin Hangi Borsada
Görüntülü Sohbet Parasız
Dlive Takipçi Satın Al
2A956
ReplyDeletepoocoin
satoshi
defillama
dappradar
looksrare
solflare
arbitrum
dextools
poocoin
FA11E
ReplyDeleteen eski kripto borsası
bitexen
paribu
bybit
bitcoin ne zaman çıktı
aax
binance ne demek
bingx
referans kod
04E13
ReplyDeletecanlı sohbet odaları
kizlarla canli sohbet
telegram kripto
kraken
kraken
bitmex
paribu
kraken
papaya
634E8
ReplyDeletekizlarla canli sohbet
kucoin
bitget
kripto para nereden alınır
kraken
ilk kripto borsası
bibox
bybit
bybit
D4B9C
ReplyDelete----
----
----
----
----
----
matadorbet
----
----
0BF6F
ReplyDeletesanal show canlı
D8AD1
ReplyDeletecanlı güvenilir şov
7A99E
ReplyDeletesanal canlı şov
88AED
ReplyDeletegörüntülü şov whatsapp
3CF4D
ReplyDeletegörüntülü şov whatsapp numarası
1468A
ReplyDeletewhatsapp görüntülü şov