【问题标题】:JPA One-to-Many relationship using a List - OrderBy ignored/not working使用列表的 JPA 一对多关系 - OrderBy 被忽略/不起作用
【发布时间】:2014-06-20 13:23:38
【问题描述】:

我会尽量把问题表述得更简单:

@Entity
public class One implements Serializable {
...
@Id
@GeneratedValue
private Long id;
@OneToMany
@OrderBy("name ASC")
private List<Many> many;
...

首先,我用一些多实体填充列表并保持单实体。其次,我检索(em.find)期望列表按Many#name升序排列的单一实体,但它不是按名称排序的。该列表按 id 排序。如有必要,完整代码见下文。

几天前的原帖:

我正在使用当前的 Netbeans Glassfish 包。

产品版本:NetBeans IDE 8.0(内部版本 201403101706) 更新:NetBeans IDE 已更新至 NetBeans 8.0 Patch 2 版本 爪哇:1.7.0_51; Java HotSpot(TM) 64 位服务器 VM 24.51-b03 运行时:Java(TM) SE 运行时环境 1.7.0_51-b13 系统:在 x86_64 上运行的 Mac OS X 版本 10.9.3; UTF-8; de_DE (nb)

JPA @OrderBy 注释被完全忽略。

@Entity
public class One implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;
    @OneToMany
    @OrderBy("name ASC")
    private List<Many> many;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public List<Many> getMany() {
        return many;
    }

    public void setMany(List<Many> many) {
        this.many = many;
    }

}

众多实体

@Entity
public class Many implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    public Many() {
    }

    public Many(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

服务类 (EJB)

@Stateless
public class Service {

    @PersistenceContext(unitName = "cwPU")
    private EntityManager em;


    public One createOne() {
        return em.merge(new One());
    }

    public Many createMany(String name) {
        return em.merge(new Many(name));
    }

    public One add(Long oneId, Long manyId) {
        One one = em.find(One.class, oneId);
        Many many = em.find(Many.class, manyId);
        one.getMany().add(many);
        return one;
    }

    public One find(Long id) {
        One one = em.find(One.class, id);
        return one;
    }

}

主类

public class Main {

    public static void main(String[] args) throws NamingException {
        EJBContainer container = EJBContainer.createEJBContainer();
        Context ctx = container.getContext();
        Service service = (Service) ctx.lookup("java:global/classes/Service");
        One one = service.createOne();
        Many many = service.createMany("the-first");
        service.add(one.getId(), many.getId());
        many = service.createMany("a-second");
        one = service.add(one.getId(), many.getId());
        one = service.find(one.getId());
        System.out.println("-------------------------------------------------");
        for (Many m : one.getMany()) {
            System.out.println(m.getName());
        }
        container.close();
    }

}

输出:

the-first
a-second

无论我向@OrderBy 注解写什么(name ASC,name DESC,id ASC,id DESC),输出总是相同的 id 升序排列。

知道我错过了什么吗?

【问题讨论】:

    标签: jpa eclipselink


    【解决方案1】:

    @Orderby 注释实际上并不是这样工作的。根据javadoc,注释“指定集合元素的顺序......在集合被检索时。”

    因此注释会影响查询(查找)的结果,但不会规定您将结果集存储到的集合中的顺序。

    【讨论】:

    • 感谢您的评论。但我不明白这种方式的javadoc。您能否提供一个示例,如何检索集合元素?据我了解,@OrderBy 注释不会对数据库强制执行任何实体顺序。但是在检索到时,它们会按照指定的顺序插入到集合(List)中。看看最后的主要课程。一 = service.find(one.getId()); (基本上是 em.find(One.class, id);) 所以我确实检索了实体。
    • 如前所述,JPA 将使用您定义的 orderby 字段从数据库中检索列表。但是,它不会在您的实体中维护顺序,因此在添加新元素时由您的应用程序维护此顺序。由于实体缓存,每个查询或查找调用都不必转到数据库,因此您必须始终维护此列表,否则会出现此类问题。您可以通过调用 em.refresh 来强制重新加载列表,但只维护您的集合顺序会更有效。
    • 再次感谢。我想我理解你的评论和文档。但评论不适用于我的示例代码。请再看一遍。我将两个Many 实体插入到实体One 的多关系中。然后我坚持One 并再次检索它。现在应该对多关系列表进行排序。但事实并非如此。调用刷新没有效果。我不知道这里出了什么问题或如何缩小问题范围。
    【解决方案2】:

    如 Chris 和 WPrecht 所述,解决方案是调用 em.refresh(在正确的位置)。我必须在单独的 EJB 方法中执行此操作。

    没有起作用:

    public One find(Long id) {
        em.refresh(em.find(One.class, id)); // did not work
        One one = em.find(One.class, id);
        return one;
    }
    

    添加单独的刷新方法

    public void refresh(Long id) {
        em.refresh(em.find(One.class, id));
    }
    

    并在主程序中调用它

    ...
    service.refresh(one.getId());
    one = service.find(one.getId());
    ...
    

    有效!

    可能我需要做更多的阅读才能理解缓存。

    【讨论】:

    • 我正在使用 EclipseLink 2.6.0。 em.refresh(entity) 也为我完成了这项工作(em.find(class,id) 不足以申请订单) - 谢谢你的提示。
    猜你喜欢
    • 2018-04-04
    • 2014-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    • 2016-09-07
    相关资源
    最近更新 更多