JPA ManyToOne returning complete parent entity vs id

Viewed 118

I have a springboot application with JPA. The ORM setup is a ManyToOne and I have roughly followed the excellent post here from Vlad so that I have only setup the @ManyToOne on the child.

My Entities are HealthCheck (Many) which must have a Patient (One). The issue is that when I retrieve a HealthCheck via my Rest controller instead of getting just the id of the patient I get the whole entity.

For my project I probably will get the whole patient with a HealthCheck, but I would like to know how i could get just the HealthCheck with the patient_id instead of the whole patient entity if I so needed to do so.

HEALTH CHECK

@AllArgsConstructor
@NoArgsConstructor
@Entity
@Data
public class HealthCheck {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Patient patient;
    //Getters and Setters

PATIENT

@Entity
@Data
public class Patient {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    @Column(unique = true)
    @NotEmpty(message = "Name must not be null or empty")
    private String name;
    // Getters and Setters

The HealthCheckServiceImpl uses the derived queries to get one by id, and its this call thats used to get a HealthCheck by the REST controller:

@Override
public HealthCheck get(Long id) {
    log.info("Getting a single Health Check");
    return healthCheckRepository.getById(id);
}

The result of a call to the REST controller results in something like:

{
    "id": 2,
    "patient": {
        "id": 2,
        "name": "Jono",
        "hibernateLazyInitializer": {}
    },
    "other fields": "some comment",
    "hibernateLazyInitializer": {}
}

Note1 the whole patient entity is returned

Note2 that because I have only used the ManyToOne annottaion on the child side I dont get the Jackson recursion issues some others do

QUESTION: How can I control the returned HealthCheck so that it includes the patient_id not the whole object?

UPDATE

The line that calls to the service is :

@GetMapping("get/{id}")
public ResponseEntity<HealthCheck> getHealthCheck(@PathVariable("id") Long id) {
    return ResponseEntity.ok()
            .header("Custom-Header", "foo")
            .body(healthCheckService.get(id));

I breakpoint on healthCheckService.get(id)) but noting on the debugger looks like it contains an entity reference:

enter image description here

UPDATE2 enter image description here

1 Answers

Well, it seems you're returning your entities objects directly from your controller.

The issue is that when I retrieve a HealthCheck via my Rest controller instead of getting just the id of the patient I get the whole entity.

You can use the DTO Pattern explained here and here as response in your controller. That, also decouples your domain from your controller layer. This is a simplified example:

@Data
@NoArgsConstructor
public class HealthCheckDto {

    private Long id;
    private PatientDto patient;
    private String otherField;

    public HealthCheckDto(HealthCheck healthCheck) {
        this.id = healthCheck.getId();
        this.patient = new PatientDto(healthCheck.getPatient());
        this.otherField = healthCheck.getOtherField();
    }
}

@Data
@NoArgsConstructor
public class PatientDto {

    private Long id;

    public PatientDto(Patient patient) {
        this.id = patient.getId();
    }
}

// In your controller
public ResponseEntity<HealthCheckDto> getHealthCheck(Long id) {
    HealthCheck healthCheck = healthCheckService.getById(id);
    return ResponseEntity.ok(new HealthCheckDto(healthCheck));
}
Related