5

I have two entity classes Country and Language having bi-directional one to many relationship.

Below are the entity classes:

@Entity
@Table(name = "COUNTRY")
public class Country {

    @Id
    @GeneratedValue
    @Column(name = "COUNTRY_ID")
    private Long id;

    @Column(name = "COUNTRY_NAME")
    private String name;

    @Column(name = "COUNTRY_CODE")
    private String code;

    @JacksonXmlElementWrapper(localName = "languages")
    @JacksonXmlProperty(localName = "languages")
    @OneToMany(mappedBy = "country", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    List<Language> languages;
    // getters and setters
}

And...

@Entity
@Table(name = "LANGUAGE")
public class Language {
    @Id
    @GeneratedValue
    @Column(name = "LANGUAGE_ID")
    private Long id;

    @Column(name = "LANGUAGE_NAME")
    private String name;

    @ManyToOne
    @JoinColumn(name = "COUNTRY_ID")
    @JsonIgnore
    private Country country;
    //getters and setters
}

Below is my Rest controller:

@RestController
@RequestMapping("/countries")
public class CountryRestController {

    private final ICountryRepository iCountryRepository;

    @Autowired
    public CountryRestController(ICountryRepository iCountryRepository) {
        this.iCountryRepository = iCountryRepository;
    }

    @PostMapping("/country")
    public ResponseEntity<?> postCountryDetails(@RequestBody Country country) {
        Country savedCountry = this.iCountryRepository.save(country);

        URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
                .buildAndExpand(savedCountry.getId()).toUri();
        return ResponseEntity.created(location).build();
    }

 //other methods

}

I'm trying to save below JSON:

{
  "name": "Ireland",
  "code": "IRE",
  "languages": [
    {
      "name": "Irish"
    }
  ]
}

The problem is that the language (child) foreign key is always null but other properties are getting inserted. I have used @JsonIgnore on property Country country of Language class because it was causing issues with request size as I have another API fetching data of Country along with its Languages.

Please guide.

Pawan Kumar
  • 259
  • 1
  • 6
  • 17

3 Answers3

6

You can do it in this way :

Country newCountry = new Country(country.getName());

ArrayList < Language > langList = new ArrayList<>();

for (Language lang : country.getLanguages()) {
     langList.add( new Language(language.getName(), newCountry ) ) ;
}

newCountry.setLanguages( langList );

iCountryRepository.save(newCountry);

PS : Don't forget to add appropriate constructors. Also it is mandatory to add a default constructor if you are doing constructor overloading like this :

public Country() {}

public Country(String name) {this.name = name } 
Vivek Bansal
  • 216
  • 2
  • 8
  • thanks. It worked for me. Can you provide me some explanation on your answer? Why it is not working with my way? – Pawan Kumar Sep 02 '18 at 10:28
  • 1
    I think this is because the input Object (country) has childs (languages) and childs have no idea that who their parent object is in Hibernate Context. So , you need to create new Parent and child and explicitly set the parent of childs. – Vivek Bansal Sep 02 '18 at 10:38
  • 1
    That's why when you were doing your way initially , it was inserting in both the tables although with no foreign key .. but when you told the context (using contructor ) that which Parent object to point , then it first inserted the parent object and map foreign key in child table. – Vivek Bansal Sep 02 '18 at 10:40
  • How can this be achieved only using domain classes? without creating objects twice? – dryleaf Nov 25 '18 at 10:14
4

You can do it in this way also. Here it doesn't create new objects. In the same object which is parsing it creates the relationship in language objects.

@PostMapping("/country")
public Country postCountryDetails(@RequestBody Country country) {

    if( country.getLanguages().size() > 0 )
    {
        country.getLanguages().stream().forEach( countryItem -> {
            countryItem.setCountry( country );
        } );
    }
    return country;
}
dasunse
  • 2,839
  • 1
  • 14
  • 32
0

Update the setter for languages in Country class to the below :

 public void setLanguages(List<Language> languages) {
        this.languages = languages;
        languages.forEach(entity -> entity.setCountry(this));
    }
Alferd Nobel
  • 3,185
  • 2
  • 30
  • 35