Spring 3.2 + Hibernate 4.3.4 + jUnit : EntityManagerFactory is closed - java

I'm writing some junit tests for my Spring 3.2.8 + Hibernate 4.3.4 application. My test class is annotated with:
#DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
#Transactional
public class UserServiceImplTest {
...
...
}
However, I get the following error thrown by an aspect that is trying to use the entity manager:
org.springframework.dao.InvalidDataAccessApiUsageException: EntityManagerFactory is closed; nested exception is java.lang.IllegalStateException: EntityManagerFactory is closed
If I look at the logs, I see the following error message at the start of my second test:
2014-04-02 16:36:48,891 [main] WARN org.hibernate.jpa.internal.EntityManagerFactoryRegistry - HHH000436: Entity manager factory name (default) is already registered. If entity manager will be clustered or passivated, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'
I realize that it is just a warning, but I am wondering if this is part of the issue. I do not understand why it is complaining that the factory is already registered when the #DirtiesContext is supposed to ensure that the context is cleaned out before starting the second test.
I do see the context being cleaned up at the end of my first test:
2014-04-02 16:36:10,733 [main] INFO springframework.test.context.transaction.TransactionalTestExecutionListener - Rolled back transaction after test execution for test context [TestContext#1ff221ad testClass = UserServiceImplTest, testInstance = com.ia.service.UserServiceImplTest#5180f53d, testMethod = testUpdateRoles#UserServiceImplTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration#6964a198 testClass = UserServiceImplTest, locations = '{classpath:META-INF/spring/applicationContext*.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{test}', resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.test.context.web.WebDelegatingSmartContextLoader', parent = [null]]]
2014-04-02 16:36:10,735 [main] INFO springframework.web.context.support.GenericWebApplicationContext - Closing org.springframework.web.context.support.GenericWebApplicationContext#53c3dcdc: startup date [Wed Apr 02 16:35:00 EDT 2014]; root of context hierarchy
2014-04-02 16:36:10,736 [main] INFO springframework.beans.factory.support.DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#2ff0a910: defining beans [dataSource,entityManagerFactory,org.springframework.transaction.config.internalTransactionAspect,hibernateJpaVendorAdapter,transactionManager,org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor#0,org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor#0,org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor#0,rateRepository,userRepositoryImpl,userRepository,patchRepository,org.springframework.security.filterChains,org.springframework.security.filterChainProxy,org.springframework.security.web.DefaultSecurityFilterChain#0,org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler#0,org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource#0,org.springframework.security.access.vote.AffirmativeBased#0,org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor#0,org.springframework.security.methodSecurityMetadataSourceAdvisor,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.security.web.PortMapperImpl#0,org.springframework.security.web.PortResolverImpl#0,org.springframework.security.config.authentication.AuthenticationManagerFactoryBean#0,org.springframework.security.authentication.ProviderManager#0,org.springframework.security.web.context.NullSecurityContextRepository#0,org.springframework.security.web.savedrequest.NullRequestCache#0,org.springframework.security.access.vote.AffirmativeBased#1,org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0,org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator#0,org.springframework.security.authentication.AnonymousAuthenticationProvider#0,org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint#0,org.springframework.security.userDetailsServiceFactory,org.springframework.security.web.DefaultSecurityFilterChain#1,org.springframework.security.authentication.dao.DaoAuthenticationProvider#0,org.springframework.security.authentication.DefaultAuthenticationEventPublisher#0,org.springframework.security.authenticationManager,basicAuthenticationFilter,authenticationEntryPoint,defaultWebSecurityExpressionHandler,org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0,org.springframework.context.config.internalBeanConfigurerAspect,contactDataOnDemand,userDataOnDemand,testBase.BeanRegistry,securityService,patchServiceImpl,rateServiceImpl,userServiceImpl,appConfig,viewConfig,webContextFilter,patchEngineContextListener,patchEngineImpl.PatchEngineInitializer,patchEngineImpl,initializeUsersRolesAndPermissionsPatch,tilesContainerFactory,viewMetaPreparer,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,applicationProperties,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,org.springframework.data.auditing.AuditingHandler#0,org.springframework.data.jpa.domain.support.AuditingEntityListener,org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor,org.springframework.data.auditing.AuditingHandler#1,auditorProvider,objectMapper,org.springframework.orm.jpa.SharedEntityManagerCreator#0]; root of factory hierarchy
2014-04-02 16:36:10,754 [main] INFO org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
so I am not sure why it is complaining that the EntityManagerFactory is already registered. Furthermore, I am not sure why the EMF is closed when I am trying to access it.
Do I need to do something special to ensure that it is still accessible in each test? I did find this post on SO that relates to the issue, but doesn't help me nail down why this is happening.

It turns out after much debugging that the problem is in the aspect that is using the EntityManagerFactory. Given that the aspect is instantiated outside the Spring context, when the context is refreshed, the aspect's references are not updated. Consequently, the aspect is holding a reference to an EMF that is indeed closed - the original EMF from the first unit test. Now I just have to figure out how to get Spring to update/refresh the references when the context is updated.

Seems that something try to get a persistence context before the spring context was set up or after it was disposed.
Don't you have some junit #before or #after annotated method which accesses the persistence ?

Related

Spring opens a new transaction for each JpaRepository method that is called within an #Transactional annotated method

I have a method that is annotated with #Transactional. That should mean that any database queries that are fired within this method should all use the same transaction. But in reality that doesn't happen. What does happen is that a transaction is opened for the method itself but then when the first JpaRepository method is called a new transaction is opened for that particular method call.
To make matters more complex, For custom repository methods this new transaction is only opened when the JpaRepository or the JpaRepository custom method is annotated with #Transactional as well.
If not i get the following trace log statement about it:
No need to create transaction for
[org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]:
This method is not transactional.
So it doesn't create a new transaction but it also doesn't seem to use the transaction created by the calling method either.
Heres the repository class:
#Repository
public interface LanguageDao extends JpaRepository<Language, Long> {
#Transactional
public Language findByLanguageCode(String languageCode);
public Language findByIdNotNull();
}
Heres the method that uses different repository methods.
#Transactional
public void afterSingletonsInstantiated() {
languageDao.findByLanguageCode(); //This custom method opens a new transaction, but only because i've annotated this method with #Transactional as well.
languageDao.findAll(); //This one as well because its a standard JpaRepository method.
languageDao.findByIdNotNull();//This custom method doesn't because it lacks its own #Transactional annotation.
}
Heres the #Configuration file, with transaction management and jpa repositories enabled
#EnableJpaRepositories(basePackages={"DAOs"}, transactionManagerRef = "customTransactionManager", enableDefaultTransactions = true)
#EnableTransactionManagement
#Configuration
public class RootConfig implements InitializingBean {
#Bean(name = "customTransactionManager")
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
if (shouldCreateInitialLuceneIndex) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
createInitialLuceneIndex(entityManager);
entityManager.close();
}
return transactionManager;
}
}
Relevant application.properties settings
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database-platform = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.open-in-view = false
A bit of actual logs. The first line shows that a transaction for the method afterSingletonsInstantiated is created.
[TRACE] 2021-11-08 15:32:40.811 [main] TransactionInterceptor - Getting transaction for [config.StartupChecks$$EnhancerBySpringCGLIB$$134b7631.afterSingletonsInstantiated]
[INFO ] 2021-11-08 15:32:40.815 [main] StartupChecks - Calling sequence table reset procedure
[DEBUG] 2021-11-08 15:32:40.833 [main] SQL - {call RESET_SEQUENCE_TABLE_VALUES_TO_LATEST_ID_VALUES()}
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - Sequence tables reset call finished!
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - doing stuff
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - testing!
[TRACE] 2021-11-08 15:32:41.087 [main] TransactionInterceptor - Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
[DEBUG] 2021-11-08 15:32:41.088 [main] SQL - select language0_.id as id1_77_, language0_.dateCreated as datecrea2_77_, language0_.englishLanguageName as englishl3_77_, language0_.languageCode as language4_77_, language0_.rightToLeft as righttol5_77_, language0_.translatedLanguageName as translat6_77_ from languages language0_
[TRACE] 2021-11-08 15:32:41.091 [main] TransactionInterceptor - Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
[INFO ] 2021-11-08 15:32:41.091 [main] StartupChecks - end test!
[TRACE] 2021-11-08 15:32:41.091 [main] TransactionInterceptor - Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]
[DEBUG] 2021-11-08 15:32:41.112 [main] SQL - select language0_.id as id1_77_, language0_.dateCreated as datecrea2_77_, language0_.englishLanguageName as englishl3_77_, language0_.languageCode as language4_77_, language0_.rightToLeft as righttol5_77_, language0_.translatedLanguageName as translat6_77_ from languages language0_ where language0_.languageCode=?
[TRACE] 2021-11-08 15:32:41.113 [main] TransactionInterceptor - Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]
[TRACE] 2021-11-08 15:32:41.113 [main] TransactionInterceptor - No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]: This method is not transactional.
[DEBUG] 2021-11-08 15:32:41.115 [main] SQL - select authority0_.ID as id1_7_, authority0_.dateCreated as datecrea2_7_, authority0_.NAME as name3_7_ from AUTHORITY authority0_ where authority0_.ID is not null limit ?
[TRACE] 2021-11-08 15:32:41.120 [main] TransactionInterceptor - No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]: This method is not transactional.
Here is a list of the things that i've already tried.
Annotate languageDao with #Transactional(propagation = Propagation.SUPPORTS) or
#Transactional(propagation = Propagation.NESTED). NESTED isn't supported by hibernate and thus this causes an error, This error remains even when i set nestedTransactionAllowed to true on the transactionmanager. The setting SUPPORTS is ignored. The repository still starts a new transaction for each method that is called. (Update: Propagation.MANDATORY has no effect either)
I've named my transactionmanager customTransactionManager and added this as a parameter to #EnableJpaRepositories like so: #EnableJpaRepositories(basePackages={"DAOs"}, transactionManagerRef = "customTransactionManager")
I've set enableDefaultTransactions of #EnableJpaRepositories to false. This causes default methods like findAll() and save() to no longer be executed in a transaction by default. However it doesn't force them to use the transaction of the calling method that was annotated with #Transactional.
So my question is: How do i make the (custom) jpa repositories use the transaction that was started by the calling method?
EDIT: Here JPA - Spanning a transaction over multiple JpaRepository method calls a similar problem is described. According to the user spring only uses the existing transaction when the repository implements Repository instead of CrudRepository or JpaRepository. But this is a workaround.
EDIT 2: My #Transactional annotations keep working when i remove #EnableTransactionManagement. According to this post that can occur when i use spring-boot-starter-jdbc or spring-boot-starter-data-jpa as a dependency, which i do. Could these dependencies somehow interfere with the normal working of the transaction manager?
Here is my attempt at understanding your problem. I would recommend enabling extra debug
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
My test Service class - note that this is marked as transactional - for now that's the only place it is put as that's what we intend - to create a transactional boundary.
#Service
public class LanguageService {
#Autowired
private LanguageRepository languageRepository;
#Transactional
public void runAllMethods() {
languageRepository.findByLanguageCode("en");
languageRepository.findAll();
languageRepository.findByIdNotNull();
}
}
Next is the repository - there are no transactional annotations.
public interface LanguageRepository extends JpaRepository<Language, Long> {
public Language findByLanguageCode(String languageCode);
public Language findByIdNotNull();
}
Now on hitting the service via a controller - I get below logs. Notice the line where it says "Creating new transaction with name [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT" - meaning that the transaction was created at the beginning of method invocation.
Also note the statement "Participating in existing transaction" which indicates that method is participating in transaction.
2021-11-09 11:43:06.061 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2084817241<open>)] for JPA transaction
2021-11-09 11:43:06.061 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2021-11-09 11:43:06.069 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#3107a702]
2021-11-09 11:43:06.069 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : Getting transaction for [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]
2021-11-09 11:43:06.099 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]: This method is not transactional.
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_ where language0_.language_code=?
2021-11-09 11:43:06.333 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2084817241<open>)] for JPA transaction
2021-11-09 11:43:06.333 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Participating in existing transaction
2021-11-09 11:43:06.333 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByIdNotNull]: This method is not transactional.
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_ where language0_.id is not null
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : Completing transaction for [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]
2021-11-09 11:43:06.348 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit
2021-11-09 11:43:06.348 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(2084817241<open>)]
For readonly methods - like findAll - you would see "No need to create transaction" - that's because although the default Repository implementation "SimpleJpaRepository" is marked as transactional - the readonly methods are not marked transactional.
#Repository
#Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
After having tried different things, including using a TransactionTemplate i've settled for the following solution:
First i've turned off the default transaction policy for the jparepository methods by annotating a configuration class with the following:
#EnableJpaRepositories(enableDefaultTransactions = false)
enableDefaultTransactions = false causes any inherited method of JpaRepository to stop creating a transaction whenever they're called. Only jpa methods that are explicitly annotated with #Transactional will continue to create a new transaction when called.
All the other ones will now use any transaction that is started by the calling method, for instance a service method that is annotated with #Transactional.
This isn't obvious though because the This method is not transactional log trace message will still be generated for any jpa method that isn't explicitly annotated with #Transactional. This can be a bit confusing.
However i've proven that these methods really do use the transaction of the calling method by testing it with the following custom update method.
#Modifying
#Query("UPDATE User u SET u.userStatus = 1 WHERE u.userStatus = 0")
public void resetActiveUserAccountsToStatusOffline();
Such a method needs to have a transaction or else the exception javax.persistence.TransactionRequiredException: Executing an update/delete query is thrown. But as you can see this jpa method wasn't annotated with #Transactional so it really did use the transaction that was started by the calling service method.
There is one small disadvantage to setting enableDefaultTransactions = false and that is that the transaction type of inherited methods like findAll will not always use a transaction that is read only. This really depends on whether the service level transaction is readonly or not. However you could still override the findAll method and explictly annotate it with Transactional(readOnly = false). Another thing to beware of is that any calling method must always be annotated with #Transactional or the jpa method will run outside a transaction.
I think the advantage far outweighs these small disadvantages though. Because it is very costly performance wise when a new transaction is created for every jpa method call. So this is the solution i'll settle for right now.
To test your own transactions you'll need to add this to your application.properties
logging.level.org.springframework.transaction.interceptor=TRACE
If the setting doesn't work please add Log4j2 to your project.
EDIT:
These additional transactions that are opened by the JpaMethods are only logical transactions when a physical transaction has already been created by the calling method. More about this here: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction
These jpa methods still use the transaction created by the calling method.
The last answer in this SO thread also explains the difference between logical and physical transactions well: Difference between physical and logical transactions in spring
Experienced same issue while having multiple datasources, hence multiple transaction managers. Apparently the problem was that service methods marked #Transactional used the primary transaction manager, while the repositories were configured to use custom transaction manager:
#EnableJpaRepositories(
basePackageClasses = {
MyRepository.class
},
entityManagerFactoryRef = "customEntityManager",
transactionManagerRef = "customTransactionManager"
)
Solved the issue using spring's annotation on service methods with transactionManager param specified #Transactional(transactionManager = "customTransactionManager")

