Внешний ключ не сохраняется в приложении 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
аннотация указывает, что владельцем отношения является родительский класс.
Вы не можете показать данные, которые используют внешний ключ. Предположим, вам нужно показать значения, чтобы вручную ввести данные с помощью внешнего ключа.
"Удалите атрибут 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.