How to inject EntityManager in EntityListeners - java

I need to inject EntityManager in EntityListener class so that I can perform CRUD operation on it.
POJO:
#Entity
#EntityListner(AuditLogging.class)
class User
{
//Getter / setter of properties
}
AuditLogging (Listner class)
public class AuditInterceptor
{
#PersistenceContext
EntityManager entityManager;
public void setEntityManager(EntityManager entityManager)
{
this.entityManager = entityManager;
}
#PrePersist
public void prePersist(Object obj)
{
// Here I want to use ENTITY manager object so that I can perform CRUD operation
// with prePersist coming object.
entityManager.unwrap(Session.class).save(obj);
// But I am getting NULL POINTER EXCEPTION for entity manager object
}
}
JDBC-CONFIg.xml
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
<property name="packagesToScan" value="com.XXXXX.entity" />
<property name="jpaProperties">
</bean>
<!-- Datasource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driver.classname}" />
<property name="jdbcUrl" value="${jdbc.url}" />
</bean>
<!-- transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
EntityListener is not managed by any of the container.EntityListeners are instanciated by JPA, so Spring does not have an opportunity to inject EntityManager.
My question is, how we can inject inject EntityManager in EntityListener class so that I can perform CRUD operation on it ???

I have faced a similar problem where I was trying to create history records for an entity using EntityListeners.
In order to resolve this problem, I have created utility class BeanUtil with a static method to get the bean and used this util class to get bean inside my Entitylistener class
#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 we can call BeanUtil.getBean() to get the bean of any type
public class FileEntityListener {
#PrePersist
public void prePersist(File target) {
perform(target, INSERTED);
}
#Transactional(MANDATORY)
private void perform(File target, Action action) {
EntityManager entityManager = BeanUtil.getBean(EntityManager.class);
entityManager.persist(new FileHistory(target, action));
}
}
We can use this BeanUtil class to get any spring managed bean from anywhere, To know more you can read my article JPA Auditing: Persisting Audit Logs Automatically using EntityListeners.

Anyways, I got this done by getting entityManager reference from EntityManagerFactory bean which is configured in my jdbc-config.xml. But again this is not what I wanted. I wanted to work around with #PersistenceContext.
#Autowired
EntityManagerFactory entityManagerFactory;
private static EntityManager entityManager;
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
entityManager=entityManagerFactory.createEntityManager();
this.entityManagerFactory = entityManagerFactory;
}
Here are few notes that we need to keep in mind:
We can't inject an EntityManager into an EntityListener (through
#PersistenceContext). EntityListener is not managed by any of the
containers
#PersistenceContext class cannot be static. So we cant
attain the instance while class loading.
EntityListeners are
instantiated by JPA, so Spring does not have an opportunity to
inject EntityManager

Well, the first solution which came into my mind is a little "hack", but should work.
public class AuditInterceptor {
static setEntityManager emf;
#Autowired
public void setEntityManagerFactory(EntityManager emf) {
AuditInterceptor.emf = emf;
}
#PrePersist
public void prePersist(Object obj) {
EntityManager entityManager = emf.getEntityManager();
// Here I want to use ENTITY manager object so that I can perform CRUD operation
// with prePersist coming object.
entityManager.unwrap(Session.class).save(obj);
// But I am getting NULL POINTER EXCEPTION for entity manager object
}
}
Inside of your code use EntityManager entityManager = emf.getEntityManager()
Declare your AuditInterceptor as a spring bean (#Component with component-scan or define AuditorInterceptor as a bean)

I used a ThreadLocal to pass the Spring Application Context which contains EntityManager around. Though I am not sure if it is safe Is it safe to pass in the Spring Application Context into a ThreadLocal associated with a request? but so far it is working for me.

The listener can be modified to have autowiring like this. However this needs to be done on on the handlers and not the constructor (doing it on the constructor seems less predictable). You are also not limited to the EntityManager but you have access to the whole context.
#Autowired
private EntityManager entityManager;
#Autowired
private MyDao myDao;
#PrePersist
public void pre() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
Objects.requireNotNull(myDao);
myDao.doSomething();
}

Related

How can I get entityManager in BaseDao in Karaf 4.0.5?

