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.
Related
Here's my entity:
#Entity
public class Answer implements Serializable {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user")
private CoreUsers user;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person")
private Person person;
}
the service:
#Transactional
public class AnswerService {
#Autowired
private AnswerRepository repository;
public List<Answer> getAnswers(Long id) {
return repository.findByMessagesidOrderBySenddateDesc(id);
}
}
the repo:
#Repository
public interface AnswerRepository extends JpaRepository<Answer, Long> {
List<Answer> findByMessagesIdOrderBySenddateDesc(Long id);
}
the applicationContext.xml
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<entry key="hibernate.enable_lazy_load_no_trans" value="true" />
<entry key="hibernate.current_session_context_class" value="thread" />
<entry key="org.hibernate.cacheable" value="false" />
</map>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="showSql" value="false" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
</bean>
</property>
<property name="persistenceUnitName" value="cccPersistenceUnit" />
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
I load a collection of Answer (List) in the JSF bean through my service class. When I try to obtain a Person object from the collection — everything is okay, but when I try to obtain a User object from the collection — hibernate throws LazyInitializationException. What am I doing wrong?
Am I right, that hibernate tries to obtain the user object out of hibernate session?
Pls feel free to ask any addition information.
Thanks for any helps!
before create the thread, I googled many times
UPD.
I set the entities up to Eager loading, and noticed that my app tries to load unexist ID from user table. That was the problem :)
You can get your complete objects tree with :
import org.springframework.transaction.annotation.Transactional;
#Transactional
public List<Answer> getAnswers(Long id) {
return repository.findByMessagesidOrderBySenddateDesc(id);
}
I have a controller where I autowire repository:
#Controller
#RequestMapping("/account")
#EnableJpaRepositories
public class AccountController {
#Autowired
private AccountRepository accountRepo;
//methods
}
My repository extends CrudRepository
#Repository
public interface AccountRepository extends CrudRepository<Account, Integer> {
Account findOne(int primaryKey);
}
I use xml to configure my project. Here it is:
<jpa:repositories base-package="com.library.repositories"
entity-manager-factory-ref="entityManager"></jpa:repositories>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManager" />
</bean>
<bean id="entityManager"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.library.entities" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<!-- <prop key="hibernate.hbm2ddl.auto">create-drop</prop> -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
</bean>
<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/library" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
Why it doesn't work? The error I receive is that AccountController cannot autowire bean AccountRepository.
EDIT
I've refactored my configuration to annotation based and everything works. In my XML version I probably didn't scan some classes and it resulted with error.
May be you need to have #EnableJpaRepositories annotation on a configuration class with #Configuration annotation instead of Controller class.
Also, make sure your Configuration classes are under a scanned package.
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.
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.
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" />