Стоит ли избегать использования mappedBy, чтобы приложение оставалось стабильным?

У меня есть две сущности, подобные следующим:

@Entity
public class Trip {

    @OneToMany(mappedBy = "trip", fetch = FetchType.LAZY)
    private Set<Job> jobs = new HashSet<Job>();

}

@Entity
public class Job {

    @ManyToOne(fetch = FetchType.LAZY)
    private Trip trip;

}

Проблема в том, что отношение mappedBy ведет себя по-разному в разных обстоятельствах. Вот пример

    EntityManager em1 = unit.createEntityManager();
    EntityManager em2 = unit.createEntityManager();

    // One EM starts transaction and stores a trip
    em1.getTransaction().begin();

    Trip trip = new Trip();
    em1.persist(trip);

    Long tripId = trip.getId();

    assertThat(tripId).isPositive();

    em1.getTransaction().commit();

    // Then em2 starts different transaction
    em2.getTransaction().begin();

    // Looking up for the trip through clean em (cache is empty)
    Trip em2Trip = em2.find(Trip.class, tripId);

    Job job = new Job();
    job.setTrip(em2Trip);

    em2.persist(job);

    // The em2Trip should not be updated

    assertThat(em2Trip.getJobs()).hasSize(1);

    em2.getTransaction().commit();

    em1.getTransaction().begin();

    Trip em1Trip = em1.find(Trip.class, tripId);

    // fails here
    assertThat(em1Trip.getJobs()).hasSize(1);

    em1.getTransaction().commit();

Код выше показывает, что если сущность уже загружена в кэш менеджера сущностей, метод получения для отношения mappedBy может возвращать недопустимые результаты.

У меня есть доказательство, что это не работает и под JBoss. Следующий код ведет себя по-разному в зависимости от того, какой диспетчер сущностей используется. Результат непредсказуем.

        Trip trip = em.find(Trip.class, tripId);
        if (trip.getJobs().size() == 0) ...

Означает ли это, что mappedBy автоматически делает приложение глючным, как только оно вводится и используется?

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


person artemb    schedule 14.08.2009    source источник
comment
Пожалуйста, отредактируйте свой вопрос и постарайтесь сохранить его объективность. Никто не любит читать разглагольствования.   -  person Mauricio Scheffer    schedule 14.08.2009


Ответы (2)


Поведение, которое вы описываете, не имеет абсолютно никакого отношения к ассоциациям; вы получите точно такие же результаты, если просто попытаетесь прочитать/обновить простой POJO из двух менеджеров сущностей. Как только ваша сущность будет связана с контекстом сохраняемости, она НЕ будет автоматически обновляться из базы данных. Это задокументированное поведение, и в подавляющем большинстве случаев это ЖЕЛАЕМОЕ поведение.

Что касается «сохранения стабильности вашего приложения»:

  1. Экземпляры EntityManager напрямую соответствуют сеансам Hibernate и поэтому не должны удерживаться в течение длительного периода времени. Если вы перепишете свой код выше, чтобы использовать новый экземпляр EntityManager (em3) вместо повторного использования em1, ваша проблема исчезнет.
  2. EntityManager имеет метод refresh(), который вы можете вызывать для перезагрузки состояния объекта из базы данных.
  3. EntityManager имеет метод clear(), который полностью очистит контекст сохраняемости, что также предотвратит эту проблему. Однако используйте его экономно и осторожно — вызов clear() без flush() приведет к сбросу всех ожидающих обновлений.
person ChssPly76    schedule 14.08.2009
comment
Правильно, простые атрибуты тоже не обновляются! Спасибо за указание на это. - person artemb; 18.08.2009

Использование mappedBy (т. е. двунаправленных ассоциаций) не является ошибочным или непоследовательным. Однако его сложно настроить, и вы должны быть очень осторожны при его использовании — это не волшебная пуля, и если вы не будете следовать рекомендациям по программированию, вы можете привести свои объекты в несогласованное состояние.

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

person skaffman    schedule 14.08.2009
comment
Отличный ответ и освежающее изменение, чтобы увидеть совет против двунаправленного. Ссылка на Hibernate использует слишком много аргументов за утверждением. - person Dick Chesterwood; 17.10.2012