Ariane
Ariane

Reputation: 395

Spring JPA - How to Save Object with a Composite Key (EmbeddedID)

I'm building a Spring system which involves users and the medication they are taking. I have a database with the tables: users, drugs

I am creating a new datatype names UserMed which is composed of a composite primary key - a drug's id and a user's username (the primary keys of the tables above)

Here is the UserMed entity code:

@Entity
@Table(name = "userMeds")
public class UserMed implements Serializable {

    @Id
    @EmbeddedId
    private UserMedId userMedId;


    public UserMed(int drugID, String username) {
        this.userMedId.drug_id = drugID;
        this.userMedId.username = username;

    }

    public UserMed() {
    }


    public String getUsername() {
        return this.userMedId.username;
    }

    public void setUsername(String username) {
        this.userMedId.username = username;
    }


}

And here is the EmbeddedID UserMedId data type:

@RequiredArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
@EqualsAndHashCode
@Embeddable
public class UserMedId implements Serializable {

    @NonNull
    public int drug_id;

    @NonNull
    public String username;
}

I have a route, which my frontend POST requests to. I have double checked the front end, and it is doing the POST request with a serialised UserMed type, which looks like this:

export class UserMed {
    constructor(
        public drug_id: number,
        public username: string,
    ) {
    }
}

But hitting the route with a userMed type in the request body, is causing this error:

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: null; nested exception is com.fasterxml.jackson.databind.JsonMappingException: N/A
 at [Source: (PushbackInputStream); line: 1, column: 28] (through reference chain: com.example.configbackendspring.entities.UserMed["username"])]

Here is the POST mapping/route that is hit

    @PostMapping("/addNewUserMed")
    public String addNewUser(@RequestBody UserMed userMed) {
        userMedService.addNewUserMed(userMed);
        return "success";
    }

And here is the addNewUserMed method:

   public void addNewUserMed(UserMed userMed) {
        userMedRepository.save(userMed);
    }

I think the issue is that since it's a composite key which is used, the UserMed which is being sent from the frontend contains field 'username' and 'drug_id', but the Spring UserMed data type has a composite key and so only has fields 'userMedId.drug_id' and 'userMedId.username'.

Can someone please advise how to make the save operation work correctly?

Thank you

Upvotes: 5

Views: 8073

Answers (1)

Mehrdad Yami
Mehrdad Yami

Reputation: 1681

You must not use your entity for the controller, instead, you must create a DTO corresponding to your entity and use it on the controller.

So in your scenario, you must first create a UserMedDto and UserMedIdDto and use them in the controller, after getting them you must convert them into Entity and save them.

Create UserMedDto

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserMedDto {

    private UserMedIdDto userMedIdDto;

    //Setters and Getters
}

Create UserMedIdDto

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
    public class UserMedIdDto {

    @NonNull
    public int drug_id;

    @NonNull
    public String username;

    //Setters and Getters
}

Use Dtos in the addNewUser controller

@PostMapping("/addNewUserMed")
    public String addNewUser(@RequestBody UserMedDto userMedDto) {
        //Convert UserMedDto to UserMedEntity then send it to addNewUserMed
        userMedService.addNewUserMed(userMedEntity);
        return "success";
    }

These links might be useful

1- Automatically Mapping DTO to Entity on Spring Boot APIs

2- DTO to Entity and Entity to DTO Conversion

Upvotes: -1

Related Questions