JPQL ElementCollection: получение всех пользователей, у которых есть хотя бы одна роль списка

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

Определение данных выглядит следующим образом:

@Entity
public class User implements Serializable {
    @Id
    @Column(name = "Id",
            nullable = false)
    private long id;

    @Column(name = "LoginName",
            length = 30,
            unique = true,
            nullable = false)
    private String loginName;

    @ElementCollection
    @Enumerated(EnumType.STRING)
    @CollectionTable(name = "Role",
                     joinColumns = @JoinColumn(name = "Id"),
                     uniqueConstraints = @UniqueConstraint(columnNames = {"Id", "Role"}))
    @Column(name = "Role")
    private Set<RoleType> roles = new HashSet<RoleType>();
}


public enum RoleType {
    GUEST,
    ACCOUNTING,
    ADMIN,
    OPERATOR;
}

Теперь мне нужны все пользователи, у которых есть хотя бы одна роль из списка параметров. Список может быть динамическим. Я попробовал следующий код:

String sql = "SELECT DISTINCT u FROM User u JOIN u.roles AS r WHERE r IN :filteredRoles";
Query query = entityManager.createQuery(sql);
List<RoleType> filteredRoles = new ArrayList<RoleType>();
filteredRoles.add(RoleType.ADMIN);
filteredRoles.add(RoleType.ACCOUNTING);
query.setParameter("filteredRoles", filteredRoles);
List<User> result = (List<User>) query.getResultList();

и получите следующее исключение:

Exception [EclipseLink-6078] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.QueryException
Exception Description: The class of the argument for the object comparison is incorrect. 
Expression: [null] 
Mapping: [org.eclipse.persistence.mappings.DirectCollectionMapping[roles]] 
Argument: [[ADMIN, ACCOUNTING]]
Query: ReadAllQuery(referenceClass=User sql="SELECT DISTINCT t0.Id FROM User t0, Role t1 WHERE ((t1.Role IN ?) AND (t1.Id = t0.Id))")

Я попробовал несколько вариантов JPQL, но безуспешно. Надеюсь получить полезные советы.


person gipsyrg    schedule 23.09.2012    source источник


Ответы (2)


Можете попробовать версию 2.4, похоже, что-то исправлено.

person James    schedule 24.09.2012

Я не уверен, это будет лучшее решение. Я надеюсь, что это будет полезно для вас;

public enum Role {
    AAA, BBB, CCC, DDD;
}

@Entity
public class Student implements Serializable {
    @Id
    private String id;
    private String name;
    @ElementCollection
    @Enumerated(EnumType.STRING)
    @CollectionTable(name = "Role", joinColumns = @JoinColumn(name = "Id"),
                     uniqueConstraints = @UniqueConstraint(columnNames = { "Id", "Role" }))
    @Column(name = "Role")
    private List<Role> roleList;

    public Student() {
    }

    //gettter and setter
}

/**
    Assume :
    ==================          ================
    Student Table               Role Table
    ==================          ================
    R001    Student 1           R001    AAA
    R002    Student 2           R002    BBB
    R003    Student 3           R002    CCC
    R004    Student 4           R004    DDD
                                R001    BBB
                                R002    CCC

*/
public class TestCase {
    public static <T> Set<T> intersection(Set<T> setA, Set<T> setB) {
        Set<T> tmp = new TreeSet<T>();
        for (T x : setA)
          if (setB.contains(x))
            tmp.add(x);
        return tmp;
      }

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("JPA");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        Set<Role> paramRoles = new HashSet<Role>();
        paramRoles.add(Role.BBB);
        paramRoles.add(Role.CCC);

        Query q = em.createQuery("SELECT s FROM Student s");
        List<Student> studentList = q.getResultList();
        for (Student s : studentList) {
            Set<Role> result = TestCase.intersection(paramRoles, new HashSet<Role>(s.getRoleList()));
            if (!result.isEmpty()) {
                System.out.println(s.getName());
            }
        }
        em.getTransaction().commit();
        em.close();
    }
}

/** Output :

Student 1
Student 2

*/
person Zaw Than oo    schedule 24.09.2012
comment
Как это отвечает на вопрос? Я не понимаю, как может помочь переименование RoleType в Role и использование списка вместо набора. - person JB Nizet; 24.09.2012
comment
Да, спасибо за ваш комментарий. Лучше использовать Set. - person Zaw Than oo; 24.09.2012
comment
Проблема в JPQL, где я получаю исключение: - person gipsyrg; 24.09.2012
comment
Вы видите JPQL выше во фрагменте кода: SELECT DISTINCT u FROM User u JOIN u.roles AS r WHERE r IN :filteredRoles. Когда я вызываю getResultList(), я получаю исключение, которое вы можете видеть выше после фрагмента кода. - person gipsyrg; 24.09.2012
comment
Я вижу, ты пробовал как мой пример? это работа или нет для вас? Хотите решить с помощью JPQL, а не программно? - person Zaw Than oo; 24.09.2012
comment
Я думаю, что это решение является обходным путем, если я не заставлю базу данных делать то, что я от нее ожидаю. Фильтрация — это работа с базой данных, потому что базы данных оптимизированы для этого. - person gipsyrg; 24.09.2012