I use karaf 4.0.5 and hibernate 4.2.15 And I want get EntityManager in my BaseDao class.
If I tried get EntityManager in my service
<bean id="subscriberService" class="domain.payment.impl.subscriber.SubscriberServiceImpl" scope="singleton"
init-method="init">
<tx:transaction method="*" />
</bean>
and in class
#PersistenceContext(unitName="payment")
private EntityManager entityManager;
I get EntityManager normaly.
But if I tried it in another class
public class BaseJpaDao<E> implements BaseDao<E>{
protected Class<?> entityClass;
#PersistenceContext(unitName="payment")
private EntityManager entityManager;
public BaseJpaDao(Class<?> entityClass) {
this.entityClass = entityClass;
}
#Override
public E persist(E e) {
entityManager.persist(e);
return e;
}
My entityManager is NULL;
I tried
<bean id="baseDao" class="domain.payment.impl.base.BaseJpaDao" scope="singleton"
init-method="init">
<tx:transaction method="*" />
</bean>
But it not help.
In Spring project it work fine, but in OSGi I have many problems.
really only from the services I can get entityManager?
Have you checked the log ? The BaseJpaDao does not seems to have a public empty constructor, so there should be an error in karaf.log saying the the baseDaobean cannot be created...

Using custom timeout in Spring transaction management

I have several methods with spring #Transactional in my project as following:
#Transactional(value = "sys.tx.mngr", propagation = Propagation.REQUIRES_NEW)
public void addMember(InputParam input)
{
// do somthing...
}
#Transactional(value = "sys.tx.mngr", propagation = Propagation.REQUIRES_NEW)
public void blockMember(InputBlockParam param)
{
// do somthing...
}
Then I set different timeout per method as following:
#Transactional(value = "sys.tx.mngr", propagation = Propagation.REQUIRES_NEW,timeout = 40)
public void addMember(InputParam input)
{
// do somthing...
}
#Transactional(value = "sys.tx.mngr", propagation = Propagation.REQUIRES_NEW, timeout = 20)
public void blockMember(InputBlockParam param)
{
// do somthing...
}
I want in last step set timeout as configurable by a properties file but I don't know what.
Is there any solution for set timeout in spring Transactional annotaion configurable or dynamically?
EDIT:
I define sys.tx.mngr in spring context file as following:
<bean id="sys.tx.mngr" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
</bean>
<tx:annotation-driven transaction-manager="sys.tx.mngr" />
Or is there alternative way for define timeout in spring context file per method?
It can be done on following ways only :-
a) Using Reflection.
b) Using Instrumentation.
c) Using TransactionTemplate (Programatically transaction).
For (a) & (b) you can have your class like following :-
public class Test implements InitializingBean {
#Autowired
private Environment env;
public void afterPropertiesSet() throws Exception {
System.out.println("Sample prop 1 value : "+env.resolvePlaceholders("${prop1.value}"));
//Code to set/modify Transactional annotation "timeout"
// attribute values for all methods
}
}
Link on how to set/modify values can be found here
Modify field annotation value dynamically
Modify a class definition's annotation string parameter at runtime
For (c) you can have config like :-
public class MemberDaoImpl {
#Autowired
private Environment env;
#Autowired
private TransactionTemplate transactionTemplate;
public void addMember(InputParam input) {
transactionTemplate.setTimeout(Integer.parseInt(env.resolvePlaceholders("${addmember.timeout}")));
// do somthing...
}
}
<bean id="memberDao" class="com.xxx.impl.MemberDaoImpl">
<property name="transactionTemplate">
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="sys.tx.mngr" />
</bean>
</property>
</bean>
<bean id="sys.tx.mngr" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
</bean>

Using TransactionProxyFactoryBean with Spring and Hibernate

