Skip to content

Entity with embedded key is not deleted #3615

@javydreamercsw

Description

@javydreamercsw

I'm learning Spring Boot as I go, and everything has been fine so far using Spring JPA to manage the database until I tried deleting an entity with an embedded key. See the attached project.
test-stability-api.zip

The relevant entity is as follows:

```@Setter
@Getter
@Data
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Cacheable
@ToString
@Builder
@Table(name = "test_case_execution_result")
@NamedQueries({
@NamedQuery(
name = "TestCaseExecutionResult.findAll",
query = "SELECT t FROM TestCaseExecutionResult t"),
@NamedQuery(
name = "TestCaseExecutionResult.findById",
query = "SELECT t FROM TestCaseExecutionResult t WHERE t.testCaseExecutionResultPK.id = :id"),
@NamedQuery(
name = "TestCaseExecutionResult.findByTestCaseResultId",
query =
"SELECT t FROM TestCaseExecutionResult t WHERE"
\+ " t.testCaseExecutionResultPK.testCaseResultId = :testCaseResultId"),
@NamedQuery(
name = "TestCaseExecutionResult.findByTestCaseId",
query =
"SELECT t FROM TestCaseExecutionResult t WHERE t.testCaseExecutionResultPK.testCaseId ="
\+ " :testCaseId"),
@NamedQuery(
name = "TestCaseExecutionResult.findByTestRegionId",
query =
"SELECT t FROM TestCaseExecutionResult t WHERE t.testCaseExecutionResultPK.testRegionId ="
\+ " :testRegionId"),
@NamedQuery(
name = "TestCaseExecutionResult.findByDate",
query = "SELECT t FROM TestCaseExecutionResult t WHERE t.date = :date"),
@NamedQuery(
name = "TestCaseExecutionResult.findByRelatedJob",
query = "SELECT t FROM TestCaseExecutionResult t WHERE t.relatedJob = :relatedJob")
})

public class TestCaseExecutionResult implements Serializable {

@Serial private static final long serialVersionUID = 1L;
@EmbeddedId protected TestCaseExecutionResultPK testCaseExecutionResultPK;

@Basic(optional = false)
@NotNull @Column(name = "date")
@Temporal(TemporalType.TIMESTAMP)
private Date date;

@Basic(optional = false)
@NotNull @Size(min = 1, max = 255)
@Column(name = "relatedJob")
private String relatedJob;

@JoinColumn(
name = "test_case_id",
referencedColumnName = "id",
insertable = false,
updatable = false)
@ManyToOne(optional = false)
private TestCase testCase;

@JoinColumn(
name = "test_case_result_id",
referencedColumnName = "id",
insertable = false,
updatable = false)
@ManyToOne(optional = false)
private TestCaseResult testCaseResult;

@JoinColumn(
name = "test_region_id",
referencedColumnName = "id",
insertable = false,
updatable = false)
@ManyToOne(optional = false)
private TestRegion testRegion;

public TestCaseExecutionResult(TestCaseExecutionResultPK testCaseExecutionResultPK) {
this.testCaseExecutionResultPK = testCaseExecutionResultPK;
}

public TestCaseExecutionResult(
TestCaseExecutionResultPK testCaseExecutionResultPK, Date date, String relatedJob) {
this.testCaseExecutionResultPK = testCaseExecutionResultPK;
this.date = date;
this.relatedJob = relatedJob;
}

public TestCaseExecutionResult(
int testCaseExecutionResultPK, int testCaseResultId, int testCaseId, int testRegionId) {
this.testCaseExecutionResultPK =
new TestCaseExecutionResultPK(
testCaseExecutionResultPK, testCaseResultId, testCaseId, testRegionId);
}

@Override
public int hashCode() {
int hash = 0;
hash += (testCaseExecutionResultPK != null ? testCaseExecutionResultPK.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof TestCaseExecutionResult other)) {
return false;
}
return (this.testCaseExecutionResultPK != null || other.testCaseExecutionResultPK == null)
&& (this.testCaseExecutionResultPK == null
|| this.testCaseExecutionResultPK.equals(other.testCaseExecutionResultPK));
}
}

This is the relevant controller:

    ```@RestController
    @SuppressWarnings("PMD.TestClassWithoutTestCases")
    public class TestCaseExecutionResultController {
    
    private final TestCaseExecutionResultRepository repository;
    private final TestCaseExecutionResultAssembler assembler;
    
    public TestCaseExecutionResultController(
    TestCaseExecutionResultRepository repository, TestCaseExecutionResultAssembler assembler) {
    this.repository = repository;
    this.assembler = assembler;
    }
    
    @PostMapping(Endpoints.EXECUTION_RESULT_REGION)
    @ResponseStatus(HttpStatus.CREATED)
    public ResponseEntity\<EntityModel\<TestCaseExecutionResult\>\> createTestCaseExecutionResult(
    @RequestBody TestCaseExecutionResult testCaseExecutionResult,
    @PathVariable Integer tcId,
    @PathVariable Integer regionId,
    @PathVariable Integer resultId) {
    if (testCaseExecutionResult.getTestCaseExecutionResultPK() == null) {
    testCaseExecutionResult.setTestCaseExecutionResultPK(
    TestCaseExecutionResultPK.builder()
    .testCaseResultId(resultId)
    .testCaseId(tcId)
    .testRegionId(regionId)
    .build());
    }
    testCaseExecutionResult.setDate(new Date());
    return ResponseEntity.ok(this.assembler.toModel(this.repository.save(testCaseExecutionResult)));
    }
    
    @GetMapping(Endpoints.EXECUTION_RESULTS)
    public ResponseEntity\<CollectionModel\<EntityModel\<TestCaseExecutionResult\>\>\> findAll() {
    return ResponseEntity.ok(this.assembler.toCollectionModel(this.repository.findAll()));
    }
    
    @GetMapping(Endpoints.EXECUTION_RESULTS_REGION)
    public ResponseEntity\<CollectionModel\<EntityModel\<TestCaseExecutionResult\>\>\>
    executionResultsForTestCaseInRegion(
    @PathVariable Integer tcId, @PathVariable Integer regionId) throws NotFoundException {
    return ResponseEntity.ok(
    this.assembler.toCollectionModel(
    this.repository.findByTestCaseIdAndTestRegionId(tcId, regionId)));
    }
    
    @DeleteMapping(Endpoints.EXECUTION_RESULT_REGION_EXECUTION)
    public ResponseEntity\<?\> delete(
    @PathVariable Integer tcId,
    @PathVariable Integer regionId,
    @PathVariable Integer resultId,
    @PathVariable Integer executionId)
    throws NotFoundException {
    Iterable\<TestCaseExecutionResult\> results =
    repository.findAllById(
    List.of(new TestCaseExecutionResultPK(executionId, resultId, tcId, regionId)));
    if (results.iterator().hasNext()) {
    TestCaseExecutionResult target = results.iterator().next();
    this.repository.deleteById(target.getTestCaseExecutionResultPK());
    return ResponseEntity.noContent().build();
    } else {
    throw new NotFoundException(
    "Test case execution result with id "
    \+ new TestCaseExecutionResultPK(executionId, resultId, tcId, regionId)
    \+ " not found");
    }
    }
    }

