Java Spring将带有外键的数据从@RequestBody插入到DB中



我想将从@RequestBody获得的数据插入到mariaDB数据库中。目前,我可以在没有外键的情况下存储数据,没有任何问题,但现在我还需要存储外键,我不知道如何做到这一点。

这就是我的实体现在的样子:

超类:

package webtoolbackend.Model.Superclass;
import webtoolbackend.Model.Reference.Reference_Category;
import javax.persistence.*;
@MappedSuperclass
public abstract class Category {
private long category_ID;
private String title;
private String description;
public Category() {
}
public Category(long category_ID, String title, String description) {
this.category_ID = category_ID;
this.title = title;
this.description = description;
}
public Category(String title, String description) {
this.title = title;
this.description = description;
}
@Id
@Column(name ="Category_ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
public long getCategory_ID() {
return category_ID;
}
public void setCategory_ID(long category_ID) {
this.category_ID = category_ID;
}
@Column(name ="Title")
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Column(name ="Description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

类别:

package webtoolbackend.Model.DE;
import webtoolbackend.Model.Reference.Reference_Category;
import webtoolbackend.Model.Superclass.Category;
import javax.persistence.*;
import java.util.Set;
@Entity
@Table(name="Category_DE")
public class Category_DE extends Category {
private Sector_DE sector_de;
private Set<Question_DE> questions_de;
private Set<Reference_Category> reference_categories;
private long sector_id;
public Category_DE() {
super();
}
public Category_DE(long category_ID, String title, String description) {
super(category_ID, title, description);
}
public Category_DE(String title, String description, Sector_DE sector_de) {
super(title, description);
this.sector_de = sector_de;
}
public Category_DE(String title, String description, long sector_id) {
super(title, description);
this.sector_id = sector_id;
}
@ManyToOne
@JoinColumn(name="Sector_IDFS")
public Sector_DE getSector_de(){
return sector_de;
}
public void setSector_de(Sector_DE sector_de) {
this.sector_de = sector_de;
}
@OneToMany(mappedBy="category_de", cascade=CascadeType.ALL)
public Set<Question_DE> getQuestions_de() {
return questions_de;
}
public void setQuestions_de(Set<Question_DE> questions_de) {
this.questions_de = questions_de;
}
@OneToMany(mappedBy="category_de", cascade = CascadeType.ALL)
public Set<Reference_Category> getReference_categories() {
return reference_categories;
}
public void setReference_categories(Set<Reference_Category> reference_categories) {
this.reference_categories = reference_categories;
}
}

我的控制器:

@CrossOrigin(origins = "*")
@RestController
@RequestMapping("/api")
public class Category_Controller {
@Autowired
Category_Repository category_repository;
@PostMapping("/category-de")
public ResponseEntity<Category> createCategory(@RequestBody Category_DE category_de) {
try{
Category_DE savedCategory = category_repository
.save(new Category_DE(category_de.getTitle(), category_de.getDescription(), category_de.getSector_de()));
return new ResponseEntity<>(savedCategory, HttpStatus.CREATED);
}
catch (Exception e){
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

来自RequestBody的我的JSON:

{
"title": "test",
"description": "testDesc",
"sector_de": {
"sector_id": 1,
"title": "test"
}
}

由于使用和@RequestBody相同的实体对象,所以需要考虑一些事情才能将它们保存到数据库中。让我们逐一看看

当Category和Sector都是新对象时1

当您期望API每次都创建新对象时,这种方法将起作用,因为使用映射通过JPA很容易将更改级联到子表。所以假设你的请求类似于

{
"title": "test",
"description": "testDesc",
"sector_de": {
"title": "test"
}
}

这里,对象(主和内部(对象都缺少id字段,所以直接将其保存到数据库是安全的,如果我们在父表上使用此字段,则两个表都将接收条目。由于json将串行化为Category对象,如果定义了setter,它也将初始化父Sector_DE对象。

@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name="Sector_IDFS")
public Sector_DE getSector_de(){
return sector_de;
}

因此,springJPA将保存Category对象,由于cascade,它也将保存Sector_DE对象。你的请求与此不相似,所以这不适用于本案,但我提到了它,因为这些信息对你的案件很有用。

当类别是新的并且扇区存在时(您的情况(

现在,您的请求已由sector_id提供

{
"title": "test",
"description": "testDesc",
"sector_de": {
"sector_id" : 1,
"title": "test"
}
}

因此,您的Category请求对象将使用sector_id字段初始化Sector_DE对象,如果您尝试按照上面的代码直接保存它,spring数据JPA可能会给您关于分离对象的错误。

原因是,持久性上下文可能已经有了那个对象,而且由于您的对象是从外部创建的,而不是从存储库中提取的,所以持久性上下文不知道它。如果数据库中没有任何具有相同id的Sector,它可能会起作用。

因此,这是一种更新请求,您正在向现有扇区更新/添加类别,因此需要从数据库中获取该sector_id的相关对象。

在这种情况下,你必须首先检查是否有一个扇区具有相同的id,要做到这一点,你需要一个类似于CategoryRepostiorySectorRepository,然后你会像这个一样检查它

public ResponseEntity<Category> createCategory(@RequestBody Category_DE category_de) {
try{
//assuming SectorRepository available to this function
Sector_DE sector = 
sectorRepository.findById(category_de.getSector().getSectorId())
.orElseThrow(() -> new IllegalArgumentException());

category_de.setSector_de(sector); //Important! will save foriegn key to table
Category_DE savedCategory = category_repository
.save(new Category_DE(category_de.getTitle(), category_de.getDescription(), category_de.getSector_de()));
return new ResponseEntity<>(savedCategory, HttpStatus.CREATED);
}
catch (Exception e){
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}

由于我们在这里操作的是子级,所以我们不需要将更改级联到父级,除非您也想更新父级(Sector_DE(的一些属性,所以,您现有的映射是可以的。

@ManyToOne
@JoinColumn(name="Sector_IDFS")
public Sector_DE getSector_de(){
return sector_de;
}

脚注


1虽然,我提到了第一种情况,以显示在两个都是新对象的情况下映射是如何工作的,但通常我们不会以这种方式从子对象创建新对象,我们应该总是先保存父对象,然后将子对象添加到其中。因此,这是一个两步过程,应该避免从子对象到父对象的级联更改

最新更新