Spring is unable to inject an EntityManager using #PersistenceContext - java

I have a Java 8/spring 4.3.5.RELEASE web application tha runs on a wildfly 10 server. I use a persistence.xml file. I enabled trace logging on the jboss jpa and hibernate classes and I can see this file gets picked up and is resolved smoothly into a persistence unit:
DEBUG [] [org.hibernate.jpa.internal.util.LogHelper] PersistenceUnitInfo [
name: testcontext
persistence provider classname: org.hibernate.jpa.HibernatePersistenceProvider
classloader: ModuleClassLoader for Module "deployment.BasicWebapp.war:main" from Service Module Loader
excludeUnlistedClasses: false
JTA datasource: org.jboss.as.connector.subsystems.datasources.WildFlyDataSource#fb80232
Non JTA datasource: null
Transaction type: JTA
PU root URL: vfs:/C:/Users/Me/Wildfly 10.0.0/standalone/deployments/BasicWebapp.war/WEB-INF/classes/
Shared Cache Mode: UNSPECIFIED
Validation Mode: AUTO
Jar files URLs []
Managed classes names [
com.company.project.data.User]
Mapping files names []
Properties [
jboss.entity.manager.jndi.name: persistence/testcontext]
I now want a dao class to have an entity manager injected by spring:
UserDao.class
#Repository
public class UserDao
{
#PersistenceContext(unitName = "testcontext")
private EntityManager entityManager;
}
I have component scanning and annotation config so both the #Repository and the #PersistenceContext annotation gets processed upon starting my application:
spring-servlet.xml
<context:component-scan base-package="com.company.project" />
<context:annotation-config/>
<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/testcontext"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>
<tx:annotation-driven/>
The injection fails though, in two possible ways:
If I use #PersistenceContext(unitName = "testcontext"), the error is:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'testcontext' available
If I use #PersistenceContext, the error is:
Caused by: java.lang.NullPointerException
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:580)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:546)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:707)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:680)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354)
... 44 more
So I'm missing something in this configuration to tell the part of Spring that processes the #PersistenceContext annotation to look at either the entityManagerFactory bean that I declared in the spring-servlet.xml or just use the container's persistence unit directly. What do I need to add to achieve that?
I'm also a bit sketchy on the transactionmanager part. Does wildfly provide the transactionmanager or not? If it does, do I need to create a bean for it in Spring (will it pick up the one created by jboss or make its own one?)

I think you should double check you configuration setup against Spring official docs : https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#orm-jpa-setup-jndi .No, the transaction manager is provided by Spring. More info on this: https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#transaction-strategies

Related

How to have more than one persistence units in an JPA 2 application?

My system:
Eclipse Oxygen/JPA 2/JSF 2.2/Hibernate 4/JBoss AS 7
My condition:
I have an application with two persistence units (PU) declared in persistence.xml.
My JBoss error:
Caused by: java.lang.IllegalArgumentException: JBAS011470: Persistence unitName was not specified and there are 2 persistence unit definitions in application deployment "test.war". Either change the application to have only one persistence unit definition or specify the unitName for each reference to a persistence unit.
at org.jboss.as.jpa.container.PersistenceUnitSearch.resolvePersistenceUnitSupplier(PersistenceUnitSearch.java:69)
at org.jboss.as.jpa.processor.JPAAnnotationParseProcessor.getPersistenceUnit(JPAAnnotationParseProcessor.java:284)
at org.jboss.as.jpa.processor.JPAAnnotationParseProcessor.getBindingSource(JPAAnnotationParseProcessor.java:220)
at org.jboss.as.jpa.processor.JPAAnnotationParseProcessor.processField(JPAAnnotationParseProcessor.java:151)
at org.jboss.as.jpa.processor.JPAAnnotationParseProcessor.processPersistenceAnnotations(JPAAnnotationParseProcessor.java:118)
at org.jboss.as.jpa.processor.JPAAnnotationParseProcessor.deploy(JPAAnnotationParseProcessor.java:90)
at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:113) [jboss-as-server-7.1.0.Final.jar:7.1.0.Final]
... 5 more
My problem:
I use a framework that hides all the details on the EntityManager lifecycle. This framework gives me an ancestor class and I build all my code in a subclass, not caring about managing the EntityManager.
This ancestor class does not inject ou annotate the EntityManager, it is created by code when needed for the first time, but the exception above is thrown by JBoss during the application start when I have more than one PU.
I wrote a code in the ancestor to accept the #PersistenceUnit annotation in my subclass and to use the name set in the annotation when creating the EntityManagerFactory. When no annotation is used, the code finds out the first PU name and used it. So, the first PU existing in persistence.xml is understood as a default PU name.
However, even not injecting any EntityManagers, I still have the above exception.
What is missing in my solution?
If you have more than one persistence unit and use #PersistenceContext/#PersistenceUnit annotations, you must specify your unit name for the annotation to be unambiguous.
So instead of:
#PersistenceContext
private EntityManager manager;
you must use:
#PersistenceContext(unitName = "<unit name in persistence.xml>")
private EntityManager manager;
And instead of:
#PersistenceUnit
private EntityManagerFactory managerFactory;
you must use:
#PersistenceUnit(unitName = "<unit name in persistence.xml>")
private EntityManagerFactory managerFactory;
What the error message tells you is that the deployer has found at least one occurrence of #PersistenceContext/#PersistenceUnit without specifying a persistence unit name. That is ambiguous.

