Transaction isn't being created in Spring + Hibernate + JPA setup - java

I have this strange situation, when in test environment everything works fine, but when deployed on WildFly entityManager cannot find a transaction for some reason.
I already checked if bean is created twice - once in mvc and once in core context - but trace only appears once.
Tried pretty much every other fix for this on StackOverflow, but the problem (unlike the data i want EntityManager to save) persists.
Maybe you'd be able to spot some error in my config which i failed to find?
Hibernate java config (DataSource is initialized in another module, switching to commented DS does nothing):
#Configuration
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#EnableJpaRepositories(
entityManagerFactoryRef = "user.management.entityManagerFactory",
transactionManagerRef = "user.management.transactionManager")
public class HibernateConfiguration {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "org.postgresql.Driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "";
private static final String PROPERTY_NAME_DATABASE_URL = "jdbc:postgresql://localhost:5432/postgres";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "postgres";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
#Resource
private Environment env;
#Autowired
#Bean(name = "user.management.jpaDialect")
HibernateJpaDialect getDialect() {
return new HibernateJpaDialect();
}
#Autowired
#Bean(name = "user.management.jpaVendorAdapter")
HibernateJpaVendorAdapter getJpaVendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.HSQL);
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.HSQLDialect");
vendorAdapter.setGenerateDdl(false);
return vendorAdapter;
}
#Autowired
#Bean(name = "user.management.impl.repository.util.DbUtil")
DbUtil getDbUtil(
#Qualifier("DataSource")
DataSource dataSource
) {
DbUtil dbUtil = new DbUtil(dataSource);
return dbUtil;
}
#Autowired
#Bean(name = "hibernate5AnnotatedSessionFactory")
LocalSessionFactoryBuilder getLocalSessionFactoryBean(
#Qualifier("DataSource")
DataSource dataSource
) {
LocalSessionFactoryBuilder localSessionFactoryBean =
new LocalSessionFactoryBuilder(dataSource);
localSessionFactoryBean.scanPackages(
"user.management.impl.repository.pojo"
);
localSessionFactoryBean.addProperties(getHibernateProperties());
localSessionFactoryBean.buildSessionFactory();
return localSessionFactoryBean;
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", "false");
properties.put("hibernate.current_session_context_class", "thread");
properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
return properties;
}
/*#Autowired
#Bean(name = "user.management.dataSource")
public DataSource getPooledDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(PROPERTY_NAME_DATABASE_DRIVER);
dataSource.setUrl(PROPERTY_NAME_DATABASE_URL);
dataSource.setUsername(PROPERTY_NAME_DATABASE_USERNAME);
dataSource.setPassword(PROPERTY_NAME_DATABASE_PASSWORD);
return dataSource;
}*/
#Autowired
#Bean(name = "user.management.entityManagerFactory")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactory(
#Qualifier("DataSource")
DataSource dataSource
) {
System.out.println("getEntityManagerFactory()");
LocalContainerEntityManagerFactoryBean theEntityManager = new LocalContainerEntityManagerFactoryBean();
theEntityManager.setPersistenceXmlLocation("classpath:META-INF/persistence.xml");
theEntityManager.setPersistenceUnitName("umPersistenceUnit");
theEntityManager.setDataSource(dataSource);
theEntityManager.setPackagesToScan(
"user.management.impl.repository.pojo",
);
theEntityManager.setJpaVendorAdapter(getJpaVendorAdapter());
theEntityManager.setJpaDialect(getJpaDialect());
theEntityManager.afterPropertiesSet();
return theEntityManager;
}
#Autowired
#Bean(name = "user.management.jpaDialect")
public JpaDialect getJpaDialect() {
return new HibernateJpaDialect();
}
#Autowired
#Bean(name = "user.management.transactionManager")
public JpaTransactionManager getTransactionManager(
#Qualifier("user.management.entityManagerFactory")
EntityManagerFactory entityManagerFactory,
#Qualifier("DataSource")
DataSource dataSource
) {
JpaTransactionManager jpaTranstactionManager =
new JpaTransactionManager(entityManagerFactory);
jpaTranstactionManager.setDataSource(dataSource);
jpaTranstactionManager.setJpaDialect(getJpaDialect());
jpaTranstactionManager.setEntityManagerFactory(entityManagerFactory);
jpaTranstactionManager.afterPropertiesSet();
return jpaTranstactionManager;
}
//DAO Classes
#Autowired
#Bean(name = "user.management.userRepository")
public UserRepository getUserDao(
#Qualifier("cacheManager")
CacheManager cacheManager) {
return new UserDaoImpl(getIdGenerator(), cacheManager);
}
#Autowired
#Bean(name = "user.management.groupDao")
public GroupRepository getGroupDao() {
return new GroupDaoImpl(getIdGenerator());
}
#Autowired
#Bean(name = "user.management.roleDao")
public RoleRepository getRoleDao() {
return new RoleDaoImpl(getIdGenerator());
}
#Autowired
#Bean(name = "user.management.profileDao")
public UserProfileRepository getProfileRepository() {
return new UserProfileDaoImpl(getIdGenerator());
}
#Autowired
#Bean(name = "user.management.impl.service.idgenerator.IdGenerator")
public IdGenerator getIdGenerator() {
return new IdGenerator();
}
#Autowired
#Bean(name = "user.management.AuthenticatorRepositoryDAO")
public AuthenticatorRepository getAuthenticationRepository() {
return new AuthenticatorRepositoryDAO();
}
#Autowired
#Bean(name = "user.management.dobLdapSourceRepository")
public LdapSourceRepository getLdapSourceRepository() {
return new LdapSourceRepositoryImpl();
}
#Autowired
#Bean(name = "user.management.dobOpenIdAuthenticationProviderRepository")
public OpenIdAuthenticationProviderRepository getOpenIdAuthenticationProviderRepository() {
return new OpenIdAuthenticationProviderRepositoryDAO();
}
}
Service java config
#Configuration
public class UserManagementConfiguration {
#Autowired
#Bean(name = "user.management.services.UserService")
public UserService getUserService(...) {
return new UserServiceImpl(...);
}
}
DAO
#Transactional(propagation = Propagation.REQUIRED)
public class UserDao implements UserRepository {
public UserDao(...) {}
#PersistenceContext(unitName = "umPersistenceUnit")
private EntityManager entityManager;
#Override
#Transactional(propagation = Propagation.REQUIRED)
public void persistUser(#Nonnull MutableUser user) {
entityManager.persist(user);
}
}
Service
public class UserServiceImpl implements UserService {
public UserServiceImpl(...);
}
#Override
#Transactional
public void persistUser(#Nonnull User user) {
userDAO.persistUser(mutableUser);
}
}
Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="1.0">
<persistence-unit name="umPersistenceUnit">
<!-- Forces Hibernate to work with old-style annotations -->
<properties>
<property name="hibernate.id.new_generator_mappings" value="false"></property>
</properties>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>user.management.impl.repository.pojo.User</class>
</persistence-unit>
</persistence>
Spring config.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:annotation-config/>
<tx:annotation-driven transaction-manager="user.management.transactionManager"/>
<bean class="user.management.config.UserManagementConfiguration"/>
<bean class="user.management.config.HibernateConfiguration"/>
</bean>
</beans>
Test config is identical, only uses different dataSource and jdbc.driver to connect to an in-memory h2.
What i've already tried:
Using <tx:annotation-driven> and pointing to a correct transactionManagerFactory
Removing transaction-type="RESOURCE_LOCAL" from persistence.xml
Adding #Transactional to specific methods in Dao and Service classes.
Checked that entityManagerFactory is only loaded once on server startup
Debugged the server to see if some other entityManager is called. I can clearly see my persistence unit and qualifier in its properties.
Probably some other minor fixes that popped up when i tried to google about this issue.
When i deploy code like posted here, i can see no errors in log, etc. When entitymanager.flush() is added it becomes javax.persistence.TransactionRequiredException: no transaction is in progress

