Spring + EntityManagerFactory +Hibernate Listeners + Injection - java

i have a simple question. Its possible to add dependency injection via #Ressource or #Autowired to the Hibernate Eventlistener?
I will show you my entitymanagerfactory configuration:
<bean id="entityManagerFactory" class="org.hibernate.ejb.EntityManagerFactoryImpl">
<qualifier value="entityManagerFactory" />
<constructor-arg>
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager">
<bean
class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManagerr">
<property name="defaultDataSource" ref="dataSource" />
</bean>
</property>
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="mis" />
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence" />
<property name="jpaProperties" ref="jpa.properties" />
<property name="jpaDialect" ref="jpaDialect" />
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="database">
<util:constant
static-field="org.springframework.orm.jpa.vendor.Database.POSTGRESQL" />
</property>
<property name="showSql" value="true" />
</bean>
</property>
</bean>
</constructor-arg>
</bean>
At the moment i register my listener via jpa.properties,
hibernate.ejb.event.load=com.example.hibernate.events.LoadEvent
but in this case i have no spring injection in my listener. I found a solution, but this use the sessionFactory and not the entitymanager oder can i modifiy the sessionfactory in my context? Hopefully someone have a nice idea or solutionhow to deal with this problematic!
Big thanks!

If you used SessionFactory, this would be the configuration:
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- Stripped other stuff -->
<property name="eventListeners">
<map>
<entry key="pre-load">
<bean class="com.mycompany.MyCustomHibernateEventListener1" />
</entry>
<entry key="pre-persist">
<bean class="com.mycompany.MyCustomHibernateEventListener2" />
</entry>
</map>
</property>
</bean>
But since you are using JPA, I'm afraid you need to use AOP as outlined in this thread
Or you can
store the ApplicationContext in a ThreadLocal or a custom holder class and expose it through a static method
have a base class for your listeners something like this:
Base class:
public abstract class ListenerBase{
protected void wireMe(){
ApplicationContext ctx = ContextHelper.getCurrentApplicationContext();
ctx.getAutowireCapableBeanFactory().autowireBean(this);
}
}
Now in your lifycycle methods call wireMe() first.
Update:
Here is a sample implementation of ContextHelper:
public final class ContextHelper implements ApplicationContextAware{
private static final ContextHelper INSTANCE = new ContextHelper();
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(final ApplicationContext applicationContext){
this.applicationContext = applicationContext;
}
public static ApplicationContext getCurrentApplicationContext(){
return INSTANCE.applicationContext;
};
public static ContextHelper getInstance(){
return INSTANCE;
}
private ContextHelper(){
}
}
Wire it in your Spring Bean configuration like this:
<bean class="com.mycompany.ContextHelper" factory-method="getInstance" />

Related

