Spring Data Paging and Sorting Repository not deleting data - spring

So all I'm trying to do is allow users to delete their own comments and posts in my application, and I have a form that's supposed to run a controller method that should delete it, but it's not working.
I'll show you my controller and repository to show you guys what I'm trying to do.
So here's my Controller method
#RequestMapping(value="userEdits/editComment/{commentId}/deleteComment", method=RequestMethod.POST)
public String deleteComment (#PathVariable Long commentId, #AuthenticationPrincipal User user)
{
Comment comment = commentRepo.findOne(commentId);
User savedUser = userRepo.findUserByUsername(user.getUsername());
savedUser.getCourses().remove(comment);
commentRepo.delete(comment);
return "redirect:/userEdits";
}
And I can even run this in debug mode and see that the right comment is in the commentRepo.delete(comment); line. And it runs all the through and returns the userEdits screen, just like it should, without any errors, but the comment is still there after it runs through everything.
Here's my repository class, it's pretty simple, but who knows, I could be missing something.
public interface CommentRepository extends PagingAndSortingRepository <Comment, Long>{
public Page<Comment> findByPostOrderByIdDesc(Post post, Pageable pageable);
public List<Comment> findByUserOrderByIdDesc(User user);
}
I'm confused because this should be a simple task and it appears that it's running through and returning the view I tell it to, without error.
So if anyone can see where I'm going wrong that would be great. Thanks in advance.
UPDATE
User Entity
#Entity
#Table(name = "users")
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
public class User {
private Long id;
#ValidEmail
#NotNull
#NotEmpty
private String email;
private String username;
private String password;
private University university;
private Set<Authorities> authorities = new HashSet<>();
private Set<Course> courses = new HashSet<>();
private Set<Post> posts = new HashSet<>();
private Set<Comment> comments = new HashSet<>();
private Set<StudySet> studySet = new HashSet<>();
private Set<Course> myCourses = new HashSet<Course>();
public User() {
}
public User(User user) {
this.id = user.getId();
this.email = user.getEmail();
this.username = user.getUsername();
this.password = user.getPassword();
this.university = user.getUniversity();
this.authorities = user.getAuthorities();
this.courses = user.getCourses();
this.posts = user.getPosts();
this.comments = user.getComments();
this.studySet = user.getStudySet();
this.myCourses = user.getMyCourses();
}
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user", orphanRemoval = true)
public Set<Course> getCourses() {
return courses;
}
public void setCourses(Set<Course> courses) {
this.courses = courses;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user", orphanRemoval = true)
public Set<Post> getPosts() {
return posts;
}
public void setPosts(Set<Post> posts) {
this.posts = posts;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#ManyToOne
public University getUniversity() {
return university;
}
public void setUniversity(University university) {
this.university = university;
}
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
#JsonManagedReference
#JsonIgnoreProperties(allowGetters = true, value = "user")
public Set<Comment> getComments() {
return comments;
}
public void setComments(Set<Comment> comments) {
this.comments = comments;
}
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
public Set<Authorities> getAuthorities() {
return authorities;
}
public void setAuthorities(Set<Authorities> authorities) {
this.authorities = authorities;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user", orphanRemoval = true)
public Set<StudySet> getStudySet() {
return studySet;
}
public void setStudySet(Set<StudySet> studySet) {
this.studySet = studySet;
}
#ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
#JoinTable(name = "user_myCourses")
public Set<Course> getMyCourses() {
return myCourses;
}
public void setMyCourses(Set<Course> myCourses) {
this.myCourses = myCourses;
}
}
Comment Entity
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
public class Comment {
public Long id;
#Size(min = 1, max = 140)
public String comment;
public Post post;
public User user;
#Temporal(TemporalType.DATE)
#DateTimeFormat(pattern = "dd-MMM-YYYY")
private LocalDate date;
#Temporal(TemporalType.TIME)
#DateTimeFormat(pattern = "HH:mm:ss")
private LocalTime time;
#Temporal(TemporalType.DATE)
#DateTimeFormat(pattern = "MM/dd/yyyy HH:mm:ss")
private LocalDateTime dateTime;
public Comment() {
}
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Size(min = 1, max = 140)
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
#ManyToOne
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
#ManyToOne
#JsonBackReference
#JsonIgnoreProperties(value = { "comments" }, allowGetters = true)
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public LocalTime getTime() {
return time;
}
public void setTime(LocalTime time) {
this.time = time;
}
public LocalDateTime getDateTime() {
return dateTime;
}
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Comment other = (Comment) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
public Comment(Long id, String comment, Post post, User user, LocalDate date, LocalDateTime dateTime) {
this.id = id;
this.comment = comment;
this.post = post;
this.user = user;
this.date = date;
this.dateTime = dateTime;
}
}
UPDATE
So I realized I needed to addorphanRemoval = true to the comment of the user, now I get the error the entity must not be null when I run the controller method, however it does delete the comment. But I need my app to run the method and return the view I ask it to, without the error message popping up.

The problem is in your CascadeType. You've specified:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user", orphanRemoval = true)
CascadeType.ALL means that comments are managed by user entity and you can't delete comment directly. You should read about cascade types and change it for your needs or you have orphanRemoval = true so you can simply save user after deletion and comment should be deleted for example
savedUser.getCourses().remove(comment);
userRepo.save(savedUser);

Related

I cannot remove the association in Many To Many bidirectional hibernate

I can't delete the association in the courses_student table of course and student when trying to delete a course, even if I want to cascade delete it does not work for me since there is a foreign key in courses_student, I don't know what the problem is.
I have also tried to remove the association in the courses_student table doing a update.but nothing happened.
DAO
#Override
public boolean deleteCourse(int id) {
Session currentSession = entityManager.unwrap(Session.class);
Courses course = currentSession.load(Courses.class, id);
for(Student student : course.getEstudiantes()) {
course.removeStudent(student);
}
currentSession.delete(course);
if(course.getId() == null)
return true;
else
return false;
}
Courses entity
#Entity
#Table(name = "courses")
public class Courses {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column
private String nombre;
#Column
private String descripcion;
#ManyToMany(mappedBy = "courses")
private Set<Student> Estudiantes = new HashSet<Student>();
public Courses() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public Set<Student> getEstudiantes() {
return Estudiantes;
}
public void setEstudiantes(Set<Student> estudiantes) {
Estudiantes = estudiantes;
}
public void removeStudent(Student student) {
this.Estudiantes.remove(student);
student.getCourses().remove(this);
}
}
Student entity
#Entity
#Table(name = "students")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column
private String nombre;
#Column
private String apellido;
#Column
private String dni;
#ManyToMany(fetch=FetchType.LAZY,
cascade= {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JoinTable(
name="courses_students",
joinColumns=#JoinColumn(name="id_student"),
inverseJoinColumns=#JoinColumn(name="id_course")
)
private Set<Courses> courses = new HashSet<Courses>();
public Student() {
}
public Student(String nombre, String apellido, String dni) {
this.nombre = nombre;
this.apellido = apellido;
this.dni = dni;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellido() {
return apellido;
}
public void setApellido(String apellido) {
this.apellido = apellido;
}
public String getDni() {
return dni;
}
public void setDni(String dni) {
this.dni = dni;
}
public Set<Courses> getCourses() {
return courses;
}
public void setCourses(Set<Courses> courses) {
this.courses = courses;
}
}
EDIT:
apparently it works for me, trying to update since owner side.
#Override
public boolean deleteCourse(int id) {
Session currentSession = entityManager.unwrap(Session.class);
Courses course = currentSession.load(Courses.class, id);
for(Student student : course.getEstudiantes()) {
student.removeCourse(course);
}
currentSession.update(course);
if(course.getId() == null)
return true;
else
return false;
}
It seems to me that you are missing a cascade configuration of your #ManyToMany annotation on Courses which is actually the one you are updating / deleting. Try the following:
#ManyToMany(mappedBy = "courses", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<Student> Estudiantes = new HashSet<Student>();
Also, given that you have a bi-directional relationship, you should also remove the Course from each Student courses property.

Spring JPA hibernate how to persist children (remove, add, or update) from #OneToMany parent column?

I'm trying to solve this problem since a while and I haven't achieved a 100% solution.
First of all I have to describe my problem. I'm developping a restaurant application, and amoung the Entities, I have the Entity Ingredient and as you know Ingredient can consist of other Ingredient with a specific quantity. So I created an Entity SubIngredient with an Embedded Id.
And to persist subIngredients list I tried a combinations of Cascade and orphanRemoval, each combination worked for some operation but not for the others.
I started by using CascadeType.ALL and the new subIngredient persisted successfuly from the #OneToMany propertiy, But if I try to remove an subIngredient from the subIngredients list and save this error appear.
java.lang.StackOverflowError: null
at com.mysql.cj.NativeSession.execSQL(NativeSession.java:1109) ~[mysql-connector-java-8.0.23.jar:8.0.23]......
I loked in the net for a solution and I find the I have to use orphanremoval = true I tried it but it didn't work until I changed cascade from CascadeType.ALL to CascadeType.PERSIST. But this one make the persistance of new SubIngredient this error aprear
Caused by: javax.persistence.EntityNotFoundException: Unable to find com.example.Resto.domain.SubIngredient with id com.example.Resto.domain.SubIngredientKey#51b11186........
These are my Enities:
#Entity
public class Ingredient {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
#Column(name="ID")
private long id;
#NotNull
#Column(unique=true)
private String name;
private String photoContentType;
#Lob
private byte[] photo;
#JsonIgnoreProperties({"photoContentType","photo"})
#ManyToOne
private IngredientType ingredientType;
#OneToMany(mappedBy = "embId.ingredientId", fetch = FetchType.EAGER,
cascade = CascadeType.ALL /*or orphanRemoval = true, cascade = CascadeType.PERSIST*/ )
private Set<SubIngredient> subIngredients = new HashSet<SubIngredient>();
getters and setters.....
And
#Entity
#AssociationOverrides({
#AssociationOverride(name = "embId.ingredientId",
joinColumns = #JoinColumn(name = "ING_ID")),
#AssociationOverride(name = "embId.subIngredientId",
joinColumns = #JoinColumn(name = "SUB_ING_ID")) })
public class SubIngredient {
#EmbeddedId
private SubIngredientKey embId = new SubIngredientKey();
private double quantity;
getters and setters....
And
#Embeddable
public class SubIngredientKey implements Serializable{
#ManyToOne(cascade = CascadeType.ALL)
private Ingredient ingredientId;
#ManyToOne(cascade = CascadeType.ALL)
private Ingredient subIngredientId;
getters and setters...
The stackoverflow happen because you use a Set<> with Hibernate. When Hibernate retrieves the entities from your DB, it will fill up the Set<> with each entities. In order to that, hashode/equals will be used to determine wether or not the entitie is already present in the Set<>. By default, when you call the hashcode of Ingredient, this happen:
hashcode Ingredient -> hashcode SubIngredient -> hashcode Ingredient
which will result in an infinite call of hashcode method. That's why you have a stackoverflow error.
The same thing will happen with equals/toString.
So to avoid such an issue, it's best to override hashcode, equals and toString.
I have solved the problem by making some changes to may Entities and override equals/hashcode methods thanks Pilpo.
#Embeddable
public class SubIngredientKey implements Serializable{
private Long ingredientId;
private Long subIngredientId;
/**
* #return the ingredientId
*/
#Override
public int hashCode() {
return Objects.hash(ingredientId, subIngredientId);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof SubIngredientKey)) {
return false;
}
SubIngredientKey other = (SubIngredientKey) obj;
return Objects.equals(ingredientId, other.ingredientId)
&& Objects.equals(subIngredientId, other.subIngredientId);
}
}
#Entity
public class SubIngredient {
#EmbeddedId
private SubIngredientKey embId = new SubIngredientKey();
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("ingredientId")
private Ingredient ingredient;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("subIngredientId")
private Ingredient subIngredient;
private double quantity;
#JsonIgnore
public SubIngredientKey getId() {
return embId;
}
public void setId(SubIngredientKey id) {
this.embId = id;
}
#JsonIgnoreProperties({"subIngredients","photo","photoContentType","ingredientType"})
public Ingredient getIngredient() {
return ingredient;
}
public void setIngredient(Ingredient ingredient) {
this.ingredient = ingredient;
}
#JsonIgnoreProperties({"subIngredients","photo","photoContentType","ingredientType"})
public Ingredient getSubIngredient() {
return subIngredient;
}
public void setSubIngredient(Ingredient subIngredient) {
this.subIngredient = subIngredient;
}
public double getQuantity() {
return quantity;
}
public void setQuantity(double quantity) {
this.quantity = quantity;
}
#Override
public String toString() {
return "subIngredient= " + getSubIngredient().getName() + " , quantity= " + getQuantity();
}
#Override
public int hashCode() {
return Objects.hash(ingredient,subIngredient);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof SubIngredient)) {
return false;
}
SubIngredient other = (SubIngredient) obj;
return Objects.equals(ingredient, other.ingredient) && Objects.equals(subIngredient, other.subIngredient);
}
}
#Entity
public class Ingredient {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
#Column(name="ID")
private long id;
#NotNull
#Column(unique=true)
private String name;
private String photoContentType;
#Lob
private byte[] photo;
#JsonIgnoreProperties({"photoContentType","photo"})
#ManyToOne
private IngredientType ingredientType;
#OneToMany(mappedBy = "embId.ingredientId", fetch = FetchType.EAGER, cascade =
CascadeType.ALL, orphanRemoval = true)
private Set<SubIngredient> subIngredients = new HashSet<SubIngredient>();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getPhotoContentType() {
return photoContentType;
}
public void setPhotoContentType(String photoContentType) {
this.photoContentType = photoContentType;
}
public byte[] getPhoto() {
return photo;
}
public void setPhoto(byte[] photo) {
this.photo = photo;
}
public IngredientType getIngredientType() {
return this.ingredientType;
}
public void setIngredientType(IngredientType ingredientType) {
this.ingredientType = ingredientType;
}
public Set<SubIngredient> getSubIngredients() {
return subIngredients;
}
public void setSubIngredients(Set<SubIngredient> subIngredients) {
this.subIngredients = subIngredients;
}
public void addSubIngredient(SubIngredient subIngredient) {
this.subIngredients.add(subIngredient);
}
#Override
public String toString() {
String subIngsText = "";
for(var subIngredient:this.subIngredients) {
subIngsText = subIngsText + ", " + subIngredient.toString();
}
return "{id= "+id+",name=" + name +", ingredients="+subIngsText+"}";
}
#Override
public int hashCode() {
return Objects.hash(name);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Ingredient)) {
return false;
}
Ingredient other = (Ingredient) obj;
return Objects.equals(name, other.name);
}
}