Providing transaction-type="JTA" to persistence.xml explicitly fixed the issue, entities are being persisted to the DB sucessfully.

Related

Tables not getting created in multiple databases in spring boot application

I am working on spring boot multi tenancy application. I have configured multi datasources as shown below :
application.properties
spring.multitenancy.datasource1.url=jdbc:mysql://localhost:3306/db1
spring.multitenancy.datasource1.username=root
spring.multitenancy.datasource1.password=****
spring.multitenancy.datasource1.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.multitenancy.datasource2.url=jdbc:mysql://localhost:3306/db2
spring.multitenancy.datasource2.username=root
spring.multitenancy.datasource2.password=****
spring.multitenancy.datasource2.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.multitenancy.datasource3.url=jdbc:mysql://localhost:3306/db3
spring.multitenancy.datasource3.username=root
spring.multitenancy.datasource3.password=****
spring.multitenancy.datasource3.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
DataSourceBasedMultiTenantConnectionProviderImpl.java
#Component
public class DataSourceBasedMultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {
private static final long serialVersionUID = 8168907057647334460L;
private static final String DEFAULT_TENANT_ID = "tenant_1";
#Autowired
private DataSource dataSource1;
#Autowired
private DataSource dataSource2;
#Autowired
private DataSource dataSource3;
private Map<String, DataSource> map;
#PostConstruct
public void load() {
map = new HashMap<>();
map.put("tenant_1", dataSource1);
map.put("tenant_2", dataSource2);
map.put("tenant_3", dataSource3);
}
#Override
protected DataSource selectAnyDataSource() {
return map.get(DEFAULT_TENANT_ID);
}
#Override
protected DataSource selectDataSource(String tenantIdentifier) {
return map.get(tenantIdentifier);
}
}
MultitenancyProperties.java
#ConfigurationProperties("spring.multitenancy")
public class MultitenancyProperties {
#NestedConfigurationProperty
private DataSourceProperties datasource1;
#NestedConfigurationProperty
private DataSourceProperties datasource2;
#NestedConfigurationProperty
private DataSourceProperties datasource3;
public DataSourceProperties getDatasource1() {
return datasource1;
}
public void setDatasource1(DataSourceProperties datasource1) {
this.datasource1 = datasource1;
}
public DataSourceProperties getDatasource2() {
return datasource2;
}
public void setDatasource2(DataSourceProperties datasource2) {
this.datasource2 = datasource2;
}
public DataSourceProperties getDatasource3() {
return datasource3;
}
public void setDatasource3(DataSourceProperties datasource3) {
this.datasource3 = datasource3;
}
}
MultiTenancyJpaConfiguration.java
#Configuration
#EnableConfigurationProperties(JpaProperties.class)
public class MultiTenancyJpaConfiguration {
#Autowired
private DataSource dataSource;
#Autowired
private JpaProperties jpaProperties;
#Autowired
private MultiTenantConnectionProvider multiTenantConnectionProvider;
#Autowired
private CurrentTenantIdentifierResolver currentTenantIdentifierResolver;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
Map<String, Object> hibernateProps = new LinkedHashMap<>();
hibernateProps.putAll(jpaProperties.getHibernateProperties(dataSource));
hibernateProps.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
hibernateProps.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider);
hibernateProps.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
hibernateProps.put(Environment.DIALECT, "org.hibernate.dialect.MySQLDialect");
return builder.dataSource(dataSource).packages(HotelEntity.class.getPackage().getName()).properties(hibernateProps).jta(false).build();
}
}
Application launcher
#SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
#EnableConfigurationProperties(MultitenancyProperties.class)
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
When I run the boot application, all tables are created in only first data source.
1) How can I create tables in all data sources on application startup?
2) How to see connections opened/closed for each of the data sources?
3) Is there a better way of configuring multi tenancy application using spring boot for better performance?
As #Alex said, you need differnt EntityManagers, TransactionManager and Datasources. Here is how I would do it
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "dataSource1EntityManagerFactory",
transactionManagerRef = "dataSource1TransactionManager",
basePackageClasses = dataSource1Repository.class)
public class DataSource1Config extends SqlConfig{// Put all common code in base class SqlConfig. If not remove it
#Bean
#Primary
public DataSource dataSource1() {
//create dataSource using MultitenancyProperties::getDataSource1
}
#Primary
#Bean(name = "dataSource1TransactionManager")
PlatformTransactionManager dataSource1TransactionManager(EntityManagerFactory dataSource1EntityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(dataSource1EntityManagerFactory);
return txManager;
}
#Primary
#Bean(name = "dataSource1EntityManagerFactory")
LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource1());
em.setPackagesToScan(dataSource1Repository.class.getPackage().getName(), dataSource1ModelClass.class.getPackage().getName());
em.setPersistenceUnitName("dataSource1Db");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
}
You can create two other classes like this. Remember to use #Primary on only one instace of datasource, transactionmanger and entitymanager(doesn't matter which one). Another word of caution, make sure Repository classes are in different packages for all three databases.
Try adding the following properties:
spring.jpa.generate-ddl=true
You need 2 different persistence factories, not one, each should produce different EntityManagers for different datasources. Also each entity mapping should be marked to been used only with one entity manager.
See full solution here:
http://www.baeldung.com/spring-data-jpa-multiple-databases

