Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/362.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JPA:如何对实体中的集合字段进行排序?_Java_Hibernate_Sorting_Jpa_Orm - Fatal编程技术网

Java JPA:如何对实体中的集合字段进行排序?

Java JPA:如何对实体中的集合字段进行排序?,java,hibernate,sorting,jpa,orm,Java,Hibernate,Sorting,Jpa,Orm,我使用的是Spring3.2.11.RELEASE、Hibernate4.3.6.Final和JPA2.1。我有以下实体和以下字段 @Entity @Table(name = "user") public class User implements Serializable, Comparable<User> { … @ManyToMany @JoinTable(name = "user_organization", joinColumns = { @Join

我使用的是Spring3.2.11.RELEASE、Hibernate4.3.6.Final和JPA2.1。我有以下实体和以下字段

@Entity
@Table(name = "user")
public class User implements Serializable, Comparable<User>
{
    …
    @ManyToMany
    @JoinTable(name = "user_organization", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "ORGANIZATION_ID") })
    @LazyCollection(LazyCollectionOption.FALSE)
    @SortNatural
    private SortedSet<Organization> organizations;
@实体
@表(name=“user”)
公共类用户实现了可序列化、可比较的
{
…
@许多
@JoinTable(name=“user\u organization”,joinColumns={@JoinColumn(name=“user\u ID”)},inverseJoinColumns={@JoinColumn(name=“organization\u ID”)})
@LazyCollection(LazyCollectionOption.FALSE)
@自然的
私人分类数据集组织;
上面,对组织的排序是通过组织的名称字段完成的。当我运行JPA查询检索用户对象时,我想根据与它们关联的组织的有序列表进行排序。我已经尝试了这个

final CriteriaBuilder builder = m_entityManager.getCriteriaBuilder();
final CriteriaQuery<User> criteria = builder.createQuery(User.class);
…
final SetJoin<User, Organization> orgsJoin = orderByRoot.join(User_.organizations, JoinType.LEFT);
orderByExpr = orgsJoin.get(Organization_.name);
criteria.orderBy(builder.asc(orderByExpr);
final CriteriaBuilder=m_entityManager.getCriteriaBuilder();
最终准则查询准则=builder.createQuery(User.class);
…
final SetJoin-orgsJoin=orderByRoot.join(用户\组织,JoinType.LEFT);
orderByExpr=orgsJoin.get(组织名称);
criteria.orderBy(builder.asc(orderByExpr));
但它不适用于用户与多个组织相关联的情况——有时列表中字母顺序较低的组织的用户会在正确的用户之前返回。如果不编写一大堆本机SQL,我如何使用JPA/CriteriaBuilder解决这个问题

编辑:这是我想要的一个例子。我正在寻找不同的用户

  • 用户A有组织机构,“AAA大楼”和“ZZZ大楼”
  • 用户B只有“MMM大厦”的组织机构
  • 用户C拥有组织“CCC大楼”和“VVV大楼”
  • 用户D拥有组织“AAA大楼”和“MMM大楼”

我想要用户D、用户A、用户C和用户B,因为用户A的组织按字母顺序连接比用户C的组织按字母顺序连接低,而用户C的组织按字母顺序连接又比用户B的组织低。

如果要选择不同的用户,则不能使用现有代码,因为它不是明确按哪个组织排序:按第一个、第二个……或最后一个

但有一个解决方案是可行的(考虑到您希望始终按第一个组织进行排序):

  • 添加新属性
    私有字符串组织sString;
    每次更改
    用户
    ,都会将此属性设置到集合中的
    组织
    的串联列表中
  • 然后,您只需调整您的CriteriaQuery以按新属性排序即可

  • 当然,您可以在获取
    用户后对其进行排序,但这可能有一个很大的缺点:如果您(愿意)如果有成千上万的用户,您将需要对其进行分页。然后,按其他字段排序将更加复杂。同时加载许多用户及其关系可能是一个很大的性能瓶颈。如果只有数百个用户,此解决方案肯定会起作用。

    您无法为实体编写此类查询。如果您使用的是本机SQL,您可以按user.id按组织名称分组,但这不是您想要的

    您只需获取加入用户和组织:

    final CriteriaBuilder builder = m_entityManager.getCriteriaBuilder();
    final CriteriaQuery<User> criteria = builder.createQuery(User.class);
    Root<User> root = criteria.from(User.class);
    Fetch<User, Organization> organizations = root.fetch("organizations");
    criteria.select(c);
    TypedQuery<User> query = em.createQuery(criteria);
    List<User> users = query.getResultList();
    
    MySQL 对用户id进行排序后,您只需将用户id提取到
    列表
    ,然后运行第二个JPQL查询以获取用户:

    List<Long> userIds = ...;
    
    TypedQuery<User> q = em.createQuery(
        "select u " +
        "from User u " +
        "where u.id in :userIds "
    , User.class);
    
    q.setParameter("userIds", userIds);
    List<User> users = q.getResultList();
    
    List userid=。。。;
    TypedQuery q=em.createQuery(
    “选择u”+
    “来自用户u”+
    “u.id所在位置:用户id”
    ,User.class);
    q、 setParameter(“userid”,userid);
    List users=q.getResultList();
    
    您必须在获取用户列表后对其进行排序。在进行排序之前,您必须向
    User
    类添加一些更改:

    public class User implements Serializable, Comparable<User>, Comparator<User>
    {
        ...
    
        public int compareTo(User u) {
            return compare(this, u);
        }
    
        public int compare(User u1, User u2) {
            Iterator<Organization> it1 = u1.organizations.iterator();
            Iterator<Organization> it2 = u2.organizations.iterator();
            while (it1.hasNext() && it2.hasNext()) {
                Organization o1 = it1.next();
                Organization o2 = it2.next();
                if(o1.compareTo(o2) != 0) {
                    return o1.compareTo(o2);
                }
            }
            if(it1.hasNext())
                return 1;
            if(it2.hasNext())
                return -1;  
            return 0;
        }
    }
    
    public class Organization implements Comparable<Organization>
    {
        ...
    
        public int compareTo(Organization o) {
            return (this.name).compareTo(o.name);
        }
    }
    
    然后,您可以通过调用以下代码获得已排序的用户列表:

    final CriteriaBuilder builder = m_entityManager.getCriteriaBuilder();
    final CriteriaQuery<User> criteria = builder.createQuery(User.class);
    Root<User> root = criteria.from(User.class);
    Join<User, Organization> organizations = root.fetch("organizations");
    criteria.select(root);
    TypedQuery<User> query = em.createQuery(criteria);
    List<User> users = query.getResultList();
    Collections.sort(users, new User());
    
    final CriteriaBuilder=m_entityManager.getCriteriaBuilder();
    最终准则查询准则=builder.createQuery(User.class);
    Root=criteria.from(User.class);
    Join organizations=root.fetch(“组织”);
    条件。选择(根);
    TypedQuery query=em.createQuery(条件);
    List users=query.getResultList();
    Collections.sort(users,new User());
    
    上面的代码将按第一个组织的名称对用户进行排序。但如果这些名称相等,则将比较第二个组织的名称,依此类推

    编辑:
  • 我不得不承认,我的上述解决方案不利于更多的人 用户
  • 有两个答案建议使用
    string_agg(o.name,,')
    或类似函数进行SQL查询。这些查询不会总是正确排序。让我们看看以下示例:

    • User1:只有组织“AAA”
    • User2:只有组织“AAAA”
    • 用户3:有组织“AAA”和“BBB”
    • 用户4:有组织“AAAA”和“BBB”
  • 使用
    string_agg(o.name,,')对其进行排序后
    我们将收到以下订单:

    • 用户1“AAA”
    • 用户2“AAAA”
    • 用户4“AAAA,BBB”
    • 用户3“AAA,BBB”
    这是不正确的

    那么让我为您提供另一种解决方案:

    organizationNames
    字段添加到
    User
    类中。在此字段中,您将存储字符串,该字符串是已排序的组织名称的串联。
    在连接之前,每个名称都应该用空格扩展到最大大小。
    您的四个示例用户如下所示:

    • 用户A
      organizationNames=“AAA Building ZZZ Building”
    • 用户B
      organizationNames=“MMM Building”
    • 用户C
      organizationNames=“CCC Building VVV Building”
    • 用户D
      organizationNames=“AAA Building MMM Building”
    因此这会增加一些冗余。此字段的值应在更改用户的组织列表后更新。
    然后,您可以简单地按
    organizati对用户进行排序
    
    public class User implements Serializable, Comparable<User>, Comparator<User>
    {
        ...
    
        public int compareTo(User u) {
            return compare(this, u);
        }
    
        public int compare(User u1, User u2) {
            Iterator<Organization> it1 = u1.organizations.iterator();
            Iterator<Organization> it2 = u2.organizations.iterator();
            while (it1.hasNext() && it2.hasNext()) {
                Organization o1 = it1.next();
                Organization o2 = it2.next();
                if(o1.compareTo(o2) != 0) {
                    return o1.compareTo(o2);
                }
            }
            if(it1.hasNext())
                return 1;
            if(it2.hasNext())
                return -1;  
            return 0;
        }
    }
    
    public class Organization implements Comparable<Organization>
    {
        ...
    
        public int compareTo(Organization o) {
            return (this.name).compareTo(o.name);
        }
    }
    
    final CriteriaBuilder builder = m_entityManager.getCriteriaBuilder();
    final CriteriaQuery<User> criteria = builder.createQuery(User.class);
    Root<User> root = criteria.from(User.class);
    Join<User, Organization> organizations = root.fetch("organizations");
    criteria.select(root);
    TypedQuery<User> query = em.createQuery(criteria);
    List<User> users = query.getResultList();
    Collections.sort(users, new User());