I'm trying to create a Rest API for a school project.Therefor I'm trying to save/edit a nested Object. I have two bidirectional entities which look like this:
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
}
EntityB
@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
}
As RequestBody I will get JSON which should look like this.
{
"field1": "Test",
"field2": "User",
"entityB": [
{
"field1": "30.03.2022"
}
]
}
Right now Spring will automatically map the fields but as soon I try to save it to my DB I will get an error, because the relation in EntityB
for EntityA
is empty.
I've seen a solution, that I should loop through the EntityB
list and add EntityA
. I tried it with a for-each but it still sais it null.
What am I doing wrong?
public EntityA createEntityA(EntityA entityA) {
for(EntityB entityB : entityA.getEntityB()){
entityB.setEntityA(entityA);
}
return entityARepository.save(entityA);
}
Edit:
Controller
@PostMapping(value = {"/json/entitya/"})
@ResponseBody
public EntityA createEntityAJson(@RequestBody EntityA entityA) {
return entityAService.createEntityA(entityA);
}
Service
@Service
public class EntityAService {
@Autowired
private EntityARepository entityARepository;
public EntityA createEntityA(EntityA entityA) {
return entityARepository.save(entityA); //in this line the error appears
}
}
Error message
null value in column "entityA" violates not-null constraint
CodePudding user response:
I'm guessing that what is happening here is that the id fields which are of a non-nullable datatype or some other hidden field from the JPA annotations get set to the wrong value by the json deserialization for JPA to understand that they are new entities. Creating these entities manually in the Java code might solve the issue.
You shouldn't reuse your entity classes as data transfer object for your API. Having classes containing both database-specific annotations and annotations for JSON serialization is a bad idea and it goes against the single-responsibility principle (SRP).
Create separate DTO classes for your API endpoint, then read the entities from the database an copy the values from the DTO object to the entities before saving.
// Receive DTO
// Read entity from DB if update or create new entities if insert
// Copy values from DTO to entitiy
// Save entity
I think your problems will go away if you apply this pattern.
CodePudding user response:
@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);
}
}