简体   繁体   中英

Hibernate Spring @Transaction doesn't work

I have the following:

@Service
public class AsyncMarketService {

    @Inject
    IdentifierManager identifierManager;

    @Transactional
    public void getProducts() {
        identifierManager.getTitleForIdentifier();
      }
}

The service method is being called from an @Async method.

I see in the log that the transaction is being opened:

134331 DEBUG [Company-1] org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
134342 DEBUG [Company-1] org.springframework.orm.hibernate4.HibernateTransactionManager - Creating new transaction with name [com.estartup.caller.AsyncMarketService.getProducts]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
134343 DEBUG [Company-1] org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@b9b0793 updates=org.hibernate.engine.spi.ExecutableList@ee6c9e deletions=org.hibernate.engine.spi.ExecutableList@4230dd40 orphanRemovals=org.hibernate.engine.spi.ExecutableList@5d5e00b collectionCreations=org.hibernate.engine.spi.ExecutableList@56a1c8bd collectionRemovals=org.hibernate.engine.spi.ExecutableList@25d8d46b collectionUpdates=org.hibernate.engine.spi.ExecutableList@61c6c74d collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@4b39a6c6 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
134346 DEBUG [Company-1] org.springframework.orm.hibernate4.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@b9b0793 updates=org.hibernate.engine.spi.ExecutableList@ee6c9e deletions=org.hibernate.engine.spi.ExecutableList@4230dd40 orphanRemovals=org.hibernate.engine.spi.ExecutableList@5d5e00b collectionCreations=org.hibernate.engine.spi.ExecutableList@56a1c8bd collectionRemovals=org.hibernate.engine.spi.ExecutableList@25d8d46b collectionUpdates=org.hibernate.engine.spi.ExecutableList@61c6c74d collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@4b39a6c6 unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
134346 DEBUG [Company-1] org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/Company?zeroDateTimeBehavior=convertToNull]
134377 DEBUG [Company-1] org.springframework.orm.hibernate4.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [com.mysql.jdbc.JDBC4Connection@4d9068ae]

However, I still get an error:

Caused by: java.util.concurrent.ExecutionException: org.hibernate.HibernateException: get is not valid without active transaction
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)

when I am trying to call from that method ( identifierManager.getTitleForIdentifier(); ) the DAO to retrieve something by id.

What is wrong?

EDITED

Configuration Bean:

@Configuration
@ComponentScan({ "com.estartup" })
@PropertySource("classpath:jdbc.properties")
@EnableScheduling
@EnableTransactionManagement
public class PersistenceConfig {

    @Autowired
    Environment env;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(env.getProperty("connection.url"), env.getProperty("connection.username"),
                env.getProperty("connection.password"));
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return driverManagerDataSource;
    }

    public PersistenceConfig() {
        super();
    }

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean factoryBean = null;
        try {
            factoryBean = createBaseSessionFactory();
            factoryBean.getHibernateProperties().setProperty("hibernate.current_session_context_class", "thread");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factoryBean;
    }

    private LocalSessionFactoryBean createBaseSessionFactory() throws IOException {
        LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
        Properties pp = new Properties();
        pp.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
        pp.setProperty("hibernate.max_fetch_depth", "3");
        pp.setProperty("hibernate.current_session_context_class", "org.springframework.orm.hibernate4.SpringSessionContext");
        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan(new String[] { "com.estartup.*" });
        factoryBean.setHibernateProperties(pp);
        return factoryBean;
    }

    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
        HibernateTransactionManager txManager = new HibernateTransactionManager();
        txManager.setSessionFactory(sessionFactory);
        return txManager;
    }
}

One thing you could try is to drop the @Transactional annotation and manually open a session yourself (not sure if there is a better way...)

@Service
public class AsyncMarketService {

    @Inject
    IdentifierManager identifierManager;

    @Inject
    SessionFactory sessionFactory;

    public void getProducts() {
        Session session = sessionFactory.openSession();
        Transaction tx = null;

        try {
            identifierManager.getTitleForIdentifier();
        } catch(Exception e) {
            if(tx != null) tx.rollback();
        } finally {
            session.close();
        }
    }
}

If you find yourself doing this more than once, maybe write another component that has a method that takes a closure or something.

If I find a more elegant way in the next few minutes, I'll keep you posted.

Try moving the @Transactional to the calling asynchronous method. The transaction does not get propagated through the call hierarchy from one Spring component to the other, because the asynchronous method is being scheduled and executed at a later time by a task executor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM