休眠 Spring @Transaction 不起作用
Hibernate Spring @Transaction doesn't work
我有以下内容:
@Service
public class AsyncMarketService {
@Inject
IdentifierManager identifierManager;
@Transactional
public void getProducts() {
identifierManager.getTitleForIdentifier();
}
}
正在从 @Async 方法调用服务方法。
我在日志中看到交易正在打开:
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]
但是,我仍然得到一个错误:
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)
当我尝试从该方法 (identifierManager.getTitleForIdentifier();
) 调用 DAO 以通过 id 检索某些内容时。
怎么了?
已编辑
配置 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;
}
}
您可以尝试的一件事是删除 @Transactional 注释并自己手动打开一个会话(不确定是否有更好的方法...)
@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();
}
}
}
如果您发现自己不止一次这样做,也许可以编写另一个组件,其中包含一个采用闭包或其他方法的方法。
如果我在接下来的几分钟内找到更优雅的方式,我会及时通知您。
尝试将@Transactional 移动到调用异步方法。事务不会通过调用层次结构从一个 Spring 组件传播到另一个组件,因为异步方法正在由任务执行程序安排并在稍后时间执行。
我有以下内容:
@Service
public class AsyncMarketService {
@Inject
IdentifierManager identifierManager;
@Transactional
public void getProducts() {
identifierManager.getTitleForIdentifier();
}
}
正在从 @Async 方法调用服务方法。
我在日志中看到交易正在打开:
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]
但是,我仍然得到一个错误:
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)
当我尝试从该方法 (identifierManager.getTitleForIdentifier();
) 调用 DAO 以通过 id 检索某些内容时。
怎么了?
已编辑
配置 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;
}
}
您可以尝试的一件事是删除 @Transactional 注释并自己手动打开一个会话(不确定是否有更好的方法...)
@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();
}
}
}
如果您发现自己不止一次这样做,也许可以编写另一个组件,其中包含一个采用闭包或其他方法的方法。
如果我在接下来的几分钟内找到更优雅的方式,我会及时通知您。
尝试将@Transactional 移动到调用异步方法。事务不会通过调用层次结构从一个 Spring 组件传播到另一个组件,因为异步方法正在由任务执行程序安排并在稍后时间执行。