错误 500:org.hibernate.HibernateException:找不到当前线程的会话

Error 500: org.hibernate.HibernateException: No Session found for current thread

我尝试在我的项目中上传图片时遇到此错误。该项目执行正常,直到它必须有效地将图片上传到数据库(我使用的是 postgresql),但最后一步永远不会奏效。

考虑到以下答案后更新了以下代码。

这是我的控制器(一部分):

@Autowired
private FileUploadImpl fileUploadImpl;

...

@RequestMapping(value = "publish4" ,method = RequestMethod.POST)
public ModelAndView publish4(@Valid @ModelAttribute("fourthPublicationForm") final FourthPublicationForm form, final BindingResult errors,
                             @RequestParam("type") String type, @RequestParam("operation") String operation , @RequestParam CommonsMultipartFile[] fileUpload) {
    if (errors.hasErrors()) {
        //return helloPublish3(form,operation,type);
    }
    System.out.println("operation: "+ operation);
    System.out.println("type: "+ type);
    ps.create(form.getTitle(), form.getAddress(), operation, form.getPrice(), form.getDescription(), 
            type, form.getBedrooms(), form.getBathrooms(), form.getFloorSize(), form.getParking());

    if (fileUpload != null && fileUpload.length > 0) {
        for (CommonsMultipartFile aFile : fileUpload){

            System.out.println("Saving file: " + aFile.getOriginalFilename());

            UploadFile uploadFile = new UploadFile();
            uploadFile.setAddress(form.getAddress());
            uploadFile.setData(aFile.getBytes());
            fileUploadImpl.save(uploadFile);               
        }
    }
    return new ModelAndView("redirect:/hello/home");
}

这是接口中的fileUploadDao:

public interface FileUploadDao {
   void save(UploadFile uploadFile);
}

这是在服务中:

@Service
public class FileUploadImpl {

    @Autowired
    private FileUploadDao fileUploadDao;

    public FileUploadImpl() {
    }

    @Transactional
    public void save(UploadFile uploadFile) {
        fileUploadDao.save(uploadFile);
    }

}

以下的坚持:

@Repository
public class FileUploadDAOImpl implements FileUploadDao {
    @Autowired
    private SessionFactory sessionFactory;

    public FileUploadDAOImpl() {
    }

    public FileUploadDAOImpl(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }


    public void save(UploadFile uploadFile) {
       sessionFactory.getCurrentSession().save(uploadFile);
    }
}

我在 WebConfig.java 中得到了这个(以及其他东西)

@Bean
public LocalSessionFactoryBean sessionFactory() {
   LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
   sessionFactory.setDataSource(dataSource());
   sessionFactory.setPackagesToScan(
       new String[] { "ar.edu.itba.paw" }
   );
   //sessionFactory.setHibernateProperties(hibernateProperties());

   return sessionFactory;
}

@Autowired
@Bean(name = "fileUploadDao")
public FileUploadDao getUserDao(SessionFactory sessionFactory) {
    return new FileUploadDAOImpl(sessionFactory);
}

@Bean(name = "multipartResolver")
public CommonsMultipartResolver getCommonsMultipartResolver() {
    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    multipartResolver.setMaxUploadSize(20971520);   // 20MB
    multipartResolver.setMaxInMemorySize(1048576);  // 1MB
    return multipartResolver;
}

@Bean
@Autowired
public HibernateTransactionManager transactionManager(
    SessionFactory sessionFactory) {
    HibernateTransactionManager txManager = new HibernateTransactionManager();
    txManager.setSessionFactory(sessionFactory);
    return txManager;
}

错误有点多:

org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at ar.edu.itba.paw.persistence.FileUploadDAOImpl.save(FileUploadDAOImpl.java:25)
at ar.edu.itba.paw.webapp.controller.HelloWorldController.publish4(HelloWorldController.java:260)

我看到其他问题的答案是没有使用 "transactional"。我在这里使用该注释,但我不确定它的方式是否 100% 正确。

