HibernateException:尽管我调用事务方法,但无法为当前线程获取事务同步会话
HibernateException: Could not obtain transaction-synchronized Session for current thread although I invoke transactional method
我有以下服务:
@Service
public class CompanyServiceImpl implements CompanyService {
@PostConstruct
public void init() {
this.refreshStopJobs();
}
@Transactional(readOnly = true)
@Override
public void refreshStopJobs() {
companyDao.getCompanysByStatus(CampaignStatus.START).forEach(this::refreshStopJob);
}
}
和以下 dao:
@SuppressWarnings("unchecked")
@Override
public List<Campaign> getCompanysByStatus(CampaignStatus campaignStatus) {
Criteria criteria = createCriteriaForGettingList(null, campaignStatus);
return criteria.list();
}
如果我 运行 我的应用程序,我会看到以下日志:
2015-11-08 17:54:04.601:WARN:oejw.WebAppContext:main: Failed startup of context o.e.j.m.p.JettyWebAppContext@48e4fba9{/,file:/D:/freelance/marcproject/src/main/webapp/,STARTING}{file:/D:/freelance/marcproject/src/main/webapp/}
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'companyServiceImpl': Invocation of init method failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
.....
Caused by:
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at com.terminal.dao.impl.CompanyDaoImpl.createCriteriaForGettingList(CompanyDaoImpl.java:77)
at com.terminal.dao.impl.CompanyDaoImpl.getCompanysByStatus(CompanyDaoImpl.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:201)
at com.sun.proxy.$Proxy80.getCompanysByStatus(Unknown Source)
at com.terminal.service.impl.CompanyServiceImpl.refreshStopJobs(CompanyServiceImpl.java:319)
at com.terminal.service.impl.CompanyServiceImpl.init(CompanyServiceImpl.java:313)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
如果将 dao 方法 getCompanysByStatus
标记为 @Transactional
- 应用程序可以正常启动。
但我不明白为什么。因为我已经在服务方法refreshStopJobs
中开始了交易
这是因为您没有通过 Spring 代理调用 refreshStopJobs() 而是直接通过它调用。您可以通过观察堆栈跟踪清楚地看到这一点。在第一种情况下,您不会看到围绕您的方法调用的事务方面。
如果将@Transactional 移至dao,这将起作用,但在DAO 层中使用@Transactional 被认为是一种不好的做法。
另一个解决方案是将 refreshStopJobs() 方法移动到另一个服务或将自我引用注入您的服务。
您可能会看到像您这样的调用对某些人有效。这是因为他们使用 AspectJ 代理而不是 spring 基于代理的 AOP。要了解 Spring AOP 的工作原理,请阅读 "proxy pattern".
AspectJ 在编译时使用字节码操作,因此它只是在实际方法周围添加一些代码,在运行时它与普通对象调用一样好用。
如何注入代理的示例(仅在 CompanyService 定义为单例而非原型时有效):
@Service
public class CompanyServiceImpl implements CompanyService, BeanNameAware {
private String name;
private CompanyService proxy;
@Autowired
private ApplicationContext applicationContext;
@Override
public void setBeanName(String name) {
this.name = name;
}
@PostConstruct
public void postConstruct() {
proxy = (CompanyService)applicationContext.getBean(name);
proxy.refreshStopJobs();
}
@Transactional(readOnly = true)
@Override
public void refreshStopJobs() {
companyDao.getCompanysByStatus(CampaignStatus.START).forEach(this::refreshStopJob);
}
}
静态获取代理:
@Service
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
public static <T> T getProxy (Class<T> proxyClass){
return (T) context.getBean(proxyClass);
}
}
请记住,此服务必须在 CompanyService 之前初始化。
我有以下服务:
@Service
public class CompanyServiceImpl implements CompanyService {
@PostConstruct
public void init() {
this.refreshStopJobs();
}
@Transactional(readOnly = true)
@Override
public void refreshStopJobs() {
companyDao.getCompanysByStatus(CampaignStatus.START).forEach(this::refreshStopJob);
}
}
和以下 dao:
@SuppressWarnings("unchecked")
@Override
public List<Campaign> getCompanysByStatus(CampaignStatus campaignStatus) {
Criteria criteria = createCriteriaForGettingList(null, campaignStatus);
return criteria.list();
}
如果我 运行 我的应用程序,我会看到以下日志:
2015-11-08 17:54:04.601:WARN:oejw.WebAppContext:main: Failed startup of context o.e.j.m.p.JettyWebAppContext@48e4fba9{/,file:/D:/freelance/marcproject/src/main/webapp/,STARTING}{file:/D:/freelance/marcproject/src/main/webapp/}
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'companyServiceImpl': Invocation of init method failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
.....
Caused by:
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at com.terminal.dao.impl.CompanyDaoImpl.createCriteriaForGettingList(CompanyDaoImpl.java:77)
at com.terminal.dao.impl.CompanyDaoImpl.getCompanysByStatus(CompanyDaoImpl.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:201)
at com.sun.proxy.$Proxy80.getCompanysByStatus(Unknown Source)
at com.terminal.service.impl.CompanyServiceImpl.refreshStopJobs(CompanyServiceImpl.java:319)
at com.terminal.service.impl.CompanyServiceImpl.init(CompanyServiceImpl.java:313)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
如果将 dao 方法 getCompanysByStatus
标记为 @Transactional
- 应用程序可以正常启动。
但我不明白为什么。因为我已经在服务方法refreshStopJobs
这是因为您没有通过 Spring 代理调用 refreshStopJobs() 而是直接通过它调用。您可以通过观察堆栈跟踪清楚地看到这一点。在第一种情况下,您不会看到围绕您的方法调用的事务方面。
如果将@Transactional 移至dao,这将起作用,但在DAO 层中使用@Transactional 被认为是一种不好的做法。
另一个解决方案是将 refreshStopJobs() 方法移动到另一个服务或将自我引用注入您的服务。
您可能会看到像您这样的调用对某些人有效。这是因为他们使用 AspectJ 代理而不是 spring 基于代理的 AOP。要了解 Spring AOP 的工作原理,请阅读 "proxy pattern".
AspectJ 在编译时使用字节码操作,因此它只是在实际方法周围添加一些代码,在运行时它与普通对象调用一样好用。
如何注入代理的示例(仅在 CompanyService 定义为单例而非原型时有效):
@Service
public class CompanyServiceImpl implements CompanyService, BeanNameAware {
private String name;
private CompanyService proxy;
@Autowired
private ApplicationContext applicationContext;
@Override
public void setBeanName(String name) {
this.name = name;
}
@PostConstruct
public void postConstruct() {
proxy = (CompanyService)applicationContext.getBean(name);
proxy.refreshStopJobs();
}
@Transactional(readOnly = true)
@Override
public void refreshStopJobs() {
companyDao.getCompanysByStatus(CampaignStatus.START).forEach(this::refreshStopJob);
}
}
静态获取代理:
@Service
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
public static <T> T getProxy (Class<T> proxyClass){
return (T) context.getBean(proxyClass);
}
}
请记住,此服务必须在 CompanyService 之前初始化。