Unable to Inject DAO class in Spring BO [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 7 years ago.
I am unable to inject my DAO class in Spring MVC:
This is my DAO class:
package com.pankaj.bookslibrary.dao;
#Component
public class BooksLibraryDAO
{
#PersistenceContext
private EntityManager em;
public void saveBook(Book book)
{
em.persist(book);
}
}
This is my BO class which calls DAO:
package com.pankaj.bookslibrary.controller;
#Service
public class BooksLibraryBO
{
#Autowired
private BooksLibraryDAO booksLibraryDAO;
public void saveBook(Book book)
{
booksLibraryDAO.saveBook(book);
}
The above line gives NullPointerException as booksLibraryDAO is null.
Here are the relevant lines from my dispatcherServlet config file:
<beans xmlns=...3.0.xsd">
<context:component-scan base-package="com.pankaj.bookslibrary" />
<context:annotation-config/>
<bean id="dataSource"....</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="persistenceUnitName" value="BooksLibrary_PersistenceUnit" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="showSql" value="false" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
<bean id="transactionManagerNonJTA" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="defaultTimeout" value="1800"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManagerNonJTA" />
I am not sure what I have missed.
This is how I am making a call from the controller:
BooksLibraryBO bo = new BooksLibraryBO();
bo.saveBook(book);
The packages the classes are in differs from the package you scan for annotations. Add the packages to the list of base-packages to scan for!
As M.Deinum explained, you are creating the BooksLibraryBO yourself, spring do not know that instances and will not process the annotations.

Mybatis with Spring NullPointerException Autowired field

I'm trying to integrate Mybatis with spring. Here you can see my application context of Spring
<context:annotation-config />
<context:component-scan base-package="com" />
<tx:annotation-driven />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/DB" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/com/mybatis/mybatis-config.xml" />
<!--<property name="transactionFactory" ref="springManagedTransactionFactory" />-->
</bean>
<!--
<bean id="springManagedTransactionFactory" class="org.mybatis.spring.transaction.SpringManagedTransactionFactory">
<constructor-arg index="0" ref="dataSource" />
</bean> -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="registroClimaMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.mybatis.dao.RegistroClimaMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
First of all I have commented springManagedTransactionFactory in XML because it's giving me an exception
Error creating bean with name 'springManagedTransactionFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
Here is my interface RegistroClimaMapper. I do not have any annotation here because I have defined the context:component scan.
public interface RegistroClimaMapper {
void insertarRegistroClima(RegistroClima registro) throws SQLException;
List<RegistroClima> getRegistrosClima() throws SQLException;
List<RegistroClima> getRegistrosClima(#Param("Validado") boolean Validado) throws SQLException;
}
I try to use this interface in a ManagedBean and when I'm going to use registroClimaPersistence inside a method of TablaRegistroClimaBean I get a NullPointerException
#ManagedBean(name = "tablaRegClimaBean")
#ViewScoped
public class TablaRegistroClimaBean implements Serializable {
#Autowired
private RegistroClimaMapper registroClimaPersistence;
public void setRegistroClimaPersistence(RegistroClimaMapper registroClimaPersistence) {
this.registroClimaPersistence = registroClimaPersistence;
}
}
As seen here: http://javadox.com/org.mybatis/mybatis-spring/1.1.1/org/mybatis/spring/transaction/SpringManagedTransactionFactory.html#SpringManagedTransactionFactory%28%29, SpringManagedTransactionFactory has a default constructor, but none that takes a datasource. You need to pass the datasource to the newTransaction() method, but not to the constructor: http://javadox.com/org.mybatis/mybatis-spring/1.1.1/org/mybatis/spring/transaction/SpringManagedTransactionFactory.html#newTransaction(javax.sql.DataSource,%20org.apache.ibatis.session.TransactionIsolationLevel,%20boolean)
Change the piece of code to
<bean id="springManagedTransactionFactory" class="org.mybatis.spring.transaction.SpringManagedTransactionFactory">
<!--<constructor-arg index="0" ref="dataSource" />-->
</bean>
or completely remove the constructor-arg.
What NullPointerException do you get? It might come from you just defining the RegistroClimaMapper interface, but not implementing this interface in any bean. Please add a Bean that implements this interface and the Autowired annotation should work.

#Transactional in unit test prevents #PostLoad from being invoked

I have the following test:
public class Book
{
public boolean postLoadInvoked;
#PostLoad
private void onPostLoad()
{
postLoadInvoked = true;
}
}
public class MyIntegrationTest extends AbstractIntegrationTest
{
#Autowired
private BookDAO bookDAO;
#Test
public void loadBooks()
{
Book book = bookDAO.findOne(...);
assertTrue(book.postLoadInvoked);
}
}
This test passes as-is, but if I add the #Transactional annotations to the test class, it fails:
#Transactional
#TransactionConfiguration(defaultRollback=true)
public class MyIntegrationTest extends AbstractIntegrationTest
Why does configuring the test with #Transactional affect the JPA callback methods?
EDIT
The DAO is just a Spring Data repository, so has no logic:
public interface BookDAO extends
JpaRepository<Book, Long>,
QueryDslPredicateExecutor<Book> {}
The transaction manager also has a standard configuration:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>com.mangofactory.example</value>
</list>
</property>
<property name="persistenceUnitName" value="spring-test" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
</bean>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" />
</bean>
I'm not sure what the spring version is that you are using or if AbstractIntegrationTest is extending something, but one thing to check is that in your hierarchy your tests should extend some abstract transactional context aware spring test class.
The classes that I'm thinking about:
org.springframework.test.AbstractTransactionalSpringContextTests
org.springframework.test.AbstractTransactionalDataSourceSpringContextTests
or some jUnit flavor (depending on the version that you are using):
org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
org.springframework.test.context.junit38.AbstractTransactionalJUnit38SpringContextTests
I could think of two reasons:
#PostLoad is only executed after the transaction is committed. Unlikely.
How did you enable transactions? Did you specify proxyTargetClass = true? Otherwise, you can get weird behavior.

Spring 3 injecting #Required field as NULL

I am having some trouble with setting up my Spring environment properly. In my applicationContext.xml I have:
...
<context:annotation-config />
<context:component-scan base-package="com.company.server" />
<import resource="databaseConfig.xml" />
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config.properties</value>
</list>
</property>
</bean>
and in my databaseConfig:
<tx:annotation-driven />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass">
<value>${jdbc.driver.className}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc.url}</value>
</property>
<property name="user">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="packagesToScan" value="org.adit.spring.hibernate.entity" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
Now my problem starts. If you look at the following class:
#Service
public class ViewContactsServiceImpl extends RemoteServiceServlet implements ViewContactsService {
private ContactDao contactDao;
#Autowired
public void setContactDao(ContactDao contactDao) {
this.contactDao = contactDao;
}
#Override
public ArrayList<Contact> getAllContacts() {
return contactDao.getAllContacts();
}
}
During application startup everything is fine. Spring does not complain that it cannot create the bean or that it cannot inject the property. However, whenever I try to access the contactDao field, it is null.
Thanks!
UPDATE
I should also mention my ContactDaoImpl.java is defined as:
#Repository("contactDao")
#Transactional
public class ContactDaoImpl implements ContactDao { ... }
UPDATE 2
NB. This is a GWT application.
ViewContactServiceImpl.java:
package com.company.server.service.viewcontacts;
ViewContactsService.java:
package com.company.client.viewcontacts;
ContactDaoImpl.java
package com.company.server.contact;
ContactDao.java
package com.company.server.contact;
I thins ViewContactsServiceImpl is being instantiated by GWT (guessing based on RemoteServiceServlet) - so it is not spring managed bean.
You need to invoked auto-wire manually by overriding and implementing the init method. Similar to the code shown below (from this article). As explained in that article create an AbstractRemoteServlet that all your GWTService can extend.
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
WebApplicationContext ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(config.getServletContext());
AutowireCapableBeanFactory beanFactory = ctx
.getAutowireCapableBeanFactory();
beanFactory.autowireBean(this);
}
Look at GWT-SL library for another approach to exposing spring managed beans as GWT remote services.

Spring, multiple Hibernate Sessionfactories configuration

I need to set up multiple Sessionfactories in my app, now I'm facing a problem. I can't use the 2nd level cache at the moment because only the cache from the first factory is returned. Providing a hibernate.cache.region_prefix would solve the problem I guess. How can I supply for each factory a own region per Spring XML config?
applicationContext.xml
<bean id="hibernateProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>/WEB-INF/hibernate/hibernate.properties</value>
</list>
</property>
</bean>
<bean id="cacheRegionFactory" class="org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge">
<constructor-arg>
<props>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
</props>
</constructor-arg>
</bean>
<bean id="factory_1"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="db2Datasource" />
<property name="mappingDirectoryLocations" value="classpath:de/ac/hibernate" />
<property name="hibernateProperties" ref="hibernateProperties" />
<property name="cacheRegionFactory" ref="cacheRegionFactory" />
</bean>
<bean id="factory_2"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="db2Datasource2" />
<property name="mappingDirectoryLocations" value="classpath:de/ac/hibernate" />
<property name="hibernateProperties" ref="hibernateProperties" />
<property name="cacheRegionFactory" ref="cacheRegionFactory" />
</bean>
hibernate.properties
hibernate.dialect=org.hibernate.dialect.DB2400Dialect
hibernate.connection.autocommit=false
hibernate.connection.charset=UTF-8
hibernate.show_sql=false
hibernate.cache.use_second_level_cache=false
hibernate.generate_statistics=false
I don't want to provide the properties per sessionfactory is this even possible? I'm using Spring 3.0.2, Hibernate 3.5
Ok hibernate.cache.region_prefix fixes my problem. I solved the problem by a workaround.
LocalSessionFactoryBeanMod.class
public class LocalSessionFactoryBeanMod extends LocalSessionFactoryBean {
private String cacheRegion;
public String getCacheRegion() {
return this.cacheRegion;
}
public void setCacheRegion(String cacheRegion) {
this.cacheRegion = cacheRegion;
getHibernateProperties().put("hibernate.cache.region_prefix", cacheRegion);
}
#Override
public void setHibernateProperties(Properties hibernateProperties) {
if (getHibernateProperties().isEmpty()) {
super.setHibernateProperties(hibernateProperties);
} else {
getHibernateProperties().putAll(hibernateProperties);
}
}
}
applicationContext.xml
<bean id="factory_1"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="db2Datasource" />
<property name="mappingDirectoryLocations" value="classpath:de/ac/hibernate" />
<property name="hibernateProperties" ref="hibernateProperties" />
<property name="cacheRegionFactory" ref="cacheRegionFactory" />
<property name="cacheRegion" value="ip_10_5_14_5_" />
</bean>
Sure it isn't a really clean or generic solution but fits my needs for now. Probably someone else can provide a much smoother solution.
Did you try to use net.sf.ehcache.hibernate.SingletonEhCacheProvider as cache provider as its sole purpose is to support multiple session factories and use singleton cache.

Categories

Resources