【问题标题】:Java Spring insert data with foreign key from @RequestBody into DBJava Spring将带有外键的数据从@RequestBody插入DB
【发布时间】:2021-07-02 09:39:17
【问题描述】:

我想将从@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"
}
}

【问题讨论】:

  • 你在这里指的是哪个外键?
  • 抱歉,我说的是“Sector_IDFS”列
  • 您知道 JPA 映射(即 @OneToMany 等)是如何工作的吗?
  • 我知道如何连接两个表,但不知道它是如何工作的,以及我以后如何将它与 e 一起使用。 G。 @RequestBody

标签: java spring hibernate rest mariadb


【解决方案1】:

由于您使用与@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;
}

因此,spring JPA 将保存Category 对象,并且由于cascade,它还将保存Sector_DE 对象。您的请求与此不相似,因此这不适用于此案例,但我提到了它,因为此信息对您的案例有用。

当 Category 是新的并且 Sector 存在时(您的情况)

现在,您的请求已提供sector_id

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

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

原因是,持久化上下文可能已经拥有该对象,并且由于您的对象是从外部创建的,而不是从存储库中获取的,因此持久化上下文不知道它。如果您在数据库中没有任何具有相同id 的扇区,它可能会起作用。

所以这是一种更新请求,您正在向现有扇区更新/添加类别,因此您需要从 db 中获取该 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虽然,我提到了第一个案例来展示映射如何在两者都是新对象的情况下工作,但通常我们不会以这种方式从子端创建新对象,我们应该始终先保存父对象,然后然后将孩子添加到它。所以这是一个两步的过程,应该避免从孩子到父母的级联变化。

【讨论】:

  • 但我必须将@JsonBackReference@JsonManagedReference 添加到列表的getter 中,以避免堆栈溢出错误。这个Link 可能有帮助
  • 是的,如果您要返回实体作为响应,您也可以将 @JsonIgnore 放在这些 getter 上,否则它将是递归的,但这与这个问题无关。
猜你喜欢
  • 1970-01-01
  • 2020-10-08
  • 2021-07-07
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 2023-01-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多