Two EntityManagerFactories, TransactionRequiredException: no transaction is in progress - spring

The Spring MVC application uses SpringDataJPA and hibernate. The production mode leverages MySQL but for the unit tests I have set up H2DB to run, both with separate java configuration. The application also utilizes Spring Security but its configuration I omitted below.
The appliaction starts up with the unit tests but at the first test (testOIC() see in the below code) I got the "TransactionRequiredException: no transaction is in progress".
testException = org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress, mergedContextConfiguration = [WebMergedContextConfiguration#1c581a0 testClass = OpenPositionsServiceTest, locations = '{}', classes = '{class our.dcollect.configuration.AppConfiguration, class our.dcollect.configuration.HibernateConfigurationTest}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.test.context.web.WebDelegatingSmartContextLoader', parent = [null]]].
The unit test I run with Propagation.REQUIRES_NEW, so it should have a transaction context. I am afraid I messed up something in the appliaction context. The posts I found in connection with this topic did not help a lot.
The ApplicationContext contains the following beans:
Spring Bean Definition Names in ApplicationContext (applicationContext.getBeanDefinitionNames()):
===================================================
Bean: appConfiguration
Bean: authenticationManagerBuilder
Bean: autowiredWebSecurityConfigurersIgnoreParents
Bean: beanNameHandlerMapping
Bean: dataSource
Bean: dataSourceTest
Bean: defaultServletHandlerMapping
Bean: delegatingApplicationListener
Bean: dtoConverter
Bean: emBeanDefinitionRegistrarPostProcessor
Bean: enableGlobalAuthenticationAutowiredConfigurer
Bean: entityManagerFactory
Bean: entityManagerFactoryTest
Bean: fileUploadController
Bean: fileUploadService
Bean: getInternalResourceViewResolverJsp
Bean: handlerExceptionResolver
Bean: hibernateConfiguration
Bean: hibernateConfigurationTest
Bean: hibernateProperties
Bean: httpRequestHandlerAdapter
Bean: initializeAuthenticationProviderBeanManagerConfigurer
Bean: initializeUserDetailsBeanManagerConfigurer
Bean: jpaContext
Bean: jpaMappingContext
Bean: jpaVendorAdapter
Bean: jpaVendorAdapterTest
Bean: multipartResolver
Bean: mvcContentNegotiationManager
Bean: mvcConversionService
Bean: mvcPathMatcher
Bean: mvcResourceUrlProvider
Bean: mvcUriComponentsContributor
Bean: mvcUrlPathHelper
Bean: mvcValidator
Bean: mvcViewResolver
Bean: objectPostProcessor
Bean: openIn....Repository
Bean: openPositionsService
Bean: org.springframework.aop.config.internalAutoProxyCreator
Bean: org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
Bean: org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
Bean: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
Bean: org.springframework.context.annotation.internalCommonAnnotationProcessor
Bean: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
Bean: org.springframework.context.annotation.internalPersistenceAnnotationProcessor
Bean: org.springframework.context.annotation.internalRequiredAnnotationProcessor
Bean: org.springframework.context.event.internalEventListenerFactory
Bean: org.springframework.context.event.internalEventListenerProcessor
Bean: org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension#0
Bean: org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport_Predictor
Bean: org.springframework.orm.jpa.SharedEntityManagerCreator#0
Bean: org.springframework.orm.jpa.SharedEntityManagerCreator#1
Bean: org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
Bean: org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration
Bean: org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration
Bean: org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration
Bean: org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration
Bean: org.springframework.transaction.config.internalTransactionAdvisor
Bean: org.springframework.transaction.config.internalTransactionalEventListenerFactory
Bean: org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
Bean: privilegeEvaluator
Bean: push....Repository
Bean: requestDataValueProcessor
Bean: requestMappingHandlerAdapter
Bean: requestMappingHandlerMapping
Bean: resourceHandlerMapping
Bean: securityConfiguration
Bean: sessionFactory
Bean: sessionFactoryTest
Bean: simpleControllerHandlerAdapter
Bean: springSecurityFilterChain
Bean: transactionAttributeSource
Bean: transactionInterceptor
Bean: transactionManager
Bean: transactionManagerTest
Bean: viewControllerHandlerMapping
Bean: webSecurityExpressionHandler
===================================================
Could you help? The relevant code parts I posted below.
MVC configuration:
package our.dcollect.configuration;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "our.dcollect")
public class AppConfiguration {
private static final Logger log = Logger.getLogger(AppConfiguration.class);
#Bean
public InternalResourceViewResolver getInternalResourceViewResolverJsp(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
viewResolver.setOrder(0);
log.info("#### Internal view resolver 0 called...");
return viewResolver;
}
#Bean
public StandardServletMultipartResolver multipartResolver(){
log.info("#### Multipart resolver called...");
return new StandardServletMultipartResolver();
}
}
Hibernate Configuration:
package our.dcollect.configuration;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#ComponentScan(basePackages = "our.dcollect")
#EnableJpaRepositories(basePackages = {"our.dcollect.repository"},
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager")
#PropertySource(value = { "classpath:application.properties" })
#EnableTransactionManagement
//#PropertySource({ "/resources/hibernate.properties" })
public class HibernateConfiguration {
#Autowired
private Environment environment;
#Bean(name="sessionFactory")
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "our.dcollect" });
sessionFactory.setHibernateProperties(this.hibernateProperties());
return sessionFactory;
}
#Bean(name="dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
}
#Bean(name="hibernateProperties")
public Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", "create-drop");
return properties;
}
#Bean(name="transactionManager")
#Autowired
public HibernateTransactionManager transactionManager(#Qualifier("sessionFactory") SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
#Bean(name="jpaVendorAdapter")
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);
return hibernateJpaVendorAdapter;
}
#Bean(name="entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(this.dataSource());
lef.setJpaProperties(this.hibernateProperties());
lef.setJpaVendorAdapter(this.jpaVendorAdapter());
lef.setPackagesToScan(new String[] { "our.dcollect.model"});
return lef;
}
}
Hibernate Configuration for the Unit Tests:
package our.dcollect.configuration;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/***************************************************************************************
* The same as the real HibernateConfiguration but this works with H2 DB instead of MySQL.
* In addition, the properties are not read from a property file.
****************************************************************************************/
#Configuration
#ComponentScan(basePackages = "our.dcollect")
#EnableJpaRepositories(basePackages = {"our.dcollect.repository"},
entityManagerFactoryRef = "entityManagerFactoryTest",
transactionManagerRef = "transactionManagerTest")
#PropertySource(value = { "classpath:application.properties" })
#EnableTransactionManagement
public class HibernateConfigurationTest {
#Bean(name = "sessionFactoryTest")
public LocalSessionFactoryBean sessionFactoryTest() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSourceTest());
sessionFactory.setPackagesToScan(new String[] { "our.dcollect" });
sessionFactory.setHibernateProperties(hibernatePropertiesTest());
return sessionFactory;
}
#Bean(name = "dataSourceTest")
public DataSource dataSourceTest() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("...");
dataSource.setPassword("...");
return dataSource;
}
private Properties hibernatePropertiesTest() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "create-drop");
return properties;
}
#Bean(name = "transactionManagerTest")
#Autowired
public HibernateTransactionManager transactionManagerTest(#Qualifier("sessionFactoryTest") SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
#Bean(name = "jpaVendorAdapterTest")
public JpaVendorAdapter jpaVendorAdapterTest() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);
return hibernateJpaVendorAdapter;
}
#Bean(name = "entityManagerFactoryTest")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryTest() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(this.dataSourceTest());
lef.setJpaProperties(this.hibernatePropertiesTest());
lef.setJpaVendorAdapter(this.jpaVendorAdapterTest());
lef.setPackagesToScan(new String[] { "our.dcollect.model"});
return lef;
}
}
The Unit Test class abbriviated for legibility:
package our.dcollect.service;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.junit.After;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import our.dcollect.configuration.AppConfiguration;
import our.dcollect.configuration.HibernateConfigurationTest;
import our.dcollect.model.OpenIC;
import our.dcollect.repository.OpenICRepository;
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {AppConfiguration.class, HibernateConfigurationTest.class})
public class OpenPositionsServiceTest {
#Autowired
private OpenICRepository OpenICDAO;
#PersistenceContext(unitName = "entityManagerFactoryTest")
private EntityManager entityManager;
#Test
#Transactional(value="transactionManagerTest", propagation = Propagation.REQUIRES_NEW)
public void testOIC() {
System.out.println("getOpenPositionsNotRegisteredInPushBanch");
OpenIC oic = new OpenIC();
oic.setBaId("111");
oic.setBezeichnung("abc");
// clear the persistence context so we don't return the previously cached location object
// this is a test only thing and normally doesn't need to be done in prod code
entityManager.clear();
OpenICDAO.saveAndFlush(oic);
List<OpenIC> list = OpenICDAO.findAll();
assertEquals(list.size(), 1);
OpenIC oicReadBack = list.get(0);
OpenICDAO.delete(oicReadBack.getOpenICId());
}
[...]
}
Steps Towards the Solution:
The posts from Funtik helped a lot. The following setps I carried out:
#DependsOn("transactionManagerTest") to tell Spring that the
EntityManagerFactory needs to be loaded after the TransactionManager
but it did not solve the problem.
Introducing #Profile("test") for HibernateConfigurationTest and #Profile("!test") for HibernateConfiguration. On the test
class I put #ActiveProfiles("test"). This separated the beans;
only one flavour was loaded during the test: the ones in
HibernateConfigurationTest. This however did not solve the
problem either.
Then I completely removed one profile in a separate branch to see whether the two configuration for the DB cause the issue. I
experienced the same problem with one DB configuration as before.
I added #TestExecutionListeners({TransactionalTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class}) to the unit test
class and #Rollback(false) to the test method to see whether
anything is saved in the MySQL database. I also changed
"hibernate.hbm2ddl.auto" to "create", to preserve the DB after the
test. It changed the situation as the message appeared in the log:
"[...] Began transaction (1) for test context
[DefaultTestContext#a15b73 [...]". So one transaction context has
definitely been created and has had 1 transaction. The problem was
however that the entity was not persisted in the database if I call
OpenICDAO.save(oic) in the test. In addition, calling
OpenICDAO.saveAndFlush(oic) causes the nearly the exception below:
"[...] 4671 [main] WARN org.springframework.test.context.TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener#45c9b3] to process 'after' execution for test: method [public void our.dcollect.service.OpenPositionsServiceTest.testGetOpenPositionsNotRegisteredInPushBanch()], instance [our.dcollect.service.OpenPositionsServiceTest#dc7b7d], exception [org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress]
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only [...]"
Tests in error:
testOIC(our.dcollect.service.OpenPositionsServiceTest): no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
testOIC(our.dcollect.service.OpenPositionsServiceTest): Transaction rolled back because it has been marked as rollback-only
Tests run: 3, Failures: 0, Errors: 2, Skipped: 0
However, I only have 2 methods not 3 from which testOIC is reported twice.
Solution:
The transactionManager must be a PlatformTransactionManager hosting a JpaTransactionManager. This solved the problem. Thank you for all the comments. I found the #Profile annotation very useful and the idea of listing the beans in the application context. After having the correct transactionManager, the annotation DependsOn() caused a circular reference, so I removed it; #TestExecutionListeners annotation is also not necessary in this case. I took a simple demo application that I changed to my configuration and tried to reproduce the original fault. After being able to reproduce it, I checked the differences between the good and bad state, it helped to find the difference. (The same also needs to be done for transactionManagerTest that expects a SessionFactoryTest object.)
// #Bean(name="transactionManager")
// #Autowired
// public HibernateTransactionManager transactionManager(#Qualifier("sessionFactory") SessionFactory s) {
// HibernateTransactionManager txManager = new HibernateTransactionManager();
// txManager.setSessionFactory(s);
// return txManager;
// }
#Bean(name="transactionManager")
#Autowired
public PlatformTransactionManager transactionManager(#Qualifier("sessionFactory") SessionFactory s) {
return new JpaTransactionManager( entityManagerFactory().getObject() );
}

I had the same problem. From what I experienced it seems that the entityManager bean is loaded before transactionManager bean, so the spring does not sense the presence of any transactions at all.
#DependsOn annotation fixed this issue for me. Try setting the explicit dependency on TransactionManager like this:
#DependsOn("transactionManagerTest")
#Bean(name = "entityManagerFactoryTest")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryTest() {

I added the Solution to the original post at the bottom.

I have run into this issue while trying to persist an entity during test.
And followed your solution in trying to resolve:
"The transactionManager must be a PlatformTransactionManager hosting a JpaTransactionManager"
Changed HibernateTransactionManager to JpaTransactionManager
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory s) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(s);
return txManager;
}
Was very amazed for your solution to work. If anyone could point me on documentation where it's described it would be great.
Thank you.

