2015-11-08 77 views
1

我有followig服务:HibernateException的:无法获得交易同步会话当前线程的,虽然我调用事务方法

@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); 
    } 
} 

并按照道:

@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 [email protected]{/,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) 

if mark dao method getCompanysByStatus as @Transactional - 应用程序启动正常。 但我不明白为什么。因为我已经开始了服务方式的交易refreshStopJobs

+0

[Spring @Transaction方法调用可能出现同一个类中的方法调用,不起作用?](http://stackoverflow.com/questions/3423972/spring-transaction-method-call-by-the-方法在同一类别不禾窝) – gstackoverflow

回答

3

这是因为您没有通过Spring代理调用refreshStopJobs(),而是直接通过this调用。您可以通过观察堆栈跟踪来清楚地看到这一点。在第一种情况下,你不会看到方法调用的事务方面。

如果您将@Transactional移动到dao,这将起作用,但在DAO层中使用@Transactional被认为是不好的做法。

另一种解决方案是将refreshStopJobs()方法移动到另一个服务或注入自我引用到您的服务。

您可能会看到调用像你工作的一些人。这是因为他们使用AspectJ代理而不是基于Spring代理的AOP。了解Spring AOP作品如何阅读“代理模式”。

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之前初始化。

+0

我相信这是非常糟糕的,如果我的业务bean依赖于applicationContext。 – gstackoverflow

+0

这只是一个例子,如何证明问题的原因。你如何实现这个取决于你。有些人创建BeanPostPorcessors,有些创建实用工具类来查找代理,您可能会在SO上找到许多工作解决方案。 –

+0

你可以显示**实用程序类的示例来查找代理**吗? – gstackoverflow

相关问题