How to test a service class that has #Autowire on a repository with spring framework?

I have been sitting with this all day, and I just dont get why this is not working. Furthermore this test does work with a spring boot project, but for some reason in this spring framework project this test does not work and the error thrown is
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean found for dependency
[com.globati.repository.DealRepository]: expected at least 1 bean
which qualifies as autowire candidate. Dependency annotations:
My test class looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/spring/DealServiceTest-context.xml"})
public class DealServiceTest {
#Autowired
DealService dealService;
#Autowired
EmployeeService employeeService;
#Test
public void createDeal() throws ServiceException {
Assert.assertEquals(1, 1);
// Employee employee = new Employee("Daniel", "tuttle", "danielptm#me.com", "dannyboy", "secret password", 23.234, 23.23);
// Deal d = dealService.createDeal("ADSF/ADSF/cat.jpg", "A title goes here", "A deal description", 23.22, "Name of business", 23.23,23.23, employee, "USA" );
// Assert.assertNotNull(d);
}
DealService looks like this:
#Service
public class DealService {
#Autowired
private DealRepository dealRepository;
DealService(){}
public DealService(DealRepository dealRepository){
this.dealRepository = dealRepository;
}
public Deal createDeal(String image, String title, String description, double distance, String location, double targetLat, double targetLong, Employee employee, String country) throws ServiceException {
Deal deal = new Deal(image, title, description, distance, location, targetLat, targetLong, employee, country);
try {
return dealRepository.save(deal);
}catch(Exception e){
throw new ServiceException("Could not create a deal: "+deal.toString(), e);
}
}
public Deal updateDeal(Deal d) throws ServiceException {
try{
return dealRepository.save(d);
}catch(Exception e){
throw new ServiceException("Could not update deal at this time: "+d.toString(),e);
}
}
public List<Deal> getAllDealsForEmployeeId(Employee employee) throws ServiceException {
try{
return dealRepository.getAllDealsBy_employeeId(employee.getId());
}catch(Exception e){
throw new ServiceException("Could not get deals for employee: "+employee.getId(), e);
}
}
}
DealRepository looks like this
public interface DealRepository extends CrudRepository<Deal, Long>{
public List<Deal> getDealsBy_country(String country);
public List<Deal> getAllDealsBy_employeeId(Long id);
}
I have a config file that looks like this
#Configuration
#EnableJpaRepositories("com.globati.repository")
#EnableTransactionManagement
public class InfrastructureConfig {
#Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/DatabaseProject");
config.setUsername("awesome");
config.setPassword("database");
return new HikariDataSource(config);
}
// #Bean
// public DataSource derbyDataSource(){
// HikariConfig config = new HikariConfig();
// config.setDriverClassName("jdbc:derby:memory:dataSource");
// config.setJdbcUrl("jdbc:derby://localhost:1527/myDB;create=true");
// config.setUsername("awesome");
// config.setPassword("database");
//
// return new HikariDataSource(config);
//
// }
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.MYSQL);
adapter.setGenerateDdl(true);
return adapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource()); //Get data source config here!
factory.setJpaVendorAdapter(jpaVendorAdapter());
factory.setPackagesToScan("com.globati.model");
return factory;
}
This is how the directory structure of my project looks
DealServiceTest-context.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- this bean will be injected into the OrderServiceTest class -->
<bean id="dealService" class="com.globati.service.DealService">
</bean>
<bean id="employeeService" class="com.globati.service.EmployeeService">
<!-- set properties, etc. -->
</bean>
<!--<bean id="employeeRepository" class="com.globati.repository.EmployeeRepository">-->
<!--</bean>-->
<!-- other beans -->
</beans>
For some reason this #Autowire over DealRepository in DealService is behaving differently than in the spring boot project where this works. Any ideas as to how I can solve this error and do a unit test on the service class would be greatly appreciated!
Try to add a test profile for DataSource as follows,
#Bean
#Profile("test")
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/DatabaseProject");
config.setUsername("awesome");
config.setPassword("database");
return new HikariDataSource(config);
}
Then annotate your test class with #ActiveProfiles("test") as follows,
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/spring/DealServiceTest-context.xml"})
#ActiveProfiles("test")
public class DealServiceTest {
This will make sure that you get a DataSource when you are executing within the test case.

Why is my entitymanager in Playframework 2.5 still null?

I want to use Playframework 2.5 together with Spring and JPA. I found the following template https://github.com/jamesward/play-java-spring where it works perfectly, unfortunately it’s not for Playframework 2.5. So I decided to adapt this template and create my own one for Playframework 2.5. However, my entitymanager in my controller Application is still null. What am I doing wrong? My code looks like the following:
AppConfig.java
package config;
#Configuration
#ComponentScan({"daos","services","controllers","models"})
public class AppConfig {
}
DataConfig.java
package config;
#Configuration
#EnableTransactionManagement
public class DataConfig
{
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPackagesToScan("models");
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
entityManagerFactory.setDataSource(dataSource());
entityManagerFactory.setJpaPropertyMap(new HashMap<String, String>(){{
put("hibernate.hbm2ddl.auto", "create-drop");
put("hibernate.dialect","org.hibernate.dialect.PostgreSQLDialect");
}});
entityManagerFactory.afterPropertiesSet();
return entityManagerFactory.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager(entityManagerFactory());
return transactionManager;
}
#Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
return dataSource;
}
}
Global.java
package config;
#Singleton
#Configuration
public class Global
{
private ConfigurableApplicationContext context;
#Inject
public Global(ApplicationLifecycle lifecyle)
{
this.context = new AnnotationConfigApplicationContext(AppConfig.class, DataConfig.class);
lifecyle.addStopHook(()-> {
context.close();
return CompletableFuture.completedFuture(null);
});
}
}
Application.java
package controllers;
#Controller
#Transactional
#Component
public class Application extends play.mvc.Controller
{
#PersistenceContext
private EntityManager em;
#Transactional
public Result index()
{
System.out.println("******************* EM " + this.em +" *************************");
return ok(index.render());
}
}
Thank you for your help!

