I don't understand why my weight record Repository is not called in spite of my code. I have written some logs before and after the weight record repository call. In my console, I can see that logs are called before and after, but not my repository. I have a code 200 OK, but in my database, my data is always here. I don't understand why. My Tomcat port is set to port 7777. I can create and read data but not delete it.
Just below my code : ENTITY, CONTROLLER, REPOSITORY and SERVICE.
CodePudding user response:
The reason
The reason of the issue is this part of the code
personConnected.getWeightsList().contains(weightRecordToDelete)
Why it happens?
There is a spring data property spring.jpa.open-in-view
and it is true
by default. It means that JPA Persistent Context (Hibernate Session) is opened during entire HTTP request. What is this spring.jpa.open-in-view=true property in Spring Boot?
If Persistent Context is opened, fetch = FetchType.LAZY
property in the code below doesn't work at all.
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "person")
private List<WeightRecord> weightsList = new ArrayList<WeightRecord>();
When you do personConnected.getWeightsList().conatins()
, Hibernate loads weightsList
in the background. That's why the last SQL log entry is logged.
select
weightslis0_.person_id_person as person_i8_3_0_
...
from
weight_record weightslis0_
where
weightslis0_.person_id_person=?
So when you delete a WeightRecord
, it remains in the Person.weightsList
, because of it was loaded by personConnected.getWeightsList().conatins()
.
When HTTP request is completed, Persistent Context becomes closed, and Hibernate flushes all changes to the database. There is cascade = CascadeType.ALL
on the Person
side, so Hibernate should keep in mind a deleted WeightRecord
in the weightsList
. So it does nothing, because you could delete WeightRecord
and insert it by some reasons again.
You can verify statements above just by removing personConnected.getWeightsList().conatins()
part of the code, delete will start work.
How to solve
Set
spring.jpa.open-in-view=false
in theapplication.property
. So you will haveLazyInitializationException
withpersonConnected.getWeightsList().conatins()
Remove
personConnected.getWeightsList().conatins()
code. You can do the same just comparingWeightRecord.Person.id
and a currentPerson.id
.
Optional<Person> personConnected = personRepository.findById(appUserConnectedId);
if (personConnected.isPresent() ) {
WeightRecord weightRecordToDelete = weightRecordRepository.findById(weightId).orElseThrow();
Long userPersonId = personConnected.get().getIdPerson();
Long recordPersonId = weightRecordToDelete.getPerson().getIdPerson();
if (Objects.equals(userPersonId, recordPersonId)) {
logger.info("SERVICE FOR DELETING");
return ResponseEntity.ok(weightRecordServiceImpl.deleteWeightById(weightRecordToDelete));
}
}
logger.info("BAD USER FOR BAD WEIGHT");
return ResponseEntity.notFound().build();
Now you can keep cascade = CascadeType.ALL
on the weightsList
on Person
side.
Notes
Always use spring.jpa.open-in-view=false
. LazyInitializationException
is your friend.
Always use fetch = FetchType.LAZY
on the @ManyToOne
part of the association. It is FetchType.EAGER
by default.
@ManyToOne(fetch = FetchType.LAZY)
private Person person;
Never use Jackson annotations like @JsonIgnore
and Hibernate Validator annotations like @Min(1)
with entities.
Use PersonEntity
class for an entity and Person
for JSON. Do mapping on the service level.
CodePudding user response:
In fact, i have juste deleted the CascadeType.ALL from my @OneToMany entity Person and all is working. I can create, read and delete records from database.
But I'am going to investigate more this problem to understand in a better way its origin.
@Entity
@Table(name = "person")
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id_person")
private Long idPerson;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "id_initial_data")
private InitialData userInitData;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "id_user")
private AppUser appUserPerson;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "person")
private List<WeightRecord> weightsList = new ArrayList<WeightRecord>();