Zeeshan
Zeeshan

Reputation: 149

how to save parent object containing child object using spring boot in @ManyToOne unidirectional mapping?

I am new in spring boot. I've two model classes Party(parent) and PartyCategory(child). PartyCategory stores data id, labelAr and labelEn successfully.

Now I am passing child id in json request and getting null values for labelAr and labelEn in json response as pasted below. Can someone please help what and doing wrong here.

I've pasted my code as well.

Json Request:

{

    "name": "Party A",
    "description": "Description of Party A",
    "category": {
        "id": 1
    }
}

Json response;

{
    "id": 6,
    "name": "Party A",
    "description": "Description of Party A",
    "category": {
        "id": 1,
        "labelAr": null,
        "labelEn": null
    }
}

Party.java:

   public class Party {

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Integer id;
        private String name;
        private String description;
        @ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
        @JoinColumn(name = "category_id")
        private PartyCategory category;

        ....setters and getters
   }

PartyCategory.java:-

public class PartyCategory {

    @Id
    @GeneratedValue
    private Integer id;
    private String labelAr;
    private String labelEn;

    ...setters and getters..

Repositories:

public interface PartyCategoryRepository extends JpaRepository<PartyCategory, Integer> {

}

public interface PartyRepository extends JpaRepository<Party, Integer> {

}

Services:

public class PartyServiceImpl {
    @Autowired
    PartyRepository partyRepository;

public Party saveParty(Party party) {

        return partyRepository.save(party);
    }

Controller:

@RestController
public class PartyController {

    @Autowired
    PartyServiceImpl partyServiceIml;


    @PostMapping(value = "/party/save")
    public Party saveParty(@RequestBody Party party ) {
        Party returnedParty = partyServiceIml.saveParty(party);
        return returnedParty;
    }
}

Upvotes: 1

Views: 2286

Answers (2)

Alan Hay
Alan Hay

Reputation: 23226

The problem is that the category you are posting is not recognised as being an existing category.

You can then do something like the below. Firstly, Create a Jackson converter class to customise the Json deserialisation. I was not sure if these were Spring managed but they are so you can then inject the necessary repository.

import com.fasterxml.jackson.databind.util.StdConverter;

@Component
public class CategoryConverter extends StdConverter<Integer, PartyCategory> {

    @Autowired
    private PartyCategoryRepository repo;

    @Override
    public PartyCategory convert(Integer value) {
        return repo.findById(value).get();
    }
}

Then update your entity as follows so that the category Json property will be handled via the converter created above. Note that in reality I would use a Jackson mix-in to apply this custom deserializer as this would avoid 'polluting' the entity class with Json processing instructions. You can look up how to do that.

@Entity
public class Party {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    private String description;

    @ManyToOne
    @JoinColumn(name = "category_id")
    @JsonDeserialize(converter = CategoryConverter.class) //in real life use a 'mix-in'
    private PartyCategory category;
}

Then you can post JSON as below where we simply specify the id of an existing category:

{

    "name": "Party A",
    "description": "Description of Party A",
    "category": 1
}

By enhancing this solution to use mix-ins as suggested then it is possible then to cleanly separate the view model from the entity model without having to create a DTO layer which will typically largely duplicate the entity model and which will required tedious mapping code to handle the conversions.

Upvotes: 1

Lungu Daniel
Lungu Daniel

Reputation: 844

First of all, it's not a good practice to use the same entity for the database and also for the rest services. They should be separate entities, normally the entities for the rest services are called DTO (Data Access Objects).

Now, regarding your problem, it's normal what it's happening in your code, because you overwrite the PartyCategory labelAr and labelEn associated to the ID 1 with null values when you save your new Party, because you didn't provide any value for those two labels.

return partyRepository.save(party);

If you want to avoid this problem you have to retrieve the PartyCategory data first from the database, set to the Party entity and after that to save it to the database. Something like this:

public class PartyServiceImpl {
    @Autowired
    PartyRepository partyRepository;

    @Autowired
    PartyCategoryRepository partyCategoryRepository;

    public Party saveParty(Party party) {
        PartyCategory partyCategory = partyCategoryRepository.findById(party.getPartyCategory().getId());

        party.setPartyCategory(partyCategory);

        return partyRepository.save(party);
    }

Upvotes: 0

Related Questions