Issue
I'm using:
- Quarkus with JPA (javax)
- Postgres 11 database
I have:
An Entity
@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;
}
A repository
@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;
}
}
A test with two variations
Variation 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;
}
}
When I execute this test class (both tests) I'm getting the following Exception on the second time the persist
method is called:
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist com.mypackage.MyEntity
...
Variation 2
I removed the constant ENTITY
from the test class, instead I'm calling now the entity()
method inside the tests, like:
...
subject.persist(entity());
...
at both places. Now the Exeption is gone and everything is fine.
Question
Can someone explain to me, why this is the case (why variante 2 is working and variante 1 not)?
Solution
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.
testDoesExist
executed,ENTITY
saved to database andENTITY.id
set to 1testDoesExist_notMatching
executed and persist called onENTITY
shows the error beacuse it exists in the database, it has anid
assigned
The simplest fix is to call entity()
twice, as in you variation 2.
But don't forget that the records will exist after a test is run, and might affect your other test cases. You might want to consider cleaning up the data in an @After
method or if you intend to use this entity in multiple test cases then put the perist code into a @BeforeClass
method.
Answered By - TomasZ.
Answer Checked By - Willingham (JavaFixing Volunteer)