Перевести в спящий режим ОТЛОЖЕННУЮ загрузку и пользовательские данные spring

#spring #hibernate #spring-data-jpa

#весна #спящий режим #spring-data-jpa

Вопрос:

У меня есть серверная часть REST без состояния. Так что никаких HTML-просмотров. Просто конечные точки JSON и REST. Аутентификация выполняется с помощью веб-токенов Json. Клиент отправляет JWT в каждом запросе. Мой серверный сервер принимает электронную почту пользователя из темы заявки в этом JWT. Затем он загружает пользовательскую модель из базы данных в class LiquidoUserDetailsService implements UserDetailsService { ...}

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

UserModel.java

 @Entity
@Table(name = "users")
public class UserModel extends BaseModel {
  @NotNull
  @NonNull
  @Column(unique = true)
  public String email;
  @ManyToOne(fetch = FetchType.LAZY)  // only load team info (with all info) if required
  public TeamModel team;

  [...]
}
 

Теперь у меня есть служба, которая должна возвращать команду текущего пользователя:

TeamService.java

 @PreAuthorize(HAS_ROLE_USER)
@RequestMapping("/getOwnTeam")
@Transactional                          // [1] 
public TeamModel getOwnTeam() {
  // Get currently logged in user (that was loaded with info from JWT)
  Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  LiquidoAuthUser authUser = (LiquidoAuthUser)authentication.getPrincipal();
  // LiquidoAuthUser is the Adapter betwen spring's User and my USerModel
  UserModel currentUser = authUser.getLiquidoUserModel()    

  TeamModel team = currentUser.getTeam()     // <=== [2] throws LazyInitializationException

  return team
}
 

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

Моя пользовательская модель загружена, class LiquidoUserDetailsService implements UserDetailsService но это происходит очень рано, в фильтре, когда обрабатывается HTTP-запрос. Поскольку швы @Transaction в моем TeamService классе на тот момент еще не начались.

Затем, когда код входит в getOwnTeam() метод, запускается новая транзакция [1]. Но там я больше не могу лениво загружать команду пользователя. [2]

Как я могу смоделировать своих пользователей и команды, чтобы

  1. Данные команды загружаются только тогда, когда это необходимо
  2. Я могу загружать данные вручную, когда это необходимо

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

1. Можете ли вы подтвердить, что TransactionSynchronizationManager.isActualTransactionActive() возвращает true в блоке @Transactional?

2. Кроме того, есть ли у вас причина использовать аннотацию @Transactional с этим методом? Я считаю, что в этом нет необходимости

3. @Transactional Аннотация к методу контроллера REST getOwnTeam() была там, потому что я думал, что тогда пользовательская модель и лениво загруженная командная модель будут частью этого одного действия. Очевидно, что это предположение было неверным.

Ответ №1:

Если вам нужна другая начальная загрузка, вы можете использовать:

  1. Собственный sql при запросе
  2. jpql с конструкцией типа join fetch
  3. Граф сущностей (https://www.baeldung.com/jpa-entity-graph ) Основным преимуществом при использовании такого способа загрузки является один запрос к базе данных. Вы можете прочитать больше https://thorben-janssen.com/lazyinitializationexception /

Ваш объект в отключенном состоянии — это причина исключения LazyInitializationException (вы можете переместить его в другое состояние, чтобы загрузить свой объект), например

 entityManager.merge(deatachedEntity);
 

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

1. Я попробовал использовать метод слияния. Но я получаю сообщение об ошибке: entityManager.merge(currentUser); throws «не удалось инициализировать прокси [org.doogie. ликвидо.модель. TeamModel#22] — нет сеанса» — я сохранил @Transactional аннотацию к методу. Вы можете мне помочь?

2. ааааааааа. Я нашел решение. Вызов EntityManager.merge(UserModel) возвращает объединенный / присоединенный объект. Как только я использую возвращаемое значение, оно работает. currentUser = entityManager.merge(currentUser);