Внешний ключ не сохраняется в приложении Spring Rest

Я пытаюсь реализовать двунаправленное отношение один ко многим, используя Spring Data JPA. Я создал контрольные примеры для сохранения и получения данных, и в отображении нет проблем, и данные сохраняются в обеих таблицах. Но когда я пытаюсь создать данные, нажав на запрос Post, внешний ключ не сохраняется. Отображение между клиентом и телефоном является двунаправленным для многих.

Ниже мой код. 1) пакет com.jwt.onetomany;

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

@SpringBootApplication открытый класс OneToMany1Application {

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

}

2)

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);
    }

}

3)

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);
        }
    }

}

4)

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;
    }

}

5)

package com.jwt.onetomany.repo;

import org.springframework.data.repository.CrudRepository;

import com.jwt.onetomany.entity.Customer;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

}

Использование базы данных MySQL

SQL Script

СОЗДАТЬ СТОЛ customer (id int (11) NOT NULL AUTO_INCREMENT, name varchar (20) ПО УМОЛЧАНИЮ NULL, ПЕРВИЧНЫЙ КЛЮЧ (id)) ENGINE = InnoDB AUTO_INCREMENT = 13 DEFAULT CHARSET = utf8;

6) СОЗДАТЬ СТОЛ phone_number (id int (11) NOT NULL AUTO_INCREMENT, customer_id int (11) DEFAULT NULL, number varchar (20) DEFAULT NULL, type varchar (20) ПО УМОЛЧАНИЮ NULL, ПЕРВИЧНЫЙ КЛЮЧ (id), КЛЮЧ FK1j552es3t8oswmbjr0rw15ew6 (customer_id), ОГРАНИЧЕНИЕ FK1j552es3t8oswmbjr0rw15ew6 ИНОСТРАННЫЙ КЛЮЧ (customer_id) РЕКОМЕНДАЦИИ customer (id), ОГРАНИЧЕНИЕ phone_number_ibfk_1 ИНОСТРАННЫЙ КЛЮЧ (customer_id) РЕКОМЕНДАЦИИ customer (id)) ENGINE = InnoDB AUTO_INCREMENT = 13 DEFAULT CHARSET = utf8;

1 ответ

Удалить mappedBy = "customer" атрибут и добавить @JoinColumn(name = "customer_id") аннотация к Set<PhoneNumber> в классе Customer.

@JoinColumn аннотацию можно удалить из PhoneNumber учебный класс.

mappedBy Атрибут указывает, что отношение принадлежит другому классу. Таким образом, в приведенном выше случае ответственность за добавление внешнего ключа лежит на дочернем классе. Заменить его на @JoinColumn аннотация указывает, что владельцем отношения является родительский класс.

См. JPA JoinColumn против mappedBy

Вы не можете показать данные, которые используют внешний ключ. Предположим, вам нужно показать значения, чтобы вручную ввести данные с помощью внешнего ключа.

"Удалите атрибут mappedBy = "customer"и добавьте аннотацию @JoinColumn(name = "customer_id") в Set в классе Customer"

В этом случае может показаться, что это решает проблему с генерацией внешнего ключа в дочерней таблице (phone_number), однако внутреннее решение вернется к однонаправленным отношениям OnetoMany, где количество запросов, сгенерированных jpa, будет 2n+1.

сделайте следующее в классе Customer:

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

и в классе PhoneNumber:

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

Это правильно устанавливает внешний ключ, и количество сгенерированных запросов также равно N+1.

Другие вопросы по тегам