Spring JPA and LazyInitializationException

I'm using spring-jpa. I have 2 tests.
#Test
#Transactional
public void testFindAll() {
List<Engine> eList = engineService.findAll();
Engine e = eList.get(0); //this engine id=3
List<Translation> tList = e.getTranslations();
for(Translation t : tList) {
...
}
}
This method fails with this exception:
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: xxx.Engine.translations, could not initialize
proxy - no Session
However, this method works just fine:
#Test
#Transactional
public void testFindOne() {
Engine e = engineService.findOne(3);
List<Translation> tList = e.getTranslations();
for(Translation t : tList) {
...
}
}
Why translation list is successfully loaded in one case, but not in another?
EDIT: service/repo code:
public interface EngineRepository extends JpaRepository<Engine, Integer>
{
}
.
#Service
#Transactional
public class EngineService
{
#Autowired
private EngineRepository engineRepository;
public List<Engine> findAll()
{
return engineRepository.findAll();
}
public Engine findOne(Integer engId)
{
return engineRepository.findOne(engId);
}
}
.
public class Engine implements Serializable {
...
#OneToMany
#JoinColumn(name="ID", referencedColumnName="TRAN_ID", insertable=false, updatable=false, nullable=true)
#LazyCollection(LazyCollectionOption.EXTRA)
private List<Translation> translations;
...
}
Config:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"xxx.dao"})
#ComponentScan(basePackages = {"xxx.dao", "xxx.service", "xxx.bean"})
#PropertySource("classpath:application.properties")
public class SpringDataConfig {
#Autowired
private Environment environment;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(environment.getProperty("db.url"));
dataSource.setDriverClassName(environment.getProperty("db.driverClass"));
dataSource.setUsername(environment.getProperty("db.username"));
dataSource.setPassword(environment.getProperty("db.password"));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL);
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getProperty("hibernate.showSQL"));
properties.put("hibernate.format_sql", environment.getProperty("hibernate.formatSQL"));
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan("xxx.model");
entityManagerFactoryBean.setJpaVendorAdapter(hibernateJpaVendorAdapter);
entityManagerFactoryBean.setJpaProperties(properties);
return entityManagerFactoryBean;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
I think the problem here is that the session gets closed after the first line in the first case. You should check out the JpaRepository implementation of findAll().
Integration Testing with Spring
It seems you're failing to provide a Spring Context within your TestCase, what that means? The #Transactional is being ignored. Therefore you end up with the closed session exception, because there is no transaction.
Take a look how to configure a TestCase with a Spring Context here
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from AppConfig and TestConfig
#ContextConfiguration(classes = {AppConfig.class, TestConfig.class})
public class MyTest {
#Autowired
EngineService engineService;
#Test
#Transactional
public void testFindOne() {}
#Test
#Transactional
public void testFindAll() {}
}