I have an old project that I'm trying to upgrade to spring 4 / hibernate 4.
We use this approach to configuring our transactions.
That is to say, in XML we define a transaction manager like:
<bean id="abstractTransactionProxy"
abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="hibernateTransactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
and
<bean id="MySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="configLocation" value="WEB-INF/hibernate.cfg.xml" />
</bean>
and then each of our service beans are defined like:
<bean id="customerService" parent="abstractTransactionProxy">
<property name="target" ref="customerTarget"/>
<property name="personDAO" ref="personDAO" />
</bean>
and the DAOs are defined like:
<alias name="personDaoImpl" alias="personDAO"/>
<bean id="personDaoImpl"
class="com.foo.bar.hibernate.PersonDaoImpl">
<property name="sessionFactory" ref="MySessionFactory" />
</bean>
PersonDaoImpl (and all the other DAOs) have many methods that look like this (all of the DAOs extend HibernateDaoSupport):
public List<Person> getPersonByCriteria(final String criteria) {
List<Person> results =
(List<Person>) this.getHibernateTemplate().executeFind(new HibernateCallback<List<Person>>(){
public List<Person> doInHibernate(Session session) throws HibernateException, SQLException {
List<Person> results = (List<Person>) session.createQuery(MYSQLHERE).setString("criteria", criteria).list();
return results;
}
});
return results;
}
Now, the problem I Face at this point is getting rid of our dependency on extending our DAOs from HibernateDaoSupport, and by extension, on using it's HibernateTemplate helper object.
Here's the steps I've taken:
Simply remove them from the DAO methods - don't extend HibernateDaoSupport, remove the HibernateTemplate callback. This looks like:
public List<Person> getPersonByCriteria(final String criteria) {
List<Person> results = (List<Person>) getSessionFactory().getCurrentSession().createQuery(MYSQLHERE).setString("criteria", criteria).list();
return results;
}
Of course this gives me compile-time errors because 'getSessionFactory' was part of the base class 'HibernateDaoSupport' which I just removed.
So I implemented a 'MyHibernateDaoSupport' Class, and had my DAO extend that instead. It looks like:
public class MyHibernateDaoSupport {
private SessionFactory sessionFactory;
protected Session session;
protected static Logger logger = Logger.getLogger(MyHibernateDaoSupport.class);
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
session = this.sessionFactory.getCurrentSession();
}
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
public Session getHibernateTemplate() {
session = this.sessionFactory.getCurrentSession();
return session;
}
public MyHibernateDaoSupport() {
}
}
Now I can build, deploy and run and I hit this error message:
No session available in this thread
How can I make this work?
In simple terms, what was the 'hibernateCallback()', and it's anoymous inner class actually doing?
How can I make my project's transactions work as before, without using HibernateDaoSupport and HibernateTemplate classes?
I've tried tagging my 'CustomerService' (the business logic object that calls 'PersonDao') with #transactional but (A) I see the same error regardless and (B) I'd really prefer to be able to do this programmatically, to reduce the scale of changes to the way the code works now. I also tried putting the transactions at the DAO level instead, and met the same issue.
Apologies if that was way too much detail for the Stack overflow format.
You need to inject the SessionFactory into your DAO classes:
#Repository
public class PersonDAOImpl implements PersonDAO {
#Autowired
private SessionFactory sessionFactory;
public List<Person> getPersonByCriteria(final String criteria) {
List<Person> results = (List<Person>) getSessionFactory().getCurrentSession().createQuery(MYSQLHERE).setString("criteria", criteria).list();
return results;
}
}
Add #Transactional to your service methods
Add HibernateTransactionManager
Instruct Spring to use declarative transactions:
<tx:annotation-driven transaction-manager="transactionManager"/>
Make sure you use LocalSessionFactoryBean for creating the SessionFactory
Here's a detailed step-by-step tutorial for configuring Spring and Hibernate 4

#PersistenceContext is always null

I'm trying to get CDI (with Open Web Beans) working from within a unit test using Delta Spike (#RunWith(CdiTestRunner.class)). Dependency injection is working fine but my EntityManagerFactory is always null:
public class EntityManagerProducer {
#PersistenceContext(unitName = "sbPersistenceUnit")
private EntityManagerFactory emf; //Always null
#Produces
public EntityManager create() {
return emf.createEntityManager();
}
public void close(#Disposes EntityManager em) {
if (em.isOpen()) {
em.close();
}
}
}
I know that my persistence.xml is okay because I can create the Session Factory manually:
EntityManagerFactory test = Persistence.createEntityManagerFactory("sbPersistenceUnit");
and all other injections are working fine. Does anybody know what might be missing?
In an unit-test you aren't in a managed environment.
OpenWebBeans would support it via the openwebbeans-resource module + #PersistenceUnit, but that isn't portable.
So you need to use e.g.:
#Specializes
public class TestEntityManagerProducer extends EntityManagerProducer {
private EntityManagerFactory emf = Persistence.createEntityManagerFactory("...");
#Produces
//...
#Override
protected EntityManager create() {
return emf.createEntityManager();
}
#Override
protected void close(#Disposes EntityManager em) {
if (em.isOpen()) {
em.close();
}
}
}
in the test-classpath
If you ask such questions on their mailing-list, you get answers petty quickly.
You will need to use #PersistenceUnit to inject EntityManagerFactory. #PersistentContext is used for EntityManager injection.
Do you define your entitymanagerFactory as a bean?
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