Have the following test where I basically create the necesary entities and then create a test case execution result which works fine. Then I proceed to delete it but fail when I expect to no longer being able to find it via the API. This is the approach that I use in all the other tests and seem to work fine in those cases. The only difference I can find so far is the key for this entity.

```@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class TestCaseExecutionTest {

@Autowired private TestCaseExecutionResultController controller;

@Autowired TestCaseExecutionResultRepository repository;

@Autowired private TestRegionRepository regionRepository;

@Autowired private TestCaseResultRepository resultRepository;

@Autowired private TestCaseRepository testCaseRepository;

@LocalServerPort private int port;

@Autowired private TestRestTemplate restTemplate;

@Test
void contextLoads() {
assertThat(controller).isNotNull();
}

@Test
@SneakyThrows
public void testCreateTestCaseExecutionResult() {
TestCase tc =
TestCase.builder()
.className("ClassName")
.testName("testName")
.testMethod("testMethod")
.description("description")
.build();
TestCase testCase = testCaseRepository.save(tc);

    Iterator<TestRegion> iterator = regionRepository.findAll().iterator();
    TestRegion region;
    if (iterator.hasNext()) {
      region = iterator.next();
    } else {
      region = TestRegion.builder().regionName(UUID.randomUUID().toString()).build();
      regionRepository.save(region);
    }
    
    TestCaseResult testCaseResult = resultRepository.findById(1);
    
    // Add an execution result
    TestCaseExecutionResult request =
        TestCaseExecutionResult.builder()
            .testRegion(TestRegion.builder().id(region.getId()).build())
            .testCase(TestCase.builder().id(testCase.getId()).build())
            .testCaseResult(TestCaseResult.builder().id(testCaseResult.getId()).build())
            .relatedJob("Dummy URL")
            .build();
    
    this.restTemplate.getForEntity(
        "http://localhost:" + port + Endpoints.EXECUTION_RESULTS_REGION,
        TestCaseExecutionResult.class,
        request.getTestCase().getId(),
        request.getTestRegion().getId());
    
    assertTrue(Objects.requireNonNull(controller.findAll().getBody()).getContent().isEmpty());
    
    TestCaseExecutionResult testCaseExecutionResult =
        this.restTemplate.postForObject(
            "http://localhost:" + port + Endpoints.EXECUTION_RESULT_REGION,
            request,
            TestCaseExecutionResult.class,
            request.getTestCase().getId(),
            request.getTestRegion().getId(),
            request.getTestCaseResult().getId());
    
    assertThat(testCaseExecutionResult).isNotNull();
    assertThat(testCaseExecutionResult)
        .usingRecursiveComparison()
        .ignoringFields(
            "testCaseExecutionResultPK", "date", "testCase", "testCaseResult", "testRegion")
        .isEqualTo(request);
    
    assertFalse(Objects.requireNonNull(controller.findAll().getBody()).getContent().isEmpty());
    
    assertFalse(
        Objects.requireNonNull(
                controller
                    .executionResultsForTestCaseInRegion(
                        testCaseExecutionResult.getTestCase().getId(),
                        testCaseExecutionResult.getTestRegion().getId())
                    .getBody())
            .getContent()
            .isEmpty());
    
    ResponseEntity<TestCaseExecutionResultWrapper> response =
        this.restTemplate.getForEntity(
            "http://localhost:" + port + Endpoints.EXECUTION_RESULTS_REGION,
            TestCaseExecutionResultWrapper.class,
            request.getTestCase().getId(),
            request.getTestRegion().getId());
    
    TestCaseExecutionResultWrapper wrapper = response.getBody();
    assertNotNull(wrapper);
    List<TestCaseExecutionResult> results = wrapper.getEmbedded().getTestCaseExecutionResults();
    assertNotNull(results);
    assertEquals(results.size(), 1);
    
    testCaseExecutionResult = results.get(0);
    
    // Delete it
    this.restTemplate.delete(
        "http://localhost:" + port + Endpoints.EXECUTION_RESULT_REGION_EXECUTION,
        testCaseExecutionResult.getTestCaseExecutionResultPK().getTestCaseId(),
        testCaseExecutionResult.getTestCaseExecutionResultPK().getTestRegionId(),
        testCaseExecutionResult.getTestCaseExecutionResultPK().getTestCaseResultId(),
        testCaseExecutionResult.getTestCaseExecutionResultPK().getId());
    
    response =
        this.restTemplate.getForEntity(
            "http://localhost:" + port + Endpoints.EXECUTION_RESULTS_REGION,
            TestCaseExecutionResultWrapper.class,
            request.getTestCase().getId(),
            request.getTestRegion().getId());
    
    wrapper = response.getBody();
    assertNotNull(wrapper);
    results = wrapper.getEmbedded().getTestCaseExecutionResults();
    assertNotNull(results);
    if (!results.isEmpty()) {
      ObjectMapper objectMapper = new ObjectMapper();
      repository
          .findAll()
          .iterator()
          .forEachRemaining(
              item -> {
                try {
                  String json =
                      objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(item);
                  log.info(json);
                } catch (JsonProcessingException e) {
                  log.error("Error converting item to JSON", e);
                }
              });
    }
    assertEquals(results.size(), 0);

}
}

I was expecting the deleted entity not to be found afterward. 

Metadata

Metadata

Assignees

Labels

status: invalidAn issue that we don't feel is valid

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions