我正在尝试为学校项目创建Rest API。因此,我正在尝试保存/编辑一个嵌套对象。我有两个双向实体,看起来像这样:
EntityA
@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将自动映射字段,但一旦我尝试将其保存到DB中,就会出现错误,因为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
@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);
}
}
我猜这里发生的事情是,JPA注释中不可为null的数据类型或其他隐藏字段的id字段被json反序列化设置为错误的值,以便JPA理解它们是新实体。在Java代码中手动创建这些实体可能会解决这个问题。
您不应该将实体类重用为API的数据传输对象。让类同时包含特定于数据库的注释和用于JSON序列化的注释是个坏主意,这违背了单一责任原则(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
我认为如果你采用这种模式,你的问题就会消失。