Why to I need to exclude from enableAutoConfiguration when I have autowired beans byname

I have 3 DataSource beans
<bean id="dataSource1" name="dataSource1" autowire="byName" .....>
<bean id="dataSource2" name="dataSource2" autowire="byName" .....>
<bean id="dataSource3" name="dataSource3" autowire="byName" .....>
I have set autowire "byName" in the beans in xml
I am creating 3 JdbcTemplates
#Autowired
#Qualifier("dataSource1")
BasicDataSource dataSource1;
#Autowired
#Qualifier("dataSource2")
BasicDataSource dataSource2;
#Autowired
#Qualifier("dataSource3")
BasicDataSource dataSource3;
#Autowired
#Bean(name="jdbcTemplate1")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource1)
}
#Autowired
#Bean(name="jdbcTemplate2")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource2)
}
#Autowired
#Bean(name="jdbcTemplate3")
public JdbcTemplate createJdbcTemplate(){
return new JdbcTemplate((DateSource)dataSource3)
}
StackTrace :
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-11-16 23:50:04.562 ERROR 15180 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
APPLICATION FAILED TO START
Description:
Field dataSource1 in com.MyClass required a single bean, but 3 were found:
- dataSource1: defined in class path resource [datasource.xml]
- dataSource2: defined in class path resource [datasource.xml]
- dataSource3: defined in class path resource [datasource.xml]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
When I add EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) everything works fine.
Can anyone explain why do I need to exclude the AutoConfiguration despite autowiring-byName.
You don't need to ignore the auto-configuration, you just need to mark one of the datasources as #Primary, as auto-configurations might try to get one by type. This leads to the error you have.
Also, feel free to check the dedicated section in the reference documentation about dealing with multiple datasources; there are better ways to configure multiple datasources and still leverage Spring Boot features and infrastructure.

Spring boot: Unit test and Config file

I am doing unit tests for a rest controller, which is only a small part of a bigger application.
My test context isn't recognized by my application and I have the following exception : java.lang.IllegalStateException: Failed to load ApplicationContext
This is my test class:
Test RestController
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(locations = "classpath:/META-INF/spring/context-test.xml")
#WebIntegrationTest
public class MyRestControllerTest extends AbstractTransactionnalTest {
#Autowired
private IManager manager;
#Test
// my unit tests
}
The thing is that if instead of locations = "classpath:/META-INF/spring/context-test.xml" I use classes = Production.class with the following application class, it works fine:
#Configuration
#EnableAutoConfiguration
#EnableTransactionManagement
#EnableScheduling
#ImportResource({ "classpath:/META-INF/spring/context-production.xml" })
public class Production {
// class content
}
I've read all the posts with similar problem and I know it is linked to the #Configuration and #EnableAutoConfiguration annotation however when I tried a custom configuration class which used these annotation and imported the settings from the context.xml it did not work.
I ideally wish not to add any configuration class and would like to only add a bean to my test-context.xml.
Is it possible to solve this issue with a bean in my context.xml or an annotation on TestRestController ?
Here is my stack trace:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
... 26 more
Caused by: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:531)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
... 35 more
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:185)
... 39 more
Here is the bean I used to mock the manager in my test-context.xml :
<bean id="IManager"
class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.service.impl.Manager"/>
Update :
I tried to used a custom manager mock where the database is replaced with a list.
If I remove the annotation #WebIntegrationTest, the application context loads correctly however I get another exception because the server isn't launched without the #WebIntegrationTest annotation.
I/O error on GET request for network address :Connection refused
I am running on spring 1.3.7.
#ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests. Specifically #ContextConfiguration declares the application context resource locations or the annotated classes that will be used to load the context.
#ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
// class body...
}
Spring Boot provides a
#SpringBootTest 
annotation which can be used as an alternative to the standard spring-test
#ContextConfiguration 
annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests via SpringApplication.
You can use the webEnvironment attribute of #SpringBootTest to further refine how your tests will run.
Spring Boot’s #*Test annotations will search for your primary configuration automatically whenever you don’t explicitly define one.
The search algorithm works up from the package that contains the test until it finds a #SpringBootApplication or #SpringBootConfiguration annotated class. As long as you’ve structured your code in a sensible way your main configuration is usually found.
If you want to customize the primary configuration, you can use a nested #TestConfiguration class. Unlike a nested #Configuration class which would be used instead of a your application’s primary configuration, a nested #TestConfiguration class will be used in addition to your application’s primary configuration.
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html 40.2

Spring: run code before a persistence context is loaded

