August 15, 2021

Spring Data, Hibernate: Пересечение множеств и запрос во вложенные параметры

Есть сущности (показаны базовые поля):

ApplicationUser

@Entity
@Table(name = "application_user")
@Data
public class ApplicationUser {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    private Long id;

    @Column(name = "is_active")
    private Boolean isActive;

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

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(unique = true)
    private User user;

}

User

@Entity
@Table(name = "sys_user")
@Data
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    private Long id;

    @NotNull
    @Email
    @Size(min = 5, max = 254)
    @Column(name = "email", length = 254, unique = true, nullable = false)
    private String email;

    @ManyToMany
    @JoinTable(
            name = "sys_user_authority",
            joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") },
            inverseJoinColumns = { @JoinColumn(name = "authority_name", referencedColumnName = "name") }
    )
    @BatchSize(size = 20)
    private Set<Authority> authorities = new HashSet<>();

}

Authority

@Entity
@Table(name = "authority")
@Data
public class Authority {

    @NotNull
    @Size(max = 50)
    @Id
    @Column(length = 50)
    private String name;

}

Требуется вывести всех активных пользователей (ApplicationUser) в режиме Pageable, которые принадлежат определённым ролям (Authority).

Первый вариант:

Page<ApplicationUser> findAllByIsActiveIsTrueAndUser_AuthoritiesIsIn(Set<Authority> authorities, Pageable pageable);

Работает, но выводятся дубли (по кол-ву совпавших у пользователя ролей).

Второй вариант:

@Query(
        value = "select distinct applicationUser from ApplicationUser applicationUser left join fetch applicationUser.user, Authority a " +
                "where a in (:authorities) and a in elements(applicationUser.user.authorities) and applicationUser.isActive = true",
        countQuery = "select distinct applicationUser from ApplicationUser applicationUser, Authority a " +
                "where a in (:authorities) and a in elements(applicationUser.user.authorities) and applicationUser.isActive = true"
)
Page<ApplicationUser> findAllByIsActiveIsTrueAndUser_AuthoritiesIsIn(Set<Authority> authorities, Pageable pageable);

Всё работает. Для сравнения множеств в раздел from дополнительно указываем Authority, а в where сравниваем authority с искомым списком ролей, а затем с ролями пользователя и делаем distinct.

Вариант findDistinctByIsActiveIsTrueAndUser_AuthoritiesIsIn для вложенных запросов не сработал, но должен работать для обычных запросов.