When using a composite id, the entity is not removed from the persistance context with a delete query written in JPQL
Our Entity and composite id :
{code:java}@Entity @NoArgsConstructor(access = AccessLevel.PRIVATE, force = true) public class SchoolSetting {
@EmbeddedId private final SchoolSettingId id;
@Getter private boolean activated;
public SchoolSetting(@NonNull final String schoolId, @NonNull final String key, final boolean activated) { this.id = new SchoolSettingId(schoolId, key); this.activated = activated; }
public String getKey() { return this.id.getKey(); } }
@Embeddable @NoArgsConstructor(access = AccessLevel.PRIVATE, force = true) @Getter(AccessLevel.PROTECTED) @EqualsAndHashCode public class SchoolSettingId implements Serializable {
private final String schoolId;
private final String key;
public SchoolSettingId(@NonNull final String schoolId, @NonNull final String key) { this.schoolId = schoolId; this.key = key; } }{code}
The repository :
{code:java}interface SpringDataSchoolSettingRepository extends CrudRepository<SchoolSetting, SchoolSettingId> { @Query("delete from SchoolSetting s WHERE s.id.key = :id") @Modifying void deleteById(@Param("id") SchoolSettingId id); }{code}
The following test fail without the testEntityManager.clear() as the entity is still in the persistence context. We we are able to reproduce the same behavior with production code inside the same transaction
{code:java}@DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @Import(JpaSchoolSettingRepository.class) class JpaSchoolSettingRepositoryIT {
static String SCHOOL_ID = "MY_SCHOOL";
@Autowired TestEntityManager testEntityManager;
@Autowired JpaSchoolSettingRepository jpaSchoolSettingRepository;
@Test void deleteTwiceShouldntThrowAnyException() { testEntityManager.persistAndFlush(createSetting());
assertThat(jpaSchoolSettingRepository.get(new SchoolSettingId(SCHOOL_ID, "ABSENCE_FOR_DAY_OFF"))) .isNotEmpty(); jpaSchoolSettingRepository.delete(new SchoolSettingId(SCHOOL_ID, "ABSENCE_FOR_DAY_OFF"));
//testEntityManager.clear(); assertThat(jpaSchoolSettingRepository.get(new SchoolSettingId(SCHOOL_ID, "ABSENCE_FOR_DAY_OFF"))) .isEmpty(); assertThatCode(() -> jpaSchoolSettingRepository.delete(new SchoolSettingId(SCHOOL_ID, "ABSENCE_FOR_DAY_OFF"))) .doesNotThrowAnyException(); }
private SchoolSetting createSetting() { return new SchoolSetting(SCHOOL_ID, ABSENCE_FOR_DAY_OFF, true); } }{code}
If we change the query to the following, it work as expected :
{code:java}@Query("delete from SchoolSetting s WHERE s.id.key = :schoolId and s.id.schoolId = :key") @Modifying void deleteById(String schoolId, String key);{code}
So we conclude that the bug is related with the handling of the composite key |
|