I am trying to sort out the following issue.
I've created a Spring Boot application which is also using Spring Data.
To do integration tests, I want to power up an H2 database.
I configured the test with #ContextConfiguration and referenced my applicationContext.xml file.
When I am running the tests out of the IDE (intelliJ) everything is fine, and the test is getting grean.
But as soon as I run the test on my build server or in the console with gradle, I get NoSuchBeanDefinitionException. It seems like the applicationContext is not considered at all...
Actually I have no clue anymore what to do..
My applicationContext-test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:data="http://www.springframework.org/schema/data/jpa"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="de.company.project"/>
<bean class=
"org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean class=
"org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${database.driverClassName}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence-
test.xml"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect"/>
</bean>
</property>
</bean>
</beans>
My persistenc-test.xml:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit"
transaction-type="RESOURCE_LOCAL">
<class>de.company.project.server.model.Entity</class>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.ejb.naming_strategy"
value="org.hibernate.cfg.ImprovedNamingStrategy" />
<property name="hibernate.connection.charSet" value="UTF-8" />
<property name="hibernate.default_schema" value="public" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
The database.properties just contains the defaults for an H2 database.
And last but not least my Testclass
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:*META-INF/applicationContext-test.xml")
public class EntityServiceTest {
#Test
public void doSomeStuff(){
}
.
.
}
I hope somebody has a clue what I am doing wrong?!
BR
Edit:
Missed to add the exception and the respository:
#Repository
public interface EntityRepository extends JpaRepository<Entity, Integer> {
}
Exception:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying
bean of type [de.company.project.server.dao.EntityRepository] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I fixed it by adding TransactionalTestExecutionListener.class to my TestExecutionListeners.
I think the #Repository annotation should be on the implementation and not the interface. I assume you have this interface #Autowired somewhere else. Spring needs to know which concrete class should be used.
Also, the config file may not be found on the classpath with unit tests. Place the test context config in src/test/resources and change the configuration annotation to:
#ContextConfiguration(locations = "classpath:/applicationContext-test.xml")
See this answer (and others for the same question) for more info: https://stackoverflow.com/a/4377840/3851006
Related
I have a fully working Spring 4 + Hibernate application. Hibernate is configured via <persistence-unit>... My unit test does all DB-related work without a problem. But now I want to access Hibernate session; to do that I should inject SessionFactory. How to inject it considering the following configuration?
I know I could define a bean like LocalSessionFactoryBean but don't know how to configure it. (I want no duplicated aconfiguration also.)
MyTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:META-INF/spring/test-context.xml" })
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class MyTest {
#Inject
SessionFactory sessionFactory; // want it injected
}
test-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<context:annotation-config/>
<context:component-scan base-package="com.mycompany"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="hibernateJpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
<property name="persistenceUnitName" value="my-persistence-unit"/>
</bean>
</beans>
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="..." version="2.0">
<persistence-unit name="my-persistence-unit" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/datasources/my</jta-data-source>
<class>com.mycompany.entity.Document</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="false"/>
<property name="hibernate.archive.autodetection" value="class, hbm"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
</properties>
</persistence-unit>
</persistence>
Because I use JPA I should inject EntityManager, not SessionFactory. This solved my task finally.
#PersistenceContext
private EntityManager entityManager;
...
entityManager.refresh(obj); // working with H-session
...
This is exactly by the #m-deinum's comment; if he will add his own answer then I'll mark it as decision.
An exception occurs while I'm trying to persist an object.
import org.springframework.transaction.annotation.Transactional;
#Repository("DBRepository")
#Transactional
public class DBRepository implements Repository {
#PersistenceContext
private EntityManager entityManager;
#Override
public void addElement(Element element) {
logger.debug("Element {} is going to be added to database", element);
entityManager.persist(element);
}
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="p_unit" transaction-type="RESOURCE_LOCAL"/>
</persistence>
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/jpa-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
jpa-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<!-- Using annotation to configure application -->
<context:annotation-config/>
<context:component-scan base-package="web.tmh"/>
<!-- Tells Spring to use persistence context annotation -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<tx:annotation-driven mode="proxy" transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="p_unit"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
</bean>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<entry key="hibernate.hbm2ddl.auto" value="update"/>
</map>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- autoReconnect=true param allows our application to reconnect to base if connection was lost -->
<property name="url" value="jdbc:mysql://localhost:3306/TellMeHow?autoReconnect=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
So as you can see I used right Transactional annotation, and have <tx:annotation-driven mode="proxy" transaction-manager="transactionManager"/>in my jpa-config.xml. So I don't really understand why it doesn't want do work
Ok, the problem was solved. I've made a small mistake in my dispatcher-config.xml:
So instead of
<mvc:annotation-driven/>
<context:component-scan base-package="web.tmh"/>
I had to write
<mvc:annotation-driven/>
<context:component-scan base-package="web.tmh.controller"/>
I thought that component-scan worked recursively, but seems like it didn't...
Well, my doubt is very simple but strange for me, because in everywhere i hear: "You must declare all persistent classes in persistent-unit".
I decide don't declare any class in my persistent-unit, just put the default configuration, and my application works fine even now. So, why declare my classes there ?
NOTE:
I don't know if this problem is related with the scneario above, but when i try to load a lazy attribute all fields in this object are NULL and have in attribute "handler" something like: JavaLazyInitializer.
EDIT 1:
This is my persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd ">
<persistence-unit name="odontonewPU">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="false" />
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
</persistence>
This is my applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- Seta anotaçoes para serem usadas pelo Spring -->
<context:annotation-config />
<!-- Define o pacote onde o Spring vai procurar por beans anotados -->
<context:component-scan
base-package="br.com.odontonew" />
<!-- define que as transaçoes irao ser anotadas -->
<tx:annotation-driven proxy-target-class="true" />
<!-- Configuracao do Banco de Dados -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/odontonew" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<!-- Configuracao do Hibernate -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="odontonewPU" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<!-- Configuracao do gerente de transacoes do Spring -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
</beans>
You do not have to declare your persistent classes in your persistence.xml, at least not with JPA 2.0. I am not sure about JPA 1.
The provider scans the classes in the persistence root and evaluates the annotations (#Entity, #MappedSuperclass etc) as well as the contents of persistence.xml, so in the end you get a union of both declarations.
From the Pro JPA 2 book:
A managed class will be included if it is among the following:
local classes: annotated classes in the deployment unit in which the persistence.xml was packaged.
classes in mapping files: classes having mapping entries in an XML mapping file
explicitly listed classes: classes that are listed as class elements in the persistence.xml
additional jars of managed classes : annotated classes in a named jar listed in a jar-file element of the persistence.xml file
I'm in trouble here..
I'm new using Spring + Hibernate in a Java SE application...
I'm trying to instantiate the entityManager, but it's not working
I'm using the annotation #PersistenceUnit, like this:
#PersistenceUnit
public void setEmf(EntityManager emf) {
this.emf = emf;
}
And it works "fine", but it doesn't persist =/
When I change to
#PersistenceContext
public void setEmf(EntityManager emf) {
this.emf = emf;
}
It came out the following error:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'daoAbstract' defined in class path resource [applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type '$Proxy10 implementing org.hibernate.ejb.HibernateEntityManagerFactory,org.springframework.orm.jpa.EntityManagerFactoryInfo' to required type 'javax.persistence.EntityManager' for property 'emf'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [$Proxy10 implementing org.hibernate.ejb.HibernateEntityManagerFactory,org.springframework.orm.jpa.EntityManagerFactoryInfo] to required type [javax.persistence.EntityManager] for property 'emf': no matching editors or conversion strategy found
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:532)
Here's my applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="ConfiguradorDePropriedades"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<description>The service properties file</description>
<property name="location" value="file:AppConfig.properties" />
</bean>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- <property name="persistenceUnitName" value="ChatJpa" /> -->
<property name="persistenceXmlLocation" value="classpath:persistence.xml" />
<property name="dataSource" ref="dataSourceLocal" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="${database}" />
<property name="showSql" value="false" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="dataSourceLocal"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.pass}" />
</bean>
<bean id="daoAbstract" class="com.planner.pvc.dao.DaoAbstract">
<property name="emf" ref="entityManagerFactory" />
</bean>
<bean id="clienteDao" class="com.planner.pvc.dao.ClienteDaoImpl">
</bean>
<bean id="pvcMainController" class="com.planner.pvc.controller.PVCMainController">
<property name="dao" ref="clienteDao" />
</bean>
</beans>
And persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="ChatJpa">
<properties>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.connection.driver_class" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<!-- <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.EhCacheProvider" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" /> -->
</properties>
</persistence-unit>
</persistence>
Any help would be appreciated.
Thanks
This line is the problem:
<bean id="daoAbstract" class="com.planner.pvc.dao.DaoAbstract">
<property name="emf" ref="entityManagerFactory" />
</bean>
Instead of doing this, let Spring inject the emf field annotated with #PersistenceContext, this will happen if you have a <context:component-scan.../> or <context:annotation-config/> tags in your application context or just add the latter.
According to its JavaDoc, PersistenceAnnotationBeanPostProcessor seems to be responsible for injecting the EntityManager with the annotation #PersistenceContext. It appears to imply without this bean declared in the Spring application context xml, the #PersistenceContext annotation won't work.
However, based on my experiments, this is not the truth.
Persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL" />
</persistence>
Spring application context XML
<context:component-scan base-package="com.test.dao" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="default"/>
<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.DerbyDialect"/>
</bean>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="url" value="jdbc:derby://localhost:1527/c:\derbydb\mydb"/>
<property name="username" value="APP"/>
<property name="password" value="APP"/>
</bean>
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!--
<bean id="persistenceAnnotation" class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
-->
UserDaoImpl
#Repository("userDao")
public class UserDaoImpl implements UserDao {
#PersistenceContext
protected EntityManager entityManager;
#Transactional
public void save(User user) {
entityManager.persist(user);
}
}
Whether I comment or uncomment the persistenceAnnotation bean, the result is the same. It doesn't hurt to leave the bean around, but what's the use of this bean?
I am using Spring 3.0.5.
Could someone provide a scenario where taking out this bean will result in failure?
Also I am not fond of creating an empty persistence unit just to fool Spring. Luckily this problem has been addressed in Spring 3.1.0.
The PersistenceAnnotationBeanPostProcessor transparently activated by the <context:component-scan /> element. To be precise it's the <context:annotation-config /> element that activates the bean but this element in turn gets transparently activated by <context:component-scan />.
As Oliver Gierke mentioned, org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor is automatically loaded into App Context by Spring when using annotation based configuration. One of its duties is to search the proper entity EntityManagerFactory that would provide the EntityManager for you #PersistenceContext annotated properties.
If you have multiple EntityManagerFactory beans in you spring config/context and you have #PersistenceContext annotations without a unitName attribute (lets say you are using a framework that comes with such a bean, and you can't touch framework code), you may run into this exception: org.springframework.beans.factory.NoUniqueBeanDefinitionException.
I found this workaround in case you tun into this:
<bean id="org.springframework.context.annotation.internalPersistenceAnnotationProcessor"
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" >
<property name="defaultPersistenceUnitName" value="entityManagerFactory"/>
</bean>
This would override the default PersistenceAnnotationBeanPostProcessor loaded by Spring with a new one with defaultPersistenceUnitName.