Related

Rollback is not working in #Transactional annotated service. Spring + Hibernate

In my code the service method savePerson is annotated with #Transactional. Inside this method a Person entity is persisted and inmediately a Runtime exception is intentionally throwed. I suposse the transaction should not be commited but the Person entity is persisted in database....rollback is not working and I dont know why.
This is my Hibernate Configuration:
package newp;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
public class HibernateConf {
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[]{"newp"});
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/poas");
dataSource.setUsername("admin");
dataSource.setPassword("admin");
return dataSource;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager= new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
hibernateProperties.setProperty("hibernate.show_sql", "true");
return hibernateProperties;
}
}
This is my service:
package newp.services;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import newp.dao.PersonDao;
import newp.model.Person;
#Service
public class PersonService{
#Autowired
PersonDao dao;
#Transactional
public void savePerson(Person p) {
dao.savePerson(p);
throw new RuntimeException();
}
}
The DAO
package newp.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import newp.entities.PersonEntity;
import newp.model.Person;
#Repository
public class PersonDao {
#Autowired
SessionFactory sessionFactory;
public void savePerson(Person person) {
Session s = sessionFactory.getCurrentSession();
PersonEntity p=new PersonEntity();
p.setAge(person.getAge());
p.setName(person.getName());
p.setSurname(person.getSurname());
s.saveOrUpdate(p);
}
}
You are probably using tables with the MyISAM storage engine which does not support transactions: https://stackoverflow.com/a/8036049/412446

