Я делаю API с помощью Spring Boot, и мне никогда не удается инициализировать ленивые коллекции. Единственное решение, которое когда-либо сработало для меня, - изменить его на нетерпеливый, но это не решение.
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "user")
public class UserEntity {
@Id
@GeneratedValue
private Long id;
@Column(unique = true)
String email;
@ElementCollection(fetch = LAZY)
List<String> roles = new ArrayList<>();
public UserEntity(String email) {
this.email = email;
}
}
@Order(Ordered.LOWEST_PRECEDENCE)
@Component
public class AuthFilter extends OncePerRequestFilter {
Logger LOG = LoggerFactory.getLogger(AuthFilter.class);
@Autowired
UserRepository userRepository;
@Override
@Transactional
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null && authentication.isAuthenticated()){
String email = authentication.getName();
UserEntity user = userRepository.findByEmail(email).orElse(new UserEntity(email));
user.getRoles().clear();
user.getRoles().addAll(authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()));
userRepository.save(user);
}
filterChain.doFilter(request, response);
}
}
Это два моих класса. Пользовательский объект и фильтр, который создаст пользователя из объекта аутентификации. Когда вызывается строка user.getRoles().clear();
, я получаю известную ошибку org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ch.ciip.ressources.api.user.UserEntity.roles, could not initialize proxy - no Session
.
Насколько я понимаю, Spring Data создаст прокси для каждого ленивого отношения и будет извлекать данные только после доступа к ним. Но для доступа к нему требуется сеанс, и, очевидно, Spring не может создать сеанс при доступе к прокси, однако у него нет проблем с этим, когда я вызываю сохранение в репозитории.
Что у меня пробовал:
@Transactional
как из org.springframework.transaction.annotation.Transactional
, так и javax.transaction.Transactional
. Но это абсолютно ничего не меняет. - Измените тип выборки на
EAGER
. Этот метод работает, но я не хочу этого делать. - Измените
enable_lazy_load_no_trans=true
на true в файле application.properties. Этот метод не рекомендуется, поэтому я не буду его пробовать. - Вызов метода в ленивой коллекции перед попыткой его изменения. Например, вызов
user.getRoles().size();
для инициализации. Но это не работает. - Вызов
Hibernate.initialize(user.getRoles());
для инициализации коллекции. Но это не работает.
Интересно, почему Spring говорит No Session
, потому что он создает сеанс для userRepository.findByEmail(email)
, а также для userRepository.save(user)
.
Я мог бы создать entitymanager вручную и использовать его для извлечения / сохранения моих объектов. Но это не цель, поскольку я использую Spring Data JPA.