I have spring based multimodule application. And in my DAO module the DB (embedded derby) is started and created by the class the implements ApplicationListener.
Problem that in the logs the huge stacktrace from Spring which say that there is no db(couldn't get connection).
Still, my application works without any problems. This stacktrace appeared before the ApplicationListener invoked and the db is created. Actually, I see it only when I am starting the application the first time on the machine, because the db created only this time, than it just used.
So my question is whow to avoid this exception in logs? Maybe there is spring or hibenate setup not connect to the db before the application context fully loaded? Or invoke the code that creates db by some other listener?
Well here is the way I do : the ROOT context contains the datasource, the dao, the service and the transaction manager. In XML config, the declaration of the database is :
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:url="jdbc:derby:/path/to/database;create=TRUE"
p:username="user" p:password="pwd"
p:driverClassName="org.apache.derby.jdbc.EmbeddedDriver"/>
it can then be used to declare a session factory for hibernate and an associated DAO as :
<bean class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
id="sessionFactory" p:dataSource-ref="datasource">
<!-- hibernate config -->
...
</bean>
<bean class="org.springframework.orm.hibernate4.HibernateTransactionManager"
name="transactionManager" p:sessionFactory-ref="sessionFactory"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="myDao" class="... .myDaoImpl" p:sessionFactory-ref="sessionFactory" .../>
That way all is created by spring, that ensures that the creation order is correct. Of course the same is possible in Java config with the same logic.
I suppose you are fetching some data from database from inside spring beans that are being created. Perhaps thru #PostConstruct or other way. Remember that until spring context is fully loaded some beans can have injected uninitialized beans.
So do not use DB, do not call any DAOs until you are sure that spring context is fully initialized.
To do such initial calls to DAOs try such patter that guarantees spring context completness:
#Component
public class SpringContextMonitor implements ApplicationListener<ApplicationEvent> {
#Autowired
private SomeDao dao;
...
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
onStart((ContextRefreshedEvent) event);
}
}
private void onStart(ContextRefreshedEvent event) {
// do your initialization here
dao.getSomething();
dao2.getSomething();
...
}
...
}
The onStart method in above example is place where you are sure that all beans are fully initialized

Junit/Spring/Hibernate: How to specify an out of container datasource for Hibernate Session in Unit tests

I have an applicationContext.xml which defines a bean called "baseDataSource"
<bean id="baseDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="resourceRef" value="true"/>
<property name="jndiName" value="java:/MySQLDS20"/>
</bean>
Now ordinarily this is created fine within a jboss application using Spring and Hibernate. But when I try to instantiate this IOC container as part of unit testing via
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:WEB-INF/applicationContext.xml"})
I recieve this error:
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:344)
at javax.naming.InitialContext.lookup(InitialContext.java:411)
at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:154)
at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87)
at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152)
at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:178)
at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95)
at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:105)
at org.springframework.jndi.JndiObjectFactoryBean.lookupWithFallback(JndiObjectFactoryBean.java:201)
at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:187)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
I have thoroughly read the discussion here
http://forum.spring.io/forum/spring-projects/data/7448-problem-running-junit-test-with-jndi-datasource
But no answer there seems to either solve the issue or clearly explain what is going on. My question is this:
How do I maker this datasource work? I cannot copy and paste my applicationContext.xml into some testApplicationContext.xml just for the purpose of modifying this one single bean. What can I do to create the container and autowire in my junit tests without changing this bean or duplicating the xml config (my CTO will shoot me)
I cannot copy and paste my applicationContext.xml into some testApplicationContext.xml just for the purpose of modifying this one single bean.
You don't have to. You only have to create a configuration containing only an override for the datasource, then load both in your testcase.
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource>
</bean>
Then in your test case load this together with your actual file and the bean definition will override the one in your actual configuration.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:WEB-INF/applicationContext.xml", "applicationContext-test.xml"})
The latter should only contain beans you want to override/replace.
To inject a custom JNDI context into your unit test you could try the following:
#BeforeClass
public static void setUp() {
DataSource ds = null; // Construct data source manually
ds.setURL("..."); ds.setUser("..."); ds.setPassword("...");
SimpleNamingContextBuilder builder = null;
try {
builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
builder.bind("java:java:/MySQLDS20",ds);
} catch (NamingException e) {
logger.error(e);
}
}
This will expose the required JNDI name through the InitialContext.
But I would recommend you to extract the baseDataSource into a separate configuration file and then use a specific configuration file for your tests instead.
Like this:
In src/main/resources/applicationContext.xml:
<import resource="datasource.jndi.xml" />
JUnit Test Class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({
"classpath*:/datasource.test.xml",
})
public class MyTests
{
...
}
To inject a custom JNDI context into your unit test [Gregor Koukkoullis]
(this is a good method to integrate DataSource into JNDI, but you also need to init DataSource, then inject to JNDI Context)
You only have to create a configuration containing only an override for the datasource [M. Deinum]
(I think If you can define the datasource in new *.xml, so this method is easy to implements)
But David Williams need no copy *.xml, the purpose is very clearly, no change of configration, no change of source code, just test with JNDI.
below is my method:
1. search JNDI env in your remote Server.
2. with setup() method to init your Server JNDI properties.
then no change of configration, no change no test code, no addition datasource config, just add setup method to init remoter Server JNDI.

Categories