Spring Not Mapping Composite Key Entity To Database

I have two entities User and Focus for which I must store a ranking such that a User can have a ranking for many Focus', and a Focus may have many users. To handle this I've followed https://www.baeldung.com/jpa-many-to-many part 3 whereby I have the 2 following classes:
#Entity(name = "UserBaseRatings")
public class Rating {
#EmbeddedId
RatingKey id;
#ManyToOne
#MapsId("userID")
#JoinColumn(name="userID")
private User user;
#ManyToOne
#MapsId("focusID")
#JoinColumn(name="focusID")
private Focus focus;
private BigDecimal baseRating;
protected Rating(){}
public Rating(User user, Focus focus, BigDecimal baseRating) {
this.user = user;
this.focus = focus;
this.baseRating = baseRating;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Focus getFocus() {
return focus;
}
public void setFocus(Focus focus) {
this.focus = focus;
}
public BigDecimal getBaseRating() {
return baseRating;
}
public void setBaseRating(BigDecimal baseRating) {
this.baseRating = baseRating;
}
}
and
#Embeddable
public class RatingKey implements Serializable {
#Column(name="userID")
private Long userID;
#Column(name="focusID")
private Long focusID;
protected RatingKey(){}
public RatingKey(Long userID, Long focusID) {
this.userID = userID;
this.focusID = focusID;
}
public Long getUserID() {
return userID;
}
public void setUserID(Long userID) {
this.userID = userID;
}
public Long getFocusID() {
return focusID;
}
public void setFocusID(Long focusID) {
this.focusID = focusID;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RatingKey ratingKey = (RatingKey) o;
return Objects.equals(userID, ratingKey.userID) &&
Objects.equals(focusID, ratingKey.focusID);
}
#Override
public int hashCode() {
return Objects.hash(userID, focusID);
}
}
The table for UserBaseRatings has a default value of 1 for baseRating such that if a user does not have a rating on the current focus their rating should be 1. I'm running into a problem where if the user hasn't made a rating then the set of Rating's is empty instead of having a rating of 1 for each focus that exists.
User:
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userID;
#Column(name = "userHashedPassword")
private String password;
#Column(name = "userName")
private String userName;
#Column(name = "userEmail")
private String email;
//probably being reset
#Transient
private List<String> groups = new LinkedList<>();
#ManyToMany
#JoinTable(name = "UserRoles",
joinColumns = #JoinColumn(
name = "userID"),
inverseJoinColumns = #JoinColumn(
name = "roleID"))
private Set<Role> roles = new HashSet<>();
#OneToMany(mappedBy = "user")
private Set<Rating> ratings;
protected User(){}
public User(String userHashedPassword, String userName, String email, Set<Role> roles){
this.password = userHashedPassword;
this.userName = userName;
this.email = email;
this.roles = roles;
}
public Long getUserId() {
return userID;
}
public void setId(Long userID) {
this.userID = userID;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
}
public String getUsername() {
return userName;
}
public void setUsername(String name) {
this.userName = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public Set<Rating> getRatings() {
return ratings;
}
public void setRatings(Set<Rating> ratings) {
this.ratings = ratings;
}
}
Focus:
#Entity
public class Focus {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long focusID;
private String focusCategory;
private String focusName;
private String focusExplanation;
#OneToMany(mappedBy = "focus")
Set<Rating> ratings;
//image
protected Focus(){}
public Focus(String focusCategory, String focusName, String focusExplanation, Set<Rating> ratings){
this.focusCategory = focusCategory;
this.focusName = focusName;
this.focusExplanation = focusExplanation;
this.ratings = ratings;
}
public Long getFocusId() {
return focusID;
}
public void setFocusId(Long focusID) {
this.focusID = focusID;
}
public String getFocusCategory() {
return focusCategory;
}
public void setFocusCategory(String focusCategory) {
this.focusCategory = focusCategory;
}
public String getFocusName() {
return focusName;
}
public void setFocusName(String focusName) {
this.focusName = focusName;
}
public String getFocusExplanation() {
return focusExplanation;
}
public void setFocusExplanation(String focusExplanation) {
this.focusExplanation = focusExplanation;
}
public Set<Rating> getRatings() {
return ratings;
}
public void setRatings(Set<Rating> ratings) {
this.ratings = ratings;
}
}
What am I missing that will allow a User's Ratings to be populated with the default value for each Focus that exists in the database?
Edit: Added #PrePersist and #PreUpdate to the Rating entity:
#Entity(name = "UserBaseRatings")
public class Rating {
#EmbeddedId
RatingKey id;
#ManyToOne
#MapsId("userID")
#JoinColumn(name="userID")
private User user;
#ManyToOne
#MapsId("focusID")
#JoinColumn(name="focusID")
private Focus focus;
private BigDecimal baseRating;
protected Rating(){}
public Rating(User user, Focus focus, BigDecimal baseRating) {
this.user = user;
this.focus = focus;
this.baseRating = baseRating;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Focus getFocus() {
return focus;
}
public void setFocus(Focus focus) {
this.focus = focus;
}
public BigDecimal getBaseRating() {
return baseRating;
}
public void setBaseRating(BigDecimal baseRating) {
this.baseRating = baseRating;
}
#PrePersist
public void prePersist() {
if(baseRating == null) {
baseRating = BigDecimal.ONE;
}
}
#PreUpdate
public void preUpdate() {
if(baseRating == null) {
baseRating = BigDecimal.ONE;
}
}
}
Changing spring.jpa.hibernate.ddl-auto= from none to update also made no difference.
If you use ORM all default values have to be explicitly declared. It can be done this way
#Entity(name = "UserBaseRatings")
public class Rating {
// ...
private BigDecimal baseRating = BigDecimal.ONE;
// ...
}
or use #PrePersist and #PreUpdate
#Entity(name = "UserBaseRatings")
public class Rating {
// ...
private BigDecimal baseRating;
// ...
#PrePersist
public void prePersist() {
if(baseRating == null) {
baseRating = BigDecimal.ONE;
}
}
#PreUpdate
public void preUpdate() {
if(baseRating == null) {
baseRating = BigDecimal.ONE;
}
}
}

Why all properties of a Model passed from AOP to controller with other arguments are null

AOP
#Around(
"execution(* net.inter.warp.bridge.controller.*.*(.., net.inter.warp.bridge.model.User)) && " +
"args(.., authenticatedUser)"
)
public Object withAuthenticatedUser(ProceedingJoinPoint joinPoint, User authenticatedUser) throws Throwable {
System.out.println(joinPoint + " -> " + authenticatedUser);
User user = null;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null)
user = (User) userService.loadUserByUsername(authentication.getName());
else
throw new UnauthorizedException("err 1");
if (user == null)
throw new UnauthorizedException("err 2");
return joinPoint.proceed(new Object[]{user});
}
Controller (all properties of authenticatedUser are null)
package net.inter.warp.bridge.controller;
#GetMapping("/boxes/{id}")
public ResponseEntity<Box> getBoxById(#PathVariable(value = "id") Long boxId, User authenticatedUser)
throws NoDynamicTableFoundException, ResourceNotFoundException {}
Controller (This works as there is no more parameters except for authenticatedUser)
package net.inter.warp.bridge.controller;
#GetMapping("/boxes/{id}")
public ResponseEntity<Box> getBoxById(User authenticatedUser)
throws NoDynamicTableFoundException, ResourceNotFoundException {}
AOP seems to hate other paramethers... authenticatedUser is not null, every property of authenticatedUser is null.
Model (I am not sure this issue is related to this)
#Entity
#Table(name="users")
#ToString
public class User extends AuthEntity implements UserDetails
{
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
String[] userRoles = this.roles.stream().map((role) -> role.getName()).toArray(String[]::new);
Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(userRoles);
return authorities;
}
#Override
public String getUsername() {
return this.email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Column(nullable=false)
#NotNull(message = "")
private String name;
#Column(nullable=false, unique=true)
#Email
//#NotBlank(message = "")
private String email;
#Column
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#JsonIgnore
private String password;
#Column(length = 20, columnDefinition ="bigint")
//#NotNull(message = "")
private Long organization_id;
#ManyToOne(optional=false)
#JoinColumn(name = "organization_id",referencedColumnName="id", insertable=false, updatable=false)
//#JsonIgnore
private Organization organization;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
//#Fetch(org.hibernate.annotations.FetchMode.SELECT)
#JoinTable(
name="user_role",
joinColumns={#JoinColumn(name="user_id")},
inverseJoinColumns={#JoinColumn(name="role_id")})
private List<Role> roles;
/*
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
//#Fetch(org.hibernate.annotations.FetchMode.SELECT)
#Fetch(value = FetchMode.SUBSELECT)
#JoinTable(
name="hyperbridge_resource.user_workspace",
joinColumns={#JoinColumn(name="user_id")},
inverseJoinColumns={#JoinColumn(name="workspace_id")})
private List<Workspace> workspaces;
*/
#Column(length = 1, columnDefinition ="char")
private String active;
#Column(name = "reset_token")
#JsonIgnore
private String resetToken;
#Column(name = "reset_token_time")
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime resetTokenTime;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Long getOrganization_id() {
return organization_id;
}
public void setOrganization_id(Long organization_id) {
this.organization_id = organization_id;
}
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
/* public List<Workspace> getWorkspaces() {
return workspaces;
}
public void setWorkspaces(List<Workspace> workspaces) {
this.workspaces = workspaces;
}*/
public String getActive() {
return active;
}
public void setActive(String active) {
this.active = active;
}
public String getResetToken() {
return resetToken;
}
public void setResetToken(String resetToken) {
this.resetToken = resetToken;
}
public LocalDateTime getResetTokenTime() {
return resetTokenTime;
}
public void setResetTokenTime(LocalDateTime resetTokenTime) {
this.resetTokenTime = resetTokenTime;
}
}
Try this, doc:
#Around(
"execution(* net.inter.warp.bridge.controller.*.*(..) && " +
"args(authenticatedUser,..)"

Spring data repositories - performance issue

I'm using Spring , JPArepostories and hibernate, to save some entities to database.
My entities :
Users:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#Column(name = "CARDID",unique=true)
private String cardId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="SUPPLIERUSERID", nullable = true)
#JsonIgnore
private SupplierUser supplierUser;
#Column(name = "NAME")
private String name;
#Column(name = "SURENAME")
private String sureName;
#Column(name = "ACTIVE")
private Boolean active;
#Column(name = "SMS")
private String sms;
#Column(name = "EMAIL")
private String email;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Box> boxList = new ArrayList<Box>();
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Notification> notificationList = new ArrayList<Notification>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public SupplierUser getSupplierUser() {
return supplierUser;
}
public void setSupplierUser(SupplierUser supplierUser) {
this.supplierUser = supplierUser;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSureName() {
return sureName;
}
public void setSureName(String sureName) {
this.sureName = sureName;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public String getSms() {
return sms;
}
public void setSms(String sms) {
this.sms = sms;
}
public List<Box> getBoxList() {
return boxList;
}
public void setBoxList(List<Box> boxList) {
this.boxList = boxList;
}
public List<Notification> getNotificationList() {
return notificationList;
}
public void setNotificationList(List<Notification> notificationList) {
this.notificationList = notificationList;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Users have boxes:
#Entity
#Table(name = "boxes")
public class Box {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="USERID", nullable = true)
#JsonIgnore
private User user;
#Column(name = "BOXNUMBER",unique=true)
private int boxNumber;
#Column(name = "MODBUSADDRESS")
private int modbusAddress;
#Column(name = "MODBUSREGISTER")
private int modbusRegister;
#Column(name = "STATE")
private String state;
#OneToMany(mappedBy = "box", fetch =FetchType.EAGER,cascade = CascadeType.ALL)
private List<Transaction> transactionsList = new ArrayList<Transaction>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getModbusAddress() {
return modbusAddress;
}
public void setModbusAddress(int modbusAddress) {
this.modbusAddress = modbusAddress;
}
public int getModbusRegister() {
return modbusRegister;
}
public void setModbusRegister(int modbusRegister) {
this.modbusRegister = modbusRegister;
}
public List<Transaction> getTransactionsList() {
return transactionsList;
}
public void setTransactionsList(List<Transaction> transactionsList) {
this.transactionsList = transactionsList;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getBoxNumber() {
return boxNumber;
}
public void setBoxNumber(int boxNumber) {
this.boxNumber = boxNumber;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
Boxes have transactions:
#Entity
#Table(name = "transactions")
public class Transaction {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="BOXID", nullable = true)
#JsonIgnore
private Box box;
#Column(name = "TYPE")
private String type;
#Column(name = "SUPPLIERUSERCARDID")
private String supplierUserCardId;
#Column(name = "DATE")
#Temporal(TemporalType.TIMESTAMP)
private Date date;
#OneToMany(mappedBy = "transaction", fetch = FetchType.EAGER,cascade = CascadeType.ALL)
private List<Notification> notificationsList = new ArrayList<Notification>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Box getBox() {
return box;
}
public void setBox(Box box) {
this.box = box;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getSupplierUserCardId() {
return supplierUserCardId;
}
public void setSupplierUserCardId(String supplierUserCardId) {
this.supplierUserCardId = supplierUserCardId;
}
public List<Notification> getNotificationsList() {
return notificationsList;
}
public void setNotificationsList(List<Notification> notificationsList) {
this.notificationsList = notificationsList;
}
}
And transaction have notifications (notification refer as well to user):
#Entity
#Table(name = "notifications")
public class Notification {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="TRANSACTIONID", nullable = true)
#JsonIgnore
private Transaction transaction;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="USERID", nullable = true)
#JsonIgnore
private User user;
#Column(name = "TYPE")
private String type;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATED")
private Date created;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "SENDED")
private Date sended;
#Column(name = "RETRIES")
private Long retries;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getSended() {
return sended;
}
public void setSended(Date sended) {
this.sended = sended;
}
public Long getRetries() {
return retries;
}
public void setRetries(Long retries) {
this.retries = retries;
}
public Transaction getTransaction() {
return transaction;
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
And my question is - what I'm doing wrong, because following method for list with 150 boxes inside takes about 20 seconds to finish.
public void changeBoxOwners(ArrayList<Box> boxList){
String id = theSoftwareCore.getSupplierUser().getCardId();
ArrayList<Box> boxToSave = new ArrayList<Box>();
for (Box box : boxList){
Box existingBox = theSoftwareCore.boxServiceImp.findByBoxNumber(box.getBoxNumber());
existingBox.setState("full");
User user = theSoftwareCore.userServiceImp.findOneByCardId(box.getUser().getCardId());
//deleting not sent notifications
for (Transaction trans : existingBox.getTransactionsList()){
for (Notification notif: trans.getNotificationsList()){
if (notif.getSended()==null){
notif.setSended(new Date(0));
}
}
}
Transaction transaction = new Transaction();
transaction.setType("in");
transaction.setSupplierUserCardId(id);
transaction.setDate(new Date());
transaction.setBox(existingBox);
Notification notification = new Notification();
notification.setCreated(new Date());
notification.setType("smsTakeYourStaff");
notification.setTransaction(transaction);
notification.setUser(user);
existingBox.setUser(user);
transaction.getNotificationsList().add(notification);
existingBox.getTransactionsList().add(transaction);
boxToSave.add(existingBox);
}
System.out.println("Start saving" + new Date());
theSoftwareCore.boxServiceImp.saveAll(boxToSave);
System.out.println("End " + new Date());
}
Thanks for your time in advance.

Resources