Не удалось инициализировать прокси-сервер — нет сеанса, внутри Spring interceptor

#java #spring #session #lazy-evaluation #interceptor

#java #весна #сеанс #ленивая оценка #перехватчик

Вопрос:

В моей конфигурации есть перехватчик, и я хочу запретить доступ к ресурсам других пользователей. Внутри WebMvcConfig (реализует WebMvcConfigurer) у меня есть:

 @Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new FolderInterceptor(userService, folderService))
            .addPathPatterns(Mapping.FOLDER_MAPPING   "/{id}",
                    Mapping.UPDATE_FOLDER_MAPPING   "/{id}",
                    Mapping.DELETE_FOLDER_MAPPING   "/{id}",
                    Mapping.DOWNLOAD_FOLDER_MAPPING   "/{id}");

}
  

В моем FolderInterceptor у меня есть метод предварительной обработки, получающий доступ к папке и проверяющий ее владельца:

 Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
Long id = Long.valueOf((String) pathVariables.get("id"));

User user = userService.getLoggedAccount();

if (folderService.existsById(id)) {
    Folder folder = folderService.findById(id);

    if (folder.getOwner().getId().equals(user.getId())) {
        return true;
    }
    else {
        response.sendError(403, "Unauthorized");
        return false;
    }
}
else {
    response.sendError(404, "Folder does not exist");
    return false;
}
  

Если я печатаю объект папки, в этой строке появляется та же ошибка.

 org.hibernate.LazyInitializationException: could not initialize proxy.
  

Спасибо за вашу помощь.

Комментарии:

1. Нет tx и, следовательно, нет сеанса / entitymanager и, следовательно, этого исключения. Код, который у вас есть в вашем перехватчике, принадлежит одному транзакционному методу обслуживания. Вместо вызова всех различных методов в вашем перехватчике.

2. Я должен добавить, что мой друг показал мне свой проект, который отлично работает, и я взял его точно такой же метод interceptor и только переименовал объект, который у него был с моим (папка).

3. Тот факт, что он работает в другом проекте, не означает, что он работает автоматически. У вас другая настройка (вероятно, нет открытого сеанса / entitymanager в поле зрения), которая у него была. У него не было отложенной загрузки, у вас, вероятно, есть. Независимо от всего этого, в идеале это должно быть в одном транзакционном методе, который решит проблему. Или измените свою объектную модель, чтобы не использовать отложенную загрузку (но это может повлиять на другие части вашего приложения).

4. В моей модели нет отложенной загрузки, я убедился в этом. И помимо версий разных зависимостей, наши два проекта очень похожи по структуре. Если это не из-за версии, я не вижу, что это такое.

5. get никогда не попадает в базу данных и всегда возвращается напрямую с помощью ЛЕНИВОГО прокси. Таким образом, даже если фактический объект не существует, он выдаст вам a Folder и приведет к проблемам, как только вы попытаетесь вызвать на нем getters / setters, которые затем отправятся в базу данных. Поиск будет запрашивать базу данных и возвращать фактическое Folder или null , когда не найдено. Следовательно, вы используете отложенную загрузку…

Ответ №1:

Вы извлекаете Folder объект, скорее всего, без какой-либо выборки зависимостей в рамках одной транзакции здесь:

 Folder folder = folderService.findById(id);
  

Затем, когда вы пытаетесь получить доступ к folder.getOwner() , зависимость владельца не была выбрана, и поставщик сохраняемости пытается лениво загрузить ее из базы данных:

 if (folder.getOwner().getId().equals(user.getId())) {
    return true;
}
  

Проблема в том, что folder он выходит за рамки транзакции и является отдельным объектом.

Я бы предложил выполнить выборку Owner внутри folderService.findById(id) метода или поместить запрос и условие в один и тот же транзакционный метод.

Ответ №2:

Я использовал метод getOne для извлечения моей папки по идентификатору в моей службе. Теперь с помощью folderRepository.findById(id) и это работает на данный момент:

 public Folder findById(Long id) {

    Optional<Folder> folder = folderRepository.findById(id);

    if (!folder.isPresent())
        return null;

    return folder.get();
}