spring boot 2. #Bean method invoked before injecting #Autowired dependency

This is just curiosity.
In example below #Autowired EntityManagerFactory and #Autowired ApplicationContext are injected before #Bean entityManager() method.
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class Config {
#Autowired
private EntityManagerFactory entityManagerFactory;
#Autowired
private ApplicationContext context;
#Bean
public EntityManager entityManager() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
return entityManager;
}
}
But when i change EntityManager bean type to SessionFactory then sessionFactory() method is invoked before autowiring EntityManagerFactory and ApplicationContext beans causing NullPointerException when unwrapping SessionFactory. Code snippet below
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class Config {
#Autowired
private EntityManagerFactory entityManagerFactory;
#Autowired
private ApplicationContext context;
#Bean
public SessionFactory sessionFactory() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
return entityManager.unwrap(SessionFactory.class);
}
}
And my question is: Why is this happening?
As of Hibernate 5.2 the SessionFactory is also an EntityManagerFactory as it now extends said interface. Prior to this the SessionFactory was wrapping an EntityManagerFactory.
Due to this the EntityManagerFactory cannot be injected because the SessionFactory is the actual bean implementing that interface.
As far as i remember there are two ways to obtain the SessionFactory:
From EntityManagerFactory
return entityManagerFactory.unwrap(SessionFactory.class)
//or -> if you have entitymanager
return em.getEntityManagerFactory().unwrap(SessionFactory.class);
From Session
Session session = entityManager.unwrap(Session.class);
return session.getSessionFactory();
And the reasons you are curious like as you said
sessionFactory() method is invoked before autowiring EntityManagerFactory and ApplicationContext beans causing NullPointerException
This is not the case

