Spring 使用 JSON RequestBody 引导保存嵌套实体
Spring Boot save nested Entity with JSON RequestBody
我正在尝试为学校创建一个 Rest API project.Therefor 我正在尝试 save/edit 一个嵌套对象。
我有两个双向实体,如下所示:
实体A
@Entity
public class EntityA {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@Column(name = "id", nullable = false)
@JsonProperty("id")
private int id;
@Column(name = "field1", nullable = false, length = -1)
@JsonProperty("field1")
private String field1;
@Column(name = "field2", nullable = false, length = -1)
@JsonProperty("field2")
private String field2;
@OneToMany(mappedBy = "entityA", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JsonProperty("entityB")
private List<EntityB> entityB;
public EntityA() {
}
//Getter+Setter
}
实体B
@Entity
public class EntityB {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@Column(name = "id", nullable = false)
@JsonProperty("id")
private int id;
@Column(name = "field1", nullable = false)
@JsonProperty("field1")
private Date field1;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(...)
@JsonProperty("entityA")
private EntityA entityA;
public EntityB() {
}
//Getter+Setter
}
作为 RequestBody,我将得到 JSON,它应该看起来像这样。
{
"field1": "Test",
"field2": "User",
"entityB": [
{
"field1": "30.03.2022"
}
]
}
现在 Spring 将自动映射字段,但是一旦我尝试将其保存到我的数据库中,我就会收到错误消息,因为 EntityB
中 EntityA
的关系是空的。
我看到了一个解决方案,我应该遍历 EntityB
列表并添加 EntityA
。我用 for-each 尝试了它,但它仍然说它是空的。
我做错了什么?
public EntityA createEntityA(EntityA entityA) {
for(EntityB entityB : entityA.getEntityB()){
entityB.setEntityA(entityA);
}
return entityARepository.save(entityA);
}
编辑:
控制器
@PostMapping(value = {"/json/entitya/"})
@ResponseBody
public EntityA createEntityAJson(@RequestBody EntityA entityA) {
return entityAService.createEntityA(entityA);
}
服务
@Service
public class EntityAService {
@Autowired
private EntityARepository entityARepository;
public EntityA createEntityA(EntityA entityA) {
return entityARepository.save(entityA); //in this line the error appears
}
}
错误信息
null value in column "entityA" violates not-null constraint
我猜这里发生的是 non-nullable 数据类型的 id 字段或 JPA 注释中的一些其他隐藏字段被 json 反序列化让 JPA 了解它们是新实体。在 Java 代码中手动创建这些实体可能会解决问题。
您不应将实体 类 重新用作 API 的数据传输对象。让 类 同时包含 database-specific 注释和 JSON 序列化的注释是一个坏主意,它违反了 single-responsibility 原则 (SRP)。
为您的 API 端点创建单独的 DTO 类,然后从数据库中读取实体并在保存之前将 DTO 对象中的值复制到实体。
// Receive DTO
// Read entity from DB if update or create new entities if insert
// Copy values from DTO to entitiy
// Save entity
我想如果你应用这个模式,你的问题就会消失。
@Service
public class EntityAService {
@Autowired
private EntityARepository entityARepository;
@Autowired
private EntityBRepository entityBRepository;
public EntityA createEntityA(EntityA entityA) {
// create an empty arrayList to stock the entities B retrieveed from the DB
List<EnityB> lst = new ArrayList<>();
// get the entities B from the JSON and sabe it to the DB
for(EntityB entityB : entityA.getEntityB()){
entityB.setEntityA(entityA);
entityBRepository.save(entityB); // you should save entities B to the DataBase before
Optional<EntityB > opt = entityBRepository.findById(entityB.getId());
EntityB b = opt.get();
// add the entities B retrieved from the DB to the arrayList
lst.add(b);
}
// set the EntityB list with the new List from the DB ( include ids ..)
entityA.setEntityB(lst);
// save the entityA to the DB
return entityARepository.save(entityA);
}
}
我正在尝试为学校创建一个 Rest API project.Therefor 我正在尝试 save/edit 一个嵌套对象。 我有两个双向实体,如下所示:
实体A
@Entity
public class EntityA {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@Column(name = "id", nullable = false)
@JsonProperty("id")
private int id;
@Column(name = "field1", nullable = false, length = -1)
@JsonProperty("field1")
private String field1;
@Column(name = "field2", nullable = false, length = -1)
@JsonProperty("field2")
private String field2;
@OneToMany(mappedBy = "entityA", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JsonProperty("entityB")
private List<EntityB> entityB;
public EntityA() {
}
//Getter+Setter
}
实体B
@Entity
public class EntityB {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@Column(name = "id", nullable = false)
@JsonProperty("id")
private int id;
@Column(name = "field1", nullable = false)
@JsonProperty("field1")
private Date field1;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(...)
@JsonProperty("entityA")
private EntityA entityA;
public EntityB() {
}
//Getter+Setter
}
作为 RequestBody,我将得到 JSON,它应该看起来像这样。
{
"field1": "Test",
"field2": "User",
"entityB": [
{
"field1": "30.03.2022"
}
]
}
现在 Spring 将自动映射字段,但是一旦我尝试将其保存到我的数据库中,我就会收到错误消息,因为 EntityB
中 EntityA
的关系是空的。
我看到了一个解决方案,我应该遍历 EntityB
列表并添加 EntityA
。我用 for-each 尝试了它,但它仍然说它是空的。
我做错了什么?
public EntityA createEntityA(EntityA entityA) {
for(EntityB entityB : entityA.getEntityB()){
entityB.setEntityA(entityA);
}
return entityARepository.save(entityA);
}
编辑:
控制器
@PostMapping(value = {"/json/entitya/"})
@ResponseBody
public EntityA createEntityAJson(@RequestBody EntityA entityA) {
return entityAService.createEntityA(entityA);
}
服务
@Service
public class EntityAService {
@Autowired
private EntityARepository entityARepository;
public EntityA createEntityA(EntityA entityA) {
return entityARepository.save(entityA); //in this line the error appears
}
}
错误信息
null value in column "entityA" violates not-null constraint
我猜这里发生的是 non-nullable 数据类型的 id 字段或 JPA 注释中的一些其他隐藏字段被 json 反序列化让 JPA 了解它们是新实体。在 Java 代码中手动创建这些实体可能会解决问题。
您不应将实体 类 重新用作 API 的数据传输对象。让 类 同时包含 database-specific 注释和 JSON 序列化的注释是一个坏主意,它违反了 single-responsibility 原则 (SRP)。
为您的 API 端点创建单独的 DTO 类,然后从数据库中读取实体并在保存之前将 DTO 对象中的值复制到实体。
// Receive DTO
// Read entity from DB if update or create new entities if insert
// Copy values from DTO to entitiy
// Save entity
我想如果你应用这个模式,你的问题就会消失。
@Service
public class EntityAService {
@Autowired
private EntityARepository entityARepository;
@Autowired
private EntityBRepository entityBRepository;
public EntityA createEntityA(EntityA entityA) {
// create an empty arrayList to stock the entities B retrieveed from the DB
List<EnityB> lst = new ArrayList<>();
// get the entities B from the JSON and sabe it to the DB
for(EntityB entityB : entityA.getEntityB()){
entityB.setEntityA(entityA);
entityBRepository.save(entityB); // you should save entities B to the DataBase before
Optional<EntityB > opt = entityBRepository.findById(entityB.getId());
EntityB b = opt.get();
// add the entities B retrieved from the DB to the arrayList
lst.add(b);
}
// set the EntityB list with the new List from the DB ( include ids ..)
entityA.setEntityB(lst);
// save the entityA to the DB
return entityARepository.save(entityA);
}
}