正如您一开始所说的,您混淆了实际的图层。你仍然可以让它在你的情况下正常工作,但让我们讨论一下你的实现。

  • FileUploadDao 是 DAO 还是服务?
  • FileUploadImpl 似乎您将 @Service@Repository 混淆了, 也许阅读这篇文章可能会对你有所帮助。 Spring Data Repositories , Spring Service Annotation
  • 您已经创建了一个交易方法,save 我无法准确地说出您想要实现的目标。您还自动装配了 FileUploadDaoSessionFactory,尽管您想要实现第一个方法,并且在方法内部您尝试通过首先在存储库上调用 save 来持久化对象两次(那是WhosebugError 从一开始,但你很幸运,因为 Spring 知道自动装配什么)然后你试图在 Hibernate 的 SessionFactory 上第二次调用 save ,这打破了抽象的 JPA 契约。另外,如果您注意到,您发布的日志中的错误来自第二次保存。
  • @Transactional 不打算讨论它是如何工作的,因为您还没有发布整个应用程序配置。但同样,您可以阅读 this 了解更多信息。

因此,根据您分享的示例,我将准备 2 个案例,这可能会帮助您了解背后发生的事情。

  1. 第一个案例,Spring DATA,并不真正关心其底层是 Hibernate 还是其他 JPA 提供程序。

您的 FileUploadImpl 变为:FileUploadService

@Service
public class FileUploadService {

    @Autowired
    private FileUploadDao fileUploadDao;

    public FileUploadService() {
    }

    @Transactional
    public void save(UploadFile uploadFile) {
        fileUploadDao.save(uploadFile);
    }

}

在您的控制器内部,您正在自动装配服务(层),而不是直接 Repository/DAO(层)。没有什么能阻止你,这只是设计问题(如果你仍然不明白这一点,请提出另一个问题)。

你的控制器的一部分

    @Autowired
    private FileUploadService fileUploadService;


    @RequestMapping(value = "publish4" ,method = RequestMethod.POST)
    public ModelAndView publish4(@Valid @ModelAttribute("fourthPublicationForm") final FourthPublicationForm form, final BindingResult errors,
                             @RequestParam("type") String type, @RequestParam("operation") String operation , @RequestParam CommonsMultipartFile[] fileUpload) {
       .........
       fileUploadService.save(uploadFile);   
}
  1. 第二种情况,如果你真的想使用 hibernate 好东西,那么没有任何理由自动装配存储库,只需自己实现这些调用即可。
import org.springframework.stereotype.Component;


@Component
public class FileUploadDao {

    @Autowired
    private SessionFactory sessionFactory;

    public FileUpload save(FileUpload obj) {
        return sessionFactory.getCurrentSession().save(obj);
    }

    public FileUpload merge(FileUpload obj) {
        return sessionFactory.getCurrentSession().merge(obj);
    }

    ..... delete / update / or custom queries(SQL/JPQL/HQL) can be placed here

}</pre>

<p>你的服务只是公开了那些方法,检查差异,我在这个层上应用了 <code>@Transactional 注释(你可以再次将它放在 DAO 层中,但正如我所说,这是一个设计问题).

@Service
public class FileUploadService {

    @Autowired
    private FileUploadDao fileUploadDao;

    public FileUploadService() {
    }

    @Transactional
    public UploadFile save(UploadFile uploadFile) {
        fileUploadDao.save(uploadFile);
    }

    @Transactional
    public UploadFile merge(UploadFile uploadFile) {
        fileUploadDao.merge(uploadFile);
    }

    ....rest of the methods you want to expose , or combinations of mulitple DAOs

}

您的控制器保持不变,这就是您需要层的实际原因。

首先从 FileUploadDAOImpl 中删除 @Transactional

相应地更改基础包,

sessionFactory.setPackagesToScan(
   new String[] { "base.package.to.scan" } 
);

base.package.to.scan 似乎是无效的基础包命名,将其更改为 ar.edu.itba.paw.

您需要一个事务管理器才能使用 @Transactional。将其添加到 WebConfig

@Bean
@Autowired
public HibernateTransactionManager transactionManager(
    SessionFactory sessionFactory) {
    HibernateTransactionManager txManager = new HibernateTransactionManager();
    txManager.setSessionFactory(sessionFactory());
    return txManager;
}

这可能会使此代码正常工作,试一试。

更新:还要确保 WebConfig class、

上存在以下注释
@Configuration
@ComponentScan({"ar.edu.itba.paw"})
@EnableTransactionManagement(mode = AdviceMode.PROXY)
public class WebConfig {
    // code
}