CrudRepository save method not returning updated entity

I'm working with Spring and in it spring-data-jpa:1.7.0.RELEASE and hibernate-jpa-2.1-api:1.0.0.Final. My database is a MySQL. In a integration test, when I save an entity, the entityRepository.save() method does not return a update version of it (with the auto-incremented id populated for example), thus I can see it is in the database.
Here's my configuration class:
//#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "core.repository")
#Configuration
#PropertySource("classpath:application.properties")
public class JPAConfiguration {
#Autowired
private Environment env;
private
#Value("${db.driverClassName}")
String driverClassName;
private
#Value("${db.url}")
String url;
private
#Value("${db.username}")
String user;
private
#Value("${db.password}")
String password;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setDatabasePlatform(env.getProperty("hibernate.dialect"));
final LocalContainerEntityManagerFactoryBean em =
new LocalContainerEntityManagerFactoryBean();
em.setJpaVendorAdapter(vendorAdapter);
em.setPackagesToScan(new String[]{"core.persistence"});
em.setDataSource(dataSource());
em.afterPropertiesSet();
return em;
}
#Bean
public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public JpaTransactionManager transactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
Here's my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {JPAConfiguration.class})
//#Transactional
//#TransactionConfiguration(defaultRollback = true)
public class AgencyRepositoryIntegrationTests {
#Autowired
AgencyRepository agencyRepository;
#Test
public void testThatAgencyIsSavedIntoRepoWorks() throws Exception {
Agency c = DomainTestData.getAgency();
Agency d = agencyRepository.save(c);
// here d equals to c but both have id 0.
Collection<Agency> results = Lists.newArrayList(agencyRepository.findAll());
//here results != null and it does contain the agency object.
assertNotNull(results);
}
}
I end up commenting the Transactional annotations (both in the test and in the configuration class) due to research other questions in stackoverflow, but it did not work.
Thanks
Try to uncomment your #Transactional annotation and run. I can't see any visible problem in your snippet but there is something inside me (and it's not hunger) saying that your transaction demarcation is not right. I had similar problems like this before and it was just #transaction annotations missing or transaction misconfiguration.
Use saveAndFlush method to make sure that the entity is in the database.
Make sure to add #Transactional to your methods if a NoTransactionException appears.

Categories

Resources