Play 2.5 - #Transactional readOnly ignored?

I have a Java Play 2.5 application, and I have a transaction marked as readonly (at the Controller level):
#Transactional(readOnly = true)
public Result list() { ...
(this is the play.db.jpa.Transactional annotation).
So, if I turn on logging for hibernate transaction:
<logger name="org.hibernate.engine.transaction" level="TRACE"/>
Then I see the transaction being made and being committed.
019-12-03 23:42:23,754 [debug] from org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator in main - No JtaPlatform was specified, checking resolver
2019-12-03 23:42:23,755 [debug] from org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformResolverInitiator in main - No JtaPlatformResolver was specified, using default [org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver]
2019-12-03 23:42:23,766 [debug] from org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver in main - Could not resolve JtaPlatform, using default [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2019-12-03 23:58:46,769 [debug] from org.hibernate.engine.transaction.internal.TransactionImpl in application-akka.actor.default-dispatcher-5 - begin
2019-12-03 23:58:46,878 [error] from application in application-akka.actor.default-dispatcher-5 - Transaction: org.hibernate.engine.transaction.internal.TransactionImpl#3d505ddd[transactionCoordinator=org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl#89a1241,exceptionConverter=org.hibernate.internal.ExceptionConverterImpl#76f0443c,transactionDriverControl=org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl#49f1518a]
2019-12-03 23:59:27,638 [debug] from org.hibernate.engine.transaction.internal.TransactionImpl in application-akka.actor.default-dispatcher-5 - committing
However, I have one problem - it's not read-only! I've added some code that modifies an object (obj.setField("changed value")), and it's saved into the database at the end of the transaction!
I was expecting an exception telling me that the transaction is read-only and that it cannot be committed!
Why it the transaction not read-only?
Can I somehow see in logs if Hibernate creates the transaction readonly or not?
Other info: Java Play 2.5.10, Hibernate 5 (5.2.6), and PostgreSQL as the DB.
So, it seems that it was because the controller class itself was annotated with #Transactional, and the method with #Transactional(readOnly=true).
Somehow, the class annotation overrides the method annotation. This doesn't seem logical to me, but in practice, that seems to be the case: as soon as I've removed the #Transactional from the class level, it worked just fine.

Spring boot test: context loaded for every test?

In my project we have a super class for all our tests. This is the signature of that class
#RunWith(SpringRunner.class)
#SpringBootTest(value = {"management.port=0"}, classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT)
#ActiveProfiles({"localhost", "test"})
#ContextConfiguration(classes = {Application.class, SomeConfiguration.class})
#Ignore
public abstract class AIntegrationTest {
Where Application.class is our main class, and SomeConfiguration.class it just for some #Bean and other stuff, nothing fancy.
I use gradle, and for running my tests I do:
./gradlew :my-project:test
My problems are:
I'm not sure if for each test the context is being initialized. But I can assure the context gets initialized multiple times. I know this by looking at the logs.
Since multiple contexts are initialized, it seems that contexts overlap with each other. I know this because one of the symptoms is this exception:
Caused by: org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [org.springframework.cloud.context.environment.EnvironmentManager#36408d9e] with key 'environmentManager'; nested exception is javax.management.InstanceAlreadyExistsException: RedeemAway:name=environmentManager,type=EnvironmentManager
Even if I don't care about the multiple contexts being loaded, is my impression that when a test finishes, the next test gets a new context BEFORE the previous one is terminated. I said this because of the overlapping of the exception from above.
Since all the tests share the same JVM, when some beans get registered twice, that exception rises up. From this link:
Context caching
It is said that:
An ApplicationContext can be uniquely identified by the combination of
configuration parameters that is used to load it. Consequently, the
unique combination of configuration parameters is used to generate a
key under which the context is cached. The TestContext framework uses
the following configuration parameters to build the context cache key
I understand that, but, I'm wondering how can I achieve that? My goal is to run all my tests over the same JVM and reuse the context with every test.
EDIT on Thu Feb 22
Things I tried:
spring.jmx.enabled: false
spring.jmx.default-domain: some-value
Really disabling JMX shouldn't help since the excpetion is around the EnvironmentManager, which is from Spring Cloud.
I found the answer to my problem. Here is well explained:
https://github.com/spring-projects/spring-boot/issues/7174
Basically, if you run a bunch of tests, as soon as one of them gets started, if it uses the annotation #MockBean it will force Spring to reload the context.
Bonus: you will see the same behavior if your test uses org.mockito.Mock.

How to completely replace persistence in SpringBoot?

Most amount of information found was in the official docs.
I want to replace the JPA provider completely (use Hibernate OGM)
One solution would be to create a bean for transactions and a bean with emf.
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "id_from_persistence.xml");
But what I am really looking for is to completely integrate another JPA provider into SpringBoot, how possible is that?
#Bean
public LocalEntityManagerFactoryBean entityManagerFactory(){
LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName("id_from_persistence.xml");
return factoryBean;
}
If it is - i am definitely missing something out, first exception is Entity Manager is not initialized.
As far as I have understood Id need to provide my own implementation of org.springframework.orm.jpa.JpaVendorAdapter? As an example there exists the following class:
org.hibernate.ogm.jpa.impl.OgmEntityManagerFactory implements HibernateEntityManagerFactory
What auto configuration classes to disable?
What manual configuration is further required?
Leading on I got the following class suggested for controlling the persistence in Spring:
org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean

#PersistenceUnit EntityManagerFactory not getting initialized / still null

I have a web application that I'm attempting to run inside the JBOSS EAP 6 App Server. I am attempting to Inject an EntityManagerFactory into my Singleton Bean.
#Singleton
#Startup
public class RestServerConfigurator {
private static RestServerConfigurator instance;
#PersistenceUnit(unitName = "clinicalTrialsEntityManager")
private EntityManagerFactory emf;
public RestServerConfigurator() {
if (emf == null) {
System.out.println("You're null.");
}
else {
System.out.println("Yay! Not null!");
}
}
To some extent, I know the PersistenceUnit annotation is being evaluated. If I attempt to give it a unitName other than the one mentioned in my persistence.xml, the JBOSS server will complain when I attempt to deploy the application.
Relevant persistence.xml structure.
<persistence-unit name="clinicalTrialsEntityManager" transaction-type="RESOURCE_LOCAL">
<jta-data-source>java:jboss/datasources/ClinicalTrialsDS</jta-data-source>
By all accounts, it appears the "emf" variable is never initialized, and that even though it deploys successfully, a null pointer is thrown whenever the EntityManagerFactory is attempted to be used.
I even created a Singleton StartUp Bean to initialize the EntityManagerFactory thinking maybe that's what I was missing and that will somehow tie them together, but to no avail. Please let me know what I'm missing, or if more information is required.
Also, if someone could explain how the EntityManagerFactory is "supposed" to get initialized behind the scenes when the annotation/injection method is used, that would be most appreciated. I'm fairly new to EJB concepts and am having trouble getting my head around it.
10:00:10,348 INFO [org.jboss.as.jpa] (ServerService Thread Pool -- 135) JBAS011402: Starting Persistence Unit Service 'clinicalTrials.war#clinicalTrialsEntityManager'
10:00:10,348 INFO [org.hibernate.ejb.Ejb3Configuration] (ServerService Thread Pool -- 135) HHH000204: Processing PersistenceUnitInfo [
name: clinicalTrialsEntityManager
...]
10:00:10,371 INFO [org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator] (ServerService Thread Pool -- 135) HHH000130: Instantiating explicit connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
10:00:10,411 INFO [org.hibernate.dialect.Dialect] (ServerService Thread Pool -- 135) HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect
Log output to verify the Bean is initialized:
13:53:36,132 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-10) JNDI bindings for session bean named RestServerConfigurator in deployment unit deployment "clinicalTrials.war" are as follows:
java:global/clinicalTrials/RestServerConfigurator!edu.emory.clinical.trials.webapp.server.rest.RestServerConfigurator
java:app/clinicalTrials/RestServerConfigurator!edu.emory.clinical.trials.webapp.server.rest.RestServerConfigurator
java:module/RestServerConfigurator!edu.emory.clinical.trials.webapp.server.rest.RestServerConfigurator
java:global/clinicalTrials/RestServerConfigurator
java:app/clinicalTrials/RestServerConfigurator
java:module/RestServerConfigurator
All of my issues surrounding dependency injection not functioning properly or as expected (especially in my Rest Service classes) were resolved once I unplugged my Jersey Implementation and relied on JBoss' Tightly coupled RESTEasy module.
To arrive at this conclusion, I used their example apps (specifically helloworld-rs and hibernate4) and reverse engineered from there.
I found an article about extending AbstractBinder and registering that with my application, but it just seemed easier (and required less code) for my purposes to remove Jersey and all the manual "exclude" RESTEasy config that I initially did after reading this article.
An entity manager whose transactions are controlled by the application through the EntityTransaction API is a resource-local entity manager. A resource-local entity manager transaction is mapped to a resource transaction over the resource by the persistence provider. Resource-local entity managers may use server or local resources to connect to the database and are unaware of the presence of JTA transactions that may OR may not be active.
<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="ShopPU" transaction-type="RESOURCE_LOCAL">
...
</persistence-unit>
</persistence>
Create EntityManager reference using below code
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Order");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
...
em.getTransaction().commit();
em.close();
emf.close ();

Categories

Resources