Mukesh Kumar
Mukesh Kumar

Reputation: 101

Foreign Key is not getting saved in Spring Rest Application

I am trying to implement bidirectional one to many relationship using Spring Data JPA. I have created test cases for save and get data and there is no issue in the mapping and data is getting saved in both the tables. But when I am trying to create data by hitting the Post request, foreign key is not getting saved. Mapping between Customer and Phone is bidirectional one to many.

OneToMany1Application

package com.jwt.onetomany;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OneToMany1Application {

    public static void main(String[] args) {
        SpringApplication.run(OneToMany1Application.class, args);
    }
}

DemoController

package com.jwt.onetomany.controller;

import java.net.URI;
import java.util.List;

import org.apache.tomcat.jni.Poll;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import com.jwt.onetomany.entity.Customer;
import com.jwt.onetomany.repo.CustomerRepository;

@RestController
public class DemoController {

    @Autowired
    CustomerRepository customerRepository;

    @GetMapping("/getall")
    ResponseEntity<List<Customer>> getAllCustomers() {

        Iterable<Customer> findAll = customerRepository.findAll();
        List<Customer> customers = (List<Customer>) findAll;
        return new ResponseEntity<List<Customer>>(customers, HttpStatus.OK);

    }

    @PostMapping(path = "/customers", produces = MediaType.APPLICATION_XML_VALUE)
    public ResponseEntity<?> createPoll(@RequestBody Customer customer) {

        customerRepository.save(customer);

        // Set the location header for the newly created resource
        HttpHeaders responseHeaders = new HttpHeaders();
        URI newPollUri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(customer.getId())
                .toUri();
        responseHeaders.setLocation(newPollUri);

        return new ResponseEntity<>(null, responseHeaders, HttpStatus.CREATED);
    }

}

Customer

package com.jwt.onetomany.entity;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String name;

    @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<PhoneNumber> phoneNumbers;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(Set<PhoneNumber> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

    public void addPhoneNumber(PhoneNumber number) {
        if (number != null) {
            if (phoneNumbers == null) {
                phoneNumbers = new HashSet<>();
            }
            number.setCustomer(this);
            phoneNumbers.add(number);
        }
    }

}

PhoneNumber

package com.jwt.onetomany.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIgnore;

@Entity
@Table(name="phone_number")
public class PhoneNumber {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;
    private String number;
    private String type;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    @JsonIgnore
    private Customer customer;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

}

CustomerRepository

package com.jwt.onetomany.repo;

import org.springframework.data.repository.CrudRepository;

import com.jwt.onetomany.entity.Customer;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

}

Database use is mysql:

SQL Script
-----------
CREATE TABLE `customer` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;


CREATE TABLE `phone_number` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `customer_id` int(11) DEFAULT NULL,
  `number` varchar(20) DEFAULT NULL,
  `type` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK1j552es3t8oswmbjr0rw15ew6` (`customer_id`),
  CONSTRAINT `FK1j552es3t8oswmbjr0rw15ew6` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`),
  CONSTRAINT `phone_number_ibfk_1` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

Upvotes: 3

Views: 7137

Answers (3)

Divas
Divas

Reputation: 453

"Remove the mappedBy = "customer" attribute and add the @JoinColumn(name = "customer_id") annotation to Set in the Customer class"

This might seem to solve the problem generating the foreign key in the child table (phone_number) in this case, however the solution will fall back internally to a OnetoMany relationship unidirectional, where the number of queries generated by jpa will be 2n+1.

do the following in the Customer class:

@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JsonManagedReference(value = "cust_phone")
private Set<PhoneNumber> phoneNumbers;

and in the PhoneNumber Class:

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "customer_id")
@JsonIgnore
@JsonBackReference(value = "cust_phone")
private Customer customer;

This sets the foreign key properly and the number of queries generated is also N+1.

Upvotes: 0

Jeya Nandhana
Jeya Nandhana

Reputation: 358

You cannot show the data which one using foreign key. Suppose you need to show the values means to manually the data using the foreign key.

Upvotes: 0

Venkata Ramana
Venkata Ramana

Reputation: 99

Remove the mappedBy = "customer" attribute and add the @JoinColumn(name = "customer_id") annotation to Set<PhoneNumber> in the Customer class.

The @JoinColumn annotation can be optionally removed from PhoneNumber class.

The mappedBy attribute specifies that the relation is owned by the other class. So, in the above case, the responsibility of adding the Foreign Key is with the child class. Replacing it with @JoinColumn annotation specifies that the ownership of the relation is with the parent class.

Refer: JPA JoinColumn vs mappedBy

Upvotes: 6

Related Questions