#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. Можете ли вы подтвердить, что TransactionSynchronizationManager.isActualTransactionActive() возвращает true в блоке @Transactional?
2. Кроме того, есть ли у вас причина использовать аннотацию @Transactional с этим методом? Я считаю, что в этом нет необходимости
3.
@Transactional
Аннотация к методу контроллера RESTgetOwnTeam()
была там, потому что я думал, что тогда пользовательская модель и лениво загруженная командная модель будут частью этого одного действия. Очевидно, что это предположение было неверным.
Ответ №1:
Если вам нужна другая начальная загрузка, вы можете использовать:
- Собственный sql при запросе
- jpql с конструкцией типа join fetch
- Граф сущностей (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);