Will called function be still in transaction if we have a #Transaction in caller function?

I understand we need to keep #Transactional boundaries as short as possible. Here is the code :
I am using userDAO object through Spring dependency injection :
private static ApplicationContext context ;
private UserDAO userDAO;
public TransactionsTest() {
userDAO = (UserDAO) context.getBean("userDAO");
}
I am calling testSaveUserAccounts() from TransactionsTest class trying to use userDAO for insertion/updation of data.
Case 1:
#Transactional
public void testSaveUserAccounts() {
UserAccounts userAccounts = new UserAccounts();
userAccounts.setCommunityId(10L);
userDAO.saveObject(userAccounts);
}
// This method is inside UserDAO
public void saveObject(Object object) {
entityManager.merge(object);
}
Case 2:
#Transactional
public void testSaveUserAccounts() {
UserAccounts userAccounts = new UserAccounts();
userAccounts.setCommunityId(10L);
userDAO.saveObject(userAccounts);
}
// This method is inside UserDAO
#Transactional(propagation=Propagation.REQUIRED)
public void saveObject(Object object) {
entityManager.merge(object);
}
Spring Context :
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourceLocal" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceUnitName" value="spring-jpa" />
</bean>
UserDAO :
#Repository
public class UserDAO extends BaseDAO {
#Transactional(propagation=Propagation.REQUIRED)
public void saveObject(Object object) {
entityManager.merge(object);
}
}
BaseDAO :
public abstract class BaseDAO {
protected EntityManager entityManager;
protected HashMap<String,Long> eventIdMap = new HashMap<String,Long>();
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this. entityManager = entityManager;
}
public <T> T getById(final Class<T> clazz, final Serializable id) {
T object = clazz.cast(entityManager.find(clazz, id));
return object;
}
#Transactional
public Object save(Object ob) {
Object object = entityManager.merge(ob);
return object;
}
#Transactional
public void persist(Object ob) {
entityManager.persist(ob);
}
#SuppressWarnings("unchecked")
public <T> ArrayList<T> getAll(final Class<T> clazz) {
String hqlQuery = "from "+ clazz.getSimpleName();
ArrayList<T> list = (ArrayList<T>)entityManager.createQuery(hqlQuery).getResultList();
return list;
}
}
I have been experimenting around several transactional boundaries REQUIRED, REQUIRES_NEW, SUPPORTS, etc but couldn't confidently make out as to why Case 1 (when method2 is called which is inside transactional boundary of method1) does not merges the data, while, this is solved in Case 2.
Why do I need to specify #Transactional in inner methods as well when already I have marked calling function within Transaction boundary ?
Your transaction test class is not a Spring Bean that is why Case 1 does not work. Spring needs to detected that a method has #Transactional on it and it does that when spring registers the bean with the spring bean factory.
Also keep in mind that the if you are doing Proxy Based AOP calls within the same bean will not be caught by the transaction aspect unless you use AspectJ load time weaving or AspectJ compile time weaving.
Also putting #Transactional on your Dao's is not a really a good idea because transaction boundaries are best marked at the service layer. The reason why is that a particular service method might need to interact with multiple Dao's and you would want those Dao's actions to be part of the tx started by the service layer, rather than having to analyze the Dao's to see what the propagation behavior is.
Can you post the complete code for the test class?
#Transactional does nothing locally, it only has an effect when called from a different service. In other words, you have to leave your current context for transaction annotations to do anything. so calling method 1 is identical in both cases, Case 2 only does anything if method2 is called from another service.

Categories