Dmitry Korchemkin
Dmitry Korchemkin

Reputation: 298

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

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:

  1. Using <tx:annotation-driven> and pointing to a correct transactionManagerFactory

  2. Removing transaction-type="RESOURCE_LOCAL" from persistence.xml

  3. Adding @Transactional to specific methods in Dao and Service classes.

  4. Checked that entityManagerFactory is only loaded once on server startup

  5. Debugged the server to see if some other entityManager is called. I can clearly see my persistence unit and qualifier in its properties.

  6. 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

Upvotes: 2

Views: 386

Answers (1)

Dmitry Korchemkin
Dmitry Korchemkin

Reputation: 298

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

Upvotes: 1

Related Questions