我想将从@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
,要做到这一点,你需要一个类似于CategoryRepostiory
的SectorRepository
,然后你会像这个一样检查它
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虽然,我提到了第一种情况,以显示在两个都是新对象的情况下映射是如何工作的,但通常我们不会以这种方式从子对象创建新对象,我们应该总是先保存父对象,然后将子对象添加到其中。因此,这是一个两步过程,应该避免从子对象到父对象的级联更改