Nano Hash - криптовалюты, майнинг, программирование

как обратиться к org.hibernate.LazyInitializationException: не удалось инициализировать прокси-сервер - нет сеанса с контроллерами Spring, использующими преобразователи

Я нахожусь в процессе переноса проекта из:

  • Весна 4.2.1 -> 5.0.0
  • Spring Data Гослинг -> Кей
  • Гибернация 4.3.8 -> 5.8.0

И я получаю «org.hibernate.LazyInitializationException: не удалось инициализировать прокси — нет сеанса» при доступе к объекту, поступающему из моей базы данных в методе контроллера.

Вот урезанная версия моего кода:

// CustomUser.java
@Entity
@Access(AccessType.FIELD)
@Table(name = "users")
public class CustomUser implements Serializable {
    ...
    @Id
    @GeneratedValue//details omitted
    @GenericGenerator//details omitted
    @Column(name = "id", insertable = true, updatable = true, unique = true, nullable = false)
    private Long id;

    @Column(name = "name")
    private String name;

    public String getName() { return name; }
}

// UserController.java
@RequestMapping(value = "/user/{userId}/", method = RequestMethod.GET)
public String showUser(@PathVariable("userId") CustomUser user) {
    System.out.println("user name is [" + user.getName() + "]");
    return "someTemplate";
}

// UserService.java
@Service
public class UserServiceImpl implements UserService {
    @Autowired UserRepository userRepository;

    @Override
    public User findUserById(Long userId) {
        return userRepository.getOne(userId);
    }
}

// UserRepository.java
public interface UserRepository extends JpaRepository<CustomUser, Long> { }

// UserConverter.java
@Component
public class UserConverter implements Converter<String, CustomUser> {
    @Autowired UserService userService;

    @Override
    public CustomUser convert(String userId) {
        CustomUser user = userService.findUserById(SomeUtilClass.parseLong(userId));
        return user;
    }
}

Существует также класс @Configuration WebMvcConfigurerAdapter, который автоматически связывает экземпляр UserConverter и добавляет его в FormatterRegistry.

Перед началом этого обновления я мог нажать: http://server:port/user/123/

и Spring возьмет строку «123», метод UserConverter::convert сработает и попадет в базу данных Postgres, чтобы найти пользователя с этим идентификатором, и я верну объект CustomUser в методе «showUser» моего контроллера.

Но теперь я получаю исключение org.hibernate.LazyInitializationException. Это происходит, когда я пытаюсь распечатать имя пользователя в методе «showUser» или даже просто «println(user)» без доступа к полю.

Большая часть информации, которую я смог найти в результате поиска, предполагает, что это исключение возникает из-за того, что объект имеет лениво загруженную коллекцию подобъектов (например, если бы у моего CustomUser была коллекция объектов Permission или что-то, что сопоставлено с другой базой данных Таблица). Но в данном случае я даже этого не делаю, это просто поле на объекте.

Мое лучшее предположение на данный момент заключается в том, что это связано с тем, что какой-то сеанс гибернации завершается после того, как преобразователь выполнит свою работу, поэтому, вернувшись в контроллер, у меня нет действительного сеанса. (хотя опять же, я не знаю, почему объект CustomUser становится непригодным для использования, я не пытаюсь получить подколлекцию).

Я добавил аннотацию Hibernate «@Proxy(lazy = false)» в свой CustomUser.java, и если я это сделаю, проблема исчезнет. Но я не уверен, что это хорошее решение - по соображениям производительности я действительно не думаю, что хочу идти по пути жадного извлечения ВСЕХ.

Я также пытался аннотировать различные вещи (метод службы, метод контроллера и т. д.) с помощью @Transactional; Я не заставил это работать, но я все еще достаточно новичок в Spring, и, возможно, я пытаюсь это сделать не в том месте или неправильно понимаю, что это должно делать.

Есть ли лучший способ справиться с этим, чем просто «@Proxy(lazy = false)» для всех моих классов Entity?


  • Как вы упомянули, это исключение обычно появляется, когда ваша таблица объединяется с другой. У вас есть другие поля в CustomUser? Я действительно не знаю об изменениях в спящем режиме 5, но я не думаю, что они лениво загружают основные поля объекта. 26.10.2017
  • Да, в CustomUser есть и другие поля, которые будут извлекаться из других таблиц базы данных. Но тот, к которому я пытаюсь получить доступ, поступает прямо из столбца в таблице пользователей, а не из соединения. 26.10.2017

Ответы:


1

Непосредственная проблема возникает из-за использования userRepository.getOne(userId). Это реализовано в SimpleJpaRepository с использованием EntityManager.getReference. Этот метод возвращает только прокси, который содержит только его идентификатор. И только при доступе к свойству они загружаются лениво. Это включает в себя простые свойства, такие как name в вашем случае.

Немедленное исправление состоит в том, чтобы использовать findOne, который должен загружать все активные свойства вашего объекта, которые должны включать простые свойства, поэтому исключение исчезает.

Обратите внимание, что это немного изменит поведение вашего конвертера. Текущая версия не выйдет из строя, если идентификатор не существует в базе данных, потому что она никогда не проверяет. Новый получит пустой необязательный параметр, и вам придется преобразовать его в нуль или создать исключение.

Но существует (или может быть) скрытая более серьезная проблема: сущность по-прежнему отсоединена, потому что в отсутствие явного разграничения транзакций транзакции охватывают только одиночные вызовы репозитория. Поэтому, если вы позже захотите получить доступ к свойствам с отложенной загрузкой, вы можете снова столкнуться с той же проблемой. Я вижу различные варианты для рассмотрения:

  1. Оставьте все как есть, помня, что вы не должны обращаться к лениво загруженным свойствам.

  2. Убедитесь, что транзакция запущена до вызова преобразователей.

  3. Пометьте свои контроллеры @Transactional и загрузите пользователя (снова) в контроллер.

27.10.2017
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..