Hibernate exception when try to launch Tomcat

I have application on spring-mvc+jpa. I build war and try to start on tomcat.
DataConfig:
import org.hibernate.ejb.HibernatePersistence;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.annotation.Resource;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:app.properties")
public class DataConfig {
private static final String PROP_DATABASE_DRIVER = "db.driver";
private static final String PROP_DATABASE_URL = "db.url";
private static final String PROP_DATABASE_USERNAME = "db.username";
private static final String PROP_HIBERNATE_DIALECT = "db.hibernate.dialect";
private static final String PROP_HIBERNATE_SHOW_SQL = "db.hibernate.show_sql";
private static final String PROP_ENTITYMANAGER_PACKAGES_TO_SCAN = "db.entitymanager.packages.to.scan";
private static final String PROP_HIBERNATE_HBM2DDL_AUTO = "db.hibernate.hbm2ddl.auto";
#Resource
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROP_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROP_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROP_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty("db.password"));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setDataSource(dataSource());
entityManager.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManager.setPersistenceProviderClass(HibernatePersistence.class);
entityManager.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManager.setJpaProperties(getHibernateProperties());
return entityManager;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put(PROP_HIBERNATE_DIALECT, env.getRequiredProperty(PROP_HIBERNATE_DIALECT));
properties.put(PROP_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROP_HIBERNATE_SHOW_SQL));
properties.put(PROP_HIBERNATE_HBM2DDL_AUTO, env.getRequiredProperty(PROP_HIBERNATE_HBM2DDL_AUTO));
return properties;
}
}
app.properties is:
#DB properties:
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://127.0.0.1:3306/mydb
db.username=root
db.password=111111
#Hibernate Configuration:
db.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
db.hibernate.show_sql=true
db.entitymanager.packages.to.scan=ru.sbrf.risks.services.data.model
db.hibernate.hbm2ddl.auto = create-drop
So, there is error message in tomcat logs:
Caused by: javax.persistence.PersistenceException: [PersistenceUnit:
default] Unable to build EntityManagerFactory at
org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:924)
at
org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:899)
at
org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:76)
at
org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:287)
at
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)
... 57 more Caused by: org.hibernate.HibernateException: Connection
cannot be null when 'hibernate.dialect' not set at
org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:97)
at
org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:67)
at
org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:170)
at
org.hibernate.service.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:75)
at
org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:159)
at
org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:131)
at
org.hibernate.cfg.Configuration.buildTypeRegistrations(Configuration.java:1818)
at
org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1776)
at
org.hibernate.ejb.EntityManagerFactoryImpl.(EntityManagerFactoryImpl.java:96)
at
org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:914)
... 63 more
Hibernate doesn't understand the properties you are passing it. You have hibernate.dialect defined as db.hibernate.dialect Remove the db portion and just have it as hibernate.dialect and do the same with your other hibernate related properties.
https://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/session-configuration.html
#PropertySource("classpath:app.properties")
This annotation value replace with below passing value
#PropertySource(value = {"classpath:application.properties","file:config/application.properties"}, ignoreResourceNotFound = true)
and rebuild you project.now it's working fine.

