#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();
}