Spring Data не удалось лениво инициализировать коллекцию ролей, не удалось инициализировать прокси - нет сеанса - PullRequest
0 голосов
/ 05 августа 2020

Я делаю 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.

Ответы [ 2 ]

3 голосов
/ 05 августа 2020

Сделайте метод public, например:

@Override
@Transactional
public 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);
}
2 голосов
/ 05 августа 2020

Создание отдельного класса обслуживания и внедрение его в этот фильтр - это обходной путь, который я могу предложить.

@Service
class UserService {
    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void findUserByEmailAndAddRoles(String email, Set<String> authorities) {
        UserEntity user = userRepository.findByEmail(email)
                   .orElse(new UserEntity(email));
        user.getRoles().clear();
        user.getRoles().addAll(authorities);
        userRepository.save(user);
    }
}

Затем используйте его в своем фильтре

@Override
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();
        Set<String> authorities = authentication.getAuthorities().stream()
           .map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
        userService.findUserByEmailAndAddRoles(email, authorities);
    }
    filterChain.doFilter(request, response);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...