Изменить сущность с помощью EntityListener на каскаде - PullRequest
1 голос
/ 27 августа 2010

У меня есть база данных, в которой у сущности (скажем, Пользователь) есть список сущностей (скажем, Список). Как требование, я должен иметь денормализованное количество объектов в списке:

@Entity
class User {
    /* ... */
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Friendship> friends;

    public int friendsCount;
    /* ... */
}

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

Я пытался сделать это, используя EntityListener:

class MyBrokenEntityListener {
    @PostPersist
    public void incrementCounter(Friendship friendship) {
        friendship.getUser1().incrementFriendsCount();
    }
}

Слушатель сущности вызывается правильно, и во время отладки я вижу, что он правильно изменяет сущность. Однако Hibernate не отправляет запрос UPDATE в базу данных, только запрос INSERT (соответствующий сущности Friendship).

Я пробовал разные события на EntityListener, но, похоже, он не работает.

Я предполагаю, что здесь происходит то, что прослушиватель сущности запускается обновлением пользователя. Затем он идентифицирует все грязные свойства пользователя, которые состоят только из списка. Поэтому он не имеет ничего общего с Пользователем, он должен только вставить дружбу. Затем она каскадно переходит в Дружбу. Слушатель объекта вызывается, он изменяет пользователя, но в это время Hibernate уже определил, что ему не нужно беспокоиться о пользователе, поэтому он не обновляет пользователя.

Правильно ли это рассуждение?

Если так, как я могу должным образом добиться этого?

Спасибо, Bruno

1 Ответ

0 голосов
/ 27 августа 2010

Я пробовал разные события на EntityListener, но, похоже, он не работает.

Из того, что я вижу, связь один ко многим между User иFriendship является двунаправленным, но ваше отображение не отражает это.Так вы могли бы сначала исправить ваше отображение?Примерно так:

@Entity
public class User1 {
    @Id @GeneratedValue
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, 
               mappedBy = "user1")
    private List<Friendship> friends = new ArrayList<Friendship>();

    public int friendsCount;

    // omitting getters, setters for brevity

    public void incrementFriendsCount() {
        friendsCount++;
    }

    public void addToFriends(Friendship friend) {
        this.getFriends().add(friend);
        friend.setUser1(this);
    }
}

Заметные изменения:

  • Я добавил mappedBy на стороне, не являющейся владельцем ассоциации
  • У меня естьдобавлен удобный метод для правильной установки обеих сторон ссылки при добавлении Friendship

Слушатель объекта вызывается, он изменяет пользователя, но в это время Hibernate уже определил, чтоему не нужно беспокоиться о пользователе, поэтому он не обновляет пользователя.

Ваш MyBrokenEntityListener слушатель работает для меня, я не могу воспроизвести проблему, используя приведенное выше фиксированное отображение: Hibernate делаетобнаружить, что экземпляр User устарел после вызова Listener, и он вызывает UPDATE.Следующий метод тестирования (запущенный внутри транзакции) проходит:

@Test
public void testPostPersistInvocationOnAddFriend() {
    User1 user = new User1();
    Friendship friend1 = new Friendship();
    user.addToFriends(friend1);
    em.persist(user);
    em.flush();
    assertEquals(1, user.getFriendsCount());

    Friendship friend2 = new Friendship();
    user.addToFriends(friend2);
    em.flush();

    em.clear(); // so that we reload the managed user from the db
    user = em.find(User1.class, user.getId());
    assertEquals(2, user.getFriendsCount());
}
...