2015-11-08 79 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

相關問題