是什么导致了带有消息 "detached entity passed to perist" 的 PersistenceException

What caused the PersistenceException with the message "detached entity passed to perist"

我正在使用:

我有:

实体

@Entity
@Table(name = "MyEntityTable")
@NamedQuery(name = MyEntity.DOES_EXIST, query = "SELECT x FROM MyEntity x WHERE x.type = :type")
public class MyEntity {

  public static final String DOES_EXIST = "MyEntity.DoesExists";

  @Id
  @SequenceGenerator(name = "myEntitySequence", allocationSize = 1)
  @GeneratedValue(generator = myEntitySequence)
  private long id;

  @Column(name = type)
  private String type;
} 

存储库

@ApplicationScoped
@Transactional(Transactional.TxType.Supports)
public class MyEntityReporitory {

  @Inject
  EntityManager entityManager;

  @Transactional(Transactional.TxType.Required)
  public void persist(final MyEntity entity) {
    entityManager.persist(entiy);
  }

  public boolean doesExist(final String type) {
    final TypedQuery<MyEntity> query = entityManager
      .createNamedQuery(MyEntity.DOES_EXIST, MyEntity.class)
      .setParameter("type", type);
    
    return query.getResultList().size() > 0;
  }
}

具有两种变体的测试

变体 1

@QuarkusTest
@QuarkusTestResource(DatabaseResource.class) // used to set up a docker container with postgres db
public class MyEntityRepositoryTest {

  private static final MyEntity ENTITY = entity();

  @Inject
  MyEntityRepository subject;

  @Test
  public void testDoesExist() {
    subject.persist(ENTITY);
    final boolean actual = subject.doesExist("type");
    assertTrue(actual);
  }

  @Test
  public void testDoesExist_notMatching() {
    subject.persist(ENTITY);
    final boolean actual = subject.doesExist("another_type");
    assertFalse(actual);
  }

  private static MyEntity entity() {
    final MyEntity result = new MyEntity();
    result.setType("type")
    return result;
  }
}

当我执行此测试时 class(两个测试)我在第二次调用 persist 方法时遇到以下异常:

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist com.mypackage.MyEntity
...

变体 2

我从测试 class 中删除了常量 ENTITY,现在我在测试中调用 entity() 方法,例如:

...
subject.persist(entity());
...

在两个地方。现在异常消失了,一切都很好。

问题

谁能给我解释一下,为什么会这样(为什么变体 2 有效而变体 1 无效)?

https://vladmihalcea.com/jpa-persist-and-merge/

The persist operation must be used only for new entities. From JPA perspective, an entity is new when it has never been associated with a database row, meaning that there is no table record in the database to match the entity in question.

  1. testDoesExist 执行,ENTITY 保存到数据库并且 ENTITY.id 设置为 1
  2. testDoesExist_notMatching 执行并持续调用 ENTITY 显示错误,因为它存在于数据库中,它有一个 id 分配

最简单的解决方法是调用 entity() 两次,就像变体 2 一样。 但不要忘记,测试 运行 后记录将存在,并且可能会影响您的其他测试用例。您可能需要考虑在 @After 方法中清理数据,或者如果您打算在多个测试用例中使用此实体,则将 perist 代码放入 @BeforeClass 方法中。