Связь JPA «Один ко многим» с использованием списка — порядок игнорируется / не работает

#jpa #eclipselink

#jpa #eclipselink

Вопрос:

Я постараюсь сформулировать вопрос более просто:

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

Сначала я заполняю список несколькими объектами и сохраняю один объект. Во-вторых, я извлекаю (em.find) единый объект, ожидающий список в порядке возрастания по многим #name, но он не упорядочен по имени. Список упорядочен по идентификатору. При необходимости полный код см. Ниже.

Оригинальное сообщение несколько дней назад:

Я использую текущий пакет Netbeans Glassfish.

Версия продукта: NetBeans IDE 8.0 (сборка 201403101706) Обновления: Среда IDE NetBeans обновлена до версии NetBeans 8.0 Исправление 2 Java: 1.7.0_51; 64-разрядная серверная виртуальная машина Java HotSpot (TM) 24.51-b03 Среда выполнения: Java (TM) SE Среда выполнения 1.7.0_51-b13 Система: Mac OS X версии 10.9.3 работает наx86_64; 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), выходные данные всегда имеют одинаковый порядок возрастания по идентификатору.

Есть идеи, чего мне не хватает?

Ответ №1:

@Orderby Аннотация на самом деле так не работает. Согласно javadoc, аннотация «Определяет порядок элементов коллекции … в момент извлечения коллекции».

Таким образом, аннотация влияет на результат запроса (find), но не определяет порядок в коллекции, в которую вы сохраняете результирующий набор.

Комментарии:

1. Спасибо за комментарий. Но я не понимаю javadoc таким образом. Не могли бы вы привести пример, как получить элементы коллекции? Как я понял, аннотация @OrderBy не применяет какой-либо порядок объектов к базе данных. Но при извлечении они вставляются в коллекцию (список) в указанном порядке. Посмотрите на основной класс в конце. one = service.find(one.getId()); (в основном em.find(One.class , идентификатор);) Итак, я извлекаю сущности.

2. Как указано, JPA извлечет список из базы данных, используя поле orderby, как вы определили. Однако он не поддерживает порядок в вашей сущности, поэтому ваше приложение должно поддерживать этот порядок при добавлении новых элементов. Каждый запрос или вызов поиска не обязательно переходит в базу данных из-за кэширования объектов, поэтому вы должны постоянно поддерживать этот список или ожидать возникновения подобных проблем. Вы можете принудительно перезагрузить список, вызвав em.refresh , но более эффективно просто поддерживать порядок вашей коллекции.

3. Еще раз спасибо. Я думаю, я понимаю ваш комментарий и документы. Но комментарий не относится к моему примеру кода. Пожалуйста, посмотрите на это еще раз. Я вставляю две Many сущности во многие отношения сущности One . Затем я сохраняю One и извлекаю его снова. Теперь список многих отношений должен быть упорядочен. Но это не так. Вызов обновления не имеет никакого эффекта. Я понятия не имею, что здесь происходит не так или как сузить проблему.

Ответ №2:

Решение заключается в вызове em.refresh (в нужном месте), как указано Крисом и WPrecht. Мне пришлось сделать это в отдельном методе 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());
...
  

работает!

Вероятно, мне нужно больше читать, чтобы понять кеширование.

Комментарии:

1. Я использую EclipseLink 2.6.0. em.refresh(entity) тоже выполняет эту работу за меня (em.find(class, id) было недостаточно для применения порядка) — спасибо за подсказку.