JPA EntityManager не может сбрасываться в PostInsertEventListener - PullRequest
0 голосов
/ 14 ноября 2018

Я пытаюсь записать журнал аудита, используя прослушиватели событий Hibernate, такие как: PostInsertEventListener, PostUpdateEventListener и PostDeleteEventListener. Но у меня есть проблема, связанная с использованием entityManager для сброса данных из контекста постоянства в базу данных в следующих прослушивателях:

Вот исходный код:

EntityA.class

@Entity
@Table(name = "ENTITY_A")
public class EntityA implements Serializable {
  private static final long serialVersionUID = -8674903027075338289L;

  @Id
  @Column(name = "ENTITY_A_ID")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @SortableField
  private Long buStepId;


  @Column(name = "ENTITY_A_CODE", unique = true)
  private String buStepCode;


  @Column(name = "ENTITY_A_NAME")
  private String buStepName;

  @OneToMany(mappedBy = "buStep", fetch = FetchType.LAZY)
  private List<BuEvent> BuEvents;

  @OneToMany(mappedBy = "buStep", fetch = FetchType.LAZY)
  private Set<BuEventFlow> BuEventFlows;

  @ManyToOne
  @JoinColumn(name = "GS_STATUS_ID", referencedColumnName = "STATUS_ID")
  private RefStatus refGsStatus;

  @ManyToOne
  @JoinColumn(name = "GT_STATUS_ID", referencedColumnName = "STATUS_ID")
  private RefStatus refGtStatus;

  @ManyToOne
  @JoinColumn(name = "GS_VERSION_ID", referencedColumnName = "VERSION_ID")
  private RefVersion refGsVersion;

  @ManyToOne
  @JoinColumn(name = "GT_VERSION_ID", referencedColumnName = "VERSION_ID")
  @AuditableField(name = "GT Version")
  private RefVersion refGtVersion;

  @ManyToOne
  @JoinColumn(name = "EVO_TYPE_ID")
  private RefEvoType refEvoType;

  @Column(name = "TURN")
  private Long turn;

  @ManyToOne
  @JoinColumn(name = "step_family_id", nullable = true)
  private RefStepFamily buStepFamily;

  @OneToMany(mappedBy = "buStep", fetch = FetchType.LAZY)
  private List<BuStepFlow> buStepFlows;

  @Transient
  private Long stepFamilyId;
}

BusinessService.java

@Service
public class BusinessService {
  @PersistenceContext
  protected EntityManager entityManager;

  public void createEntityA() {
    EntityA entityA = createPojoEntityA();
    entityManager.persist(entityA);
  }
}

EntityEventListenerRegistry.java:

@Component
public class EntityEventListenerRegistry implements 
PostInsertEventListener, PostUpdateEventListener, 
PostDeleteEventListener {

  @PersistenceContext
  EntityManager entityManager;

  @PostConstruct
  protected void init() {
    HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.emf;
    SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
    EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
    registry.appendListeners(EventType.POST_INSERT, this);
    registry.appendListeners(EventType.POST_UPDATE, this);
    registry.appendListeners(EventType.POST_DELETE, this);
  }

  @Override
  public void onPostInsert(PostInsertEvent event) {
    // Do something with entityA before
    entityManager.flush();
    // Do something with entityA after
  }
...
}

Тогда исключение происходит в методе onPostInsert:

org.hibernate.AssertionFailure: null id in EntityA entry (don't flush the Session after an exception occurs)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:60)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:175)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:135)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:216)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:85)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:38)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1300)

Мне потребовался один день для отладки, но я до сих пор не знаю причину почему. Не могли бы вы помочь мне объяснить это?

1 Ответ

0 голосов
/ 14 ноября 2018

Я вижу, что некоторые люди уже говорили вам об ограничениях в Hibernate.Документация ( Руководство пользователя Hibernate * ) гласит:

Метод обратного вызова не должен вызывать методы EntityManager или Query!

В любом случае, для решения задачи аудита выможно использовать Hibernate Envers: http://hibernate.org/orm/envers/

Также вы можете прочитать об аудите там: https://www.baeldung.com/database-auditing-jpa

  • Чистый JPA-подход является самым базовым и состоит из использования жизненного циклаобратные вызовы.Тем не менее, вам разрешено изменять только состояние отсутствия связи с объектом.Это делает обратный вызов @PreRemove бесполезным для наших целей, так как любые настройки, сделанные вами в методе, будут затем удаляться вместе с сущностью.
  • Envers - это зрелый модуль аудита, предоставляемый Hibernate.Он легко настраивается и не имеет недостатков в чистой реализации JPA.Таким образом, он позволяет нам проверять операцию удаления, поскольку он регистрируется в таблицах, отличных от таблицы сущности.

  • Подход Spring Data JPA абстрагирует работу с обратными вызовами JPA и предоставляет удобные аннотации для аудитасвойства.Он также готов к интеграции с Spring Security.Недостатком является то, что он наследует те же недостатки подхода JPA, поэтому операция удаления не может быть проверена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...