java - Hibernate异常: Could not obtain transaction-synchronized Session for current thread although I invoke transactional method

标签 java spring hibernate spring-transactions transactional

我有以下服务:

@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()方法移至另一个服务或将自引用注入(inject)到您的服务。

您可能会发现像您这样的调用对某些人有效。这是因为他们使用 AspectJ 代理而不是基于 Spring 代理的 AOP。要了解 Spring AOP 的工作原理,请阅读“代理模式”。

AspectJ 在编译时使用字节码操作,因此它只是在实际方法周围添加一些代码,并且在运行时它的工作方式与普通对象调用一样好。

如何注入(inject)代理的示例(仅当 CompanyService 定义为单例而不是原型(prototype)时才有效):

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

关于java - Hibernate异常: Could not obtain transaction-synchronized Session for current thread although I invoke transactional method,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33594784/

相关文章:

java - XSLT 扩展 : I am trying to use a custom java function in XSLT but am not able to pin point the mistake

spring - Grails:错误org.springframework.boot.SpringApplication-应用启动失败

java - Thymeleaf Map<String, Object> 迭代和映射键中的非英语字符

java - 当我们使用 Spring Autowiring DAO 时,如何在我的单元测试中模拟出各种结果集?

java - 错误 : Cannot add or update a child row: a foreign key constraint fails in hibernate using mysql

java - Eclipse,通过 setter 初始化对象的简短方法

Java - 使用 Object 参数实现接口(interface)

java - 从 Velocity 模板链接到 css 文件

database - 我的 H2/C3PO/Hibernate 设置似乎没有保留准备好的语句?

java - Hibernate工具,auto cascade all