Spring Boot 2.0.0.M4 & Hibernate 5.2.11.Final could not find bean of type EntityManagerFactoryBuilder

I have clearly what seems to be some configuration issue, but I've been unable to resolve this myself. I have hope you guys could help me?
None of the examples I find indicate having to create a bean for EntityManagerFactoryBuilder so what's the issue.
I am attempting to configure completely separate datasources, including different entity managers etc..
My Error:
APPLICATION FAILED TO START
Description:
Parameter 0 of method entityManagerFactory required a bean of type 'org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder' in your configuration.
My DataSourceConfiguration
package ...;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
#Configuration
#EnableTransactionManagement
public class DataSourceConfiguration {
#Primary
#Bean()
#ConfigurationProperties(prefix="spring.my.datasource")
public DataSource myDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "myEntityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder factoryBuilder,
#Qualifier("myEntityManager") DataSource bds) {
...
}
}
My Application
#SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class
})
My Pom
By excluding HibernateJpaAutoConfiguration you are turning off creating EntityManagerFactoryBuilder bean.
By default EntityManagerFactoryBuilder bean is produced by JpaBaseConfiguration#entityManagerFactoryBuilder(JpaVendorAdapter, ObjectProvider).
There is only one JpaBaseConfiguration implementation - HibernateJpaConfiguration, which is activated if:
there is only one DataSource candidate (or one is marked as #Primary);
HibernateJpaAutoConfiguration conditions are true - check HibernateJpaAutoConfiguration class.
You should either create and configure LocalContainerEntityManagerFactoryBean without builder (like you did in your own answer), or create EntityManagerFactoryBuilder in your configuration manually like this:
#Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
return new EntityManagerFactoryBuilder(new HibernateJpaVendorAdapter(), new HashMap<>(), null);
}
In response to #Albert Bos questions...
I think what did it was this.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackageClasses = { MyEntityRepository.class },
repositoryBaseClass = EntityRepositoryImpl.class,
transactionManagerRef = "myTransactionManager",
entityManagerFactoryRef = "myEntityManagerFactory"
)
public class MyDataSourceConfiguration { ...
This is my datasource configuration.
#Bean(name = DATASOURCE_NAME + "DataSource")
public DataSource dataSource(#Qualifier(DATASOURCE_NAME + "DataSourceProperties") DataSourceProperties dataSourceProperties)
{
return DataSourceBuilder.create()
.url(dataSourceProperties.getUrl())
.username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword())
.driverClassName(dataSourceProperties.getDriverClassName())
.build();
}
#Bean(name = DATASOURCE_NAME + "EntityManagerFactory")
#PersistenceContext(unitName = DATASOURCE_NAME + "PersistenceUnit")
public EntityManagerFactory entityManagerFactory(
#Qualifier(DATASOURCE_NAME + "JpaProperties") Properties jpaProperties,
#Qualifier(DATASOURCE_NAME + "DataSource") DataSource dataSource,
#Qualifier(DATASOURCE_NAME + "JpaVendorAdapter") JpaVendorAdapter jpaVendorAdapter)
{
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setJpaProperties(jpaProperties);
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setPackagesToScan(MyEntity.class.getPackage().getName());
factoryBean.setPersistenceUnitName(DATASOURCE_NAME + "PersistenceUnit");
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
#Bean(name = DATASOURCE_NAME + "TransactionManager")
public PlatformTransactionManager transactionManager(#Qualifier(DATASOURCE_NAME + "EntityManagerFactory") EntityManagerFactory entityManagerFactory)
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
And Application Configuration
#SpringBootApplication(exclude = {
LiquibaseAutoConfiguration.class,
DataSourceAutoConfiguration.class,
ValidationAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class
})

java.lang.NoClassDefFoundError: org/springframework/orm/hibernate4/SpringSessionContext

I am try to implement SpringMVC 4 and Hibernate 4 integration with annotation in my project but I am getting this error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in com.config.ApplicationContextConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.hibernate.SessionFactory]: Factory method 'getSessionFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/springframework/orm/hibernate4/SpringSessionContext
My Config file is::
import javax.sql.DataSource;
import java.util.Properties;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.orm.hibernate4.LocalSessionFactoryBuilder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.orm.hibernate4.SpringSessionContext;
import com.dao.UserDao;
import com.dao.UserDaoImpl;
import com.pojo.User;
#Configuration
#ComponentScan("com.config")
#EnableTransactionManagement
public class ApplicationContextConfig {
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/usersdb");
dataSource.setUsername("root");
dataSource.setPassword("");
return dataSource;
}
#Autowired
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
sessionBuilder.addAnnotatedClasses(User.class);
return sessionBuilder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
return properties;
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(
SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(
sessionFactory);
return transactionManager;
}
#Autowired
#Bean(name = "userDao")
public UserDao getUserDao(SessionFactory sessionFactory) {
return new UserDaoImpl(sessionFactory);
}
}
It appears that you need, but do not have spring-orm-4.3.0.RELEASE.jar on your classpath. (I have identified the 4.3.0 version, you may be using a different 4.X version).
If you are using Maven, add the appropriate dependency to your pom.xml.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.0.RELEASE</version>
<scope>runtime</scope>
</dependency>
If you are not using Maven, you can download the jar file here.
For information on setting the classpath see this section of the Java Tutorial.

Resources