Java JPA:如何对实体中的集合字段进行排序?
我使用的是Spring3.2.11.RELEASE、Hibernate4.3.6.Final和JPA2.1。我有以下实体和以下字段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
@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;
每次更改用户
,都会将此属性设置到集合中的组织
的串联列表中当然,您可以在获取
用户后对其进行排序,但这可能有一个很大的缺点:如果您(愿意)如果有成千上万的用户,您将需要对其进行分页。然后,按其他字段排序将更加复杂。同时加载许多用户及其关系可能是一个很大的性能瓶颈。如果只有数百个用户,此解决方案肯定会起作用。您无法为实体编写此类查询。如果您使用的是本机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());