Hibernate update java object field from database - java

I have bean
#Entity
#Table(name = "Users")
public class User {
#Id
#GeneratedValue
#Column(name = "userId")
private Integer userId;
#Column(name = "username")
private String username;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "parentId")
#Fetch(value = FetchMode.SUBSELECT)
private List<User> childs;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "userId")
#Fetch(value = FetchMode.SUBSELECT)
private List<Role> roles;
and an object loaded by hibernate, but before saving all changes to database I need to get current password from database. I am trying to do this
if (user.getPassword() == null) { user.setPassword(userDao.getUserById(user.getUserId()).getPassword());}
but I am getting DuplicateKeyException.
So, how can I do this? Or, can I save all fields except password?

You could retrieve the data from the database with Hibernate, copy the values from your (detached) bean (coming from your presentation or business layer) into this JPA bean (the one returned by Hibernate), then let Hibernate update the database (at the end of the transaction), or explicitely if you want.

You could use the annotation #Column(updatable=false) to exclude the field from database update

you can use Hibernatecallback methods #PrePersist. which does some action before persist there you can write yor fetch password into local transient variable logic.

Related

I am unable to create entity or fetch list of entities due to stack overflow error on bi-directional #ManyToMany relationship SpringDataJPA

I am working on a springboot application. I have 2 entity classes, Group and User. I also have #ManyToMany relationship defined in the Group class (Owning entity), and also in the User class, so that I can fetch all the groups a user belongs to. Unfortunately, I can't create a new group or a new user due to the following error;
{
"timestamp": "2022-09-09T20:29:22.606+00:00",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported"
}
When I try to fetch all groups a user belongs to by calling user.get().getGroups(); I get a a stack overflow error
Note: Currently I have #JsonManagedReference and #JsonBackReference in Group and User classes respectively. I also tried adding #JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") on both classes, but this did not work either. Adding value parameter to #JsonManagedReference and #JsonBackReference as demonstrated below did not work either. What am I doing wrong? What am I missing?
This is my Group entity class
#Table(name = "`group`") // <- group is a reserved keyword in SQL
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonView(Views.Public.class)
private String name;
private Integer maximumMembers;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinTable(name = "group_user", joinColumns = #JoinColumn(name = "group_id"), inverseJoinColumns = #JoinColumn(name = "user_id"))
#JsonView(Views.Public.class)
#JsonManagedReference(value = "group-member")
private Set<User> groupMembers;
}
This is my User entity class
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(Views.Public.class)
private Long id;
#JsonView(Views.Public.class)
private String nickname;
#JsonView(Views.Public.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
private Set<ApplicationUserRole> roles;
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
#JsonBackReference(value = "user-group")
private Set<Group> groups;
}
Minimal, Reproducible Example https://github.com/Java-Techie-jt/JPA-ManyToMany
I found a permanent solution for this problem. For anyone else facing a similar problem, This is what I found. First, my entity classes had #Data Lombok annotation. I removed this because the #Data annotation has a tendency of almost always loading collections even if you have FetchType.LAZY.
You can read more about why you should't annotate your entity class with #Data here https://www.jpa-buddy.com/blog/lombok-and-jpa-what-may-go-wrong/
After removing this annotation, I removed #JsonManagedReference and #JsonBackReference from both sides of the relationship(both entities). I then added #Jsonignore to the referencing side only(User class). This solves 2 things
Creating a group with a list of users works fine
Adding a list of users to a group works fine.
After this, we are left with one last problem. When we try to read a user from the api, we get a user without the associated list of groups they belong to, because we have #JsonIgnore on the user list. To solve this, I made the controller return a new object. So after fetching the user from my service, I map it to a new data transfer object, the I return this object in the controller.
From here I used #JsonView to filter my responses.
This is how my classes look, notice there is no #Data in annotations.
Group
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Getter
#Setter
#Table(name = "`group`") // <- group is a reserved keyword in SQL
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private Integer maximumMembers;
#ManyToMany(fetch = FetchType.EAGER,
cascade = {CascadeType.MERGE, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinTable(name = "group_user",
joinColumns = #JoinColumn(name = "group_id"),
inverseJoinColumns = #JoinColumn(name = "user_id"))
#JsonView(UserViews.PublicUserDetails.class)
private Set<User> groupMembers = new HashSet<>();
}
User
#Builder
#Entity
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(UserViews.PublicUserDetails.class)
private Long id;
#JsonView(UserViews.PublicUserDetails.class)
private String nickname;
#JsonView(UserViews.PublicUserDetails.class)
private String username; // <- Unique user's phone number
private String password;
#ElementCollection(targetClass = ApplicationUserRole.class)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
#Column(name = "role")
#JsonView(UserViews.PublicUserDetails.class)
private Set<ApplicationUserRole> roles;
#JsonIgnore
#ManyToMany(mappedBy = "groupMembers", fetch = FetchType.LAZY, targetEntity = Group.class)
private Set<Group> groups = new HashSet<>();
}
Method fetching user in user controller
#GetMapping("/get-groups")
public ResponseEntity<UserRequestResponseDTO> getWithGroups(#RequestParam(name = "userId") Long userId) {
User user = userService.getWithGroups(userId);
UserRequestResponseDTO response = UserRequestResponseDTO.builder()
.nickname(user.getNickname())
.username(user.getUsername())
.groups(user.getGroups())
.build();
return ResponseEntity.ok().body(response);
}
Hopefully this helps someone💁

spring jpa infinite recursion

Hello i am using spring with JPA and mysql
i have a class FishJournal
#Entity
#Table(name = "journal")
public class FishJournal {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(targetEntity =User.class,cascade = CascadeType.ALL)
#JoinColumn(name="user_id", referencedColumnName = "id")
private User users;
#ManyToOne(targetEntity =Fish.class,cascade = CascadeType.ALL)
#JoinColumn(name="fish_id", referencedColumnName = "id")
private Fish fishes;
#ManyToOne(targetEntity =Water.class,cascade = CascadeType.ALL)
#JoinColumn(name="water_id", referencedColumnName = "id")
private Water waters;
#ManyToOne(targetEntity =Bait.class,cascade = CascadeType.ALL)
#JoinColumn(name="bait_id", referencedColumnName = "id")
private Bait baits;
Date date;
String info;
String path;
boolean shared;
getters and setter() {
}
And I want it to have a unidirectional many-to-one relationship with user,water,bait and fish.
I want to be able when given the FishJournal object to be able to get the data for those fields.
The problem is when I try to use JPA to query a fishjournal from the database with jpa an infinity recursion occurs and the only way I was able to fix it was to change the getWaters,getUsers methods to return only the name and not the whole object.That isn't good enough because I want to be able to use the whole objects as getters.

How to use #JsonManagedReference and #JsonBackReference for Many to Many relationship with best practice?

I will be much apprecaite for you to explain my questions.
I have 2 entity and 1 bridge table entity,
Let's say they are Team, User and TeamUser.
TeamEntity:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "TEAM_ID")
private Integer id;
#JsonManagedReference
#OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<TeamUsers> team_users = new HashSet<>();
UserEntity:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JsonBackReference
#OneToMany(mappedBy = "user")
private Set<TeamUsers> team_users = new HashSet<>();
TeamUserEntity(bridge table):
#EmbeddedId
private TeamUsersId id;
#ManyToOne
#MapsId("teamId")
#JoinColumn(name = "team_id")
#JsonBackReference
private Team team;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
#JsonManagedReference
private User user;
#Column(name = "active")
private int active;
As you can see I used #JsonManagedReference and #JsonBackReference
to telling the program the direction for the query of the Entity and avoid infinite recrusive.
Now if I run get repo.findAll() on Team CRUDrepository I will get all Team object, and within the content I will get all bridge table data and it also include User details information.
But Let's say sometimes if I want to query the data in oppisite way, I want to get All User information and the object should contain all Team information, looks like the annotation #JsonManagedReference and #JsonBackReference block the result.
In real world development, how should we manage here?
The #JsonManagedReference and #JsonBackReference annotations are used to handle circular references. They can be used to create a JSON structure in a bidirectional way. The #JsonManagedReference annotation is used on a child reference of the target POJO, which means it is a forward reference that includes during the serialization process whereas #JsonBackReference annotation is a backreference that omits during the serialization process, usually used in the corresponding child class.
Hence, The property annotated with #JsonBackReference here, which is the Team in the TeamUsers, won't be serialized. That is why when you try to get all the users having Team inside the TeamUsers, it won't work. Also, if it did, it would violate the purpose of the annotations that they're used for, recursive access mapping.
If you want to fetch data in either way, you should use #JsonIgnoreProperties instead of those two annotations. Change your entity classes as follows and you'll get your desired output.
In Team class, set #JsonIgnoreProperties("team") on the team_users field to ignore mapping team inside this field again to avoid recursive mapping. Change your Team class to:
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "TEAM_ID")
private Integer id;
#OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonIgnoreProperties("team")
private Set<TeamUsers> team_users = new HashSet<>();
}
Similarly, in User class, set #JsonIgnoreProperties("user") on the team_users field to ignore mapping user inside this field again to avoid recursive mapping. Change your User class to:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
#JsonIgnoreProperties("user")
private Set<TeamUsers> team_users = new HashSet<>();
}
And finally, in TeamUsers class, set #JsonIgnoreProperties("team_users") on both the team and user field to ignore mapping team_users inside these field again to avoid recursive mapping. Change your TeamUsers class to:
public class TeamUsers {
#EmbeddedId
private TeamUserId id;
#ManyToOne
#MapsId("teamId")
#JoinColumn(name = "team_id")
#JsonIgnoreProperties("team_users")
private Team team;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
#JsonIgnoreProperties("team_users")
private User user;
#Column(name = "active")
private int active;
}
Now you can fetch the data in either way without having recursive mapping.

Hibernate mapping causing infinte loop

Note: Please don't mark it as duplicate before reading completely
Case : I have three classes named User, Post and Tag
User <-> Post (OneToMany Bi-directional)
Post <-> Tag (ManyToMany)
Solution I want :
Mapping should work like If i call getUserById, I should get posts
related to the user and tags related to the posts.
Same with Posts and Tags, If I call getPostById I should get the
user and tags and if I call getTagByName I should get all posts
related to tags
Solutions I have tried :
#JsonMappedReference, #JsonBackReference - Worked for read operations but failed for creating/writing
#JsonIdentityInfo - Did not worked
#JsonIgnore - Worked but I don't want to ignore as am not getting desired solution mentioned above
#ToString.Exclude, #EqualsAndHashCode.Exclude - Did not worked
Also tried with my own getters and setters and #ToString methods - Did not worked either
This is a springboot project
Here are my classes
User.java
#Entity
#Table(name = "user")
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "username")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "created_at")
#CreationTimestamp
private Timestamp createdAt;
#Column(name = "updated_at")
#UpdateTimestamp
private Timestamp updatedAt;
#OneToMany(
mappedBy = "user",
cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
private List<Post> posts;
Post.java
#Entity
#Table(name = "post")
#Data
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(
cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name = "user_id")
private User user;
#Column(name = "title")
private String postTitle;
#Column(name = "content")
private String postContent;
#Column(name = "status")
private String postStatus;
#Column(name = "created_at")
#CreationTimestamp
private Timestamp createdAt;
#Column(name = "updated_at")
#UpdateTimestamp
private Timestamp updatedAt;
#ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
#JoinTable(
name = "post_tag",
joinColumns = #JoinColumn(name = "post_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id"))
private List<Tag> tags;
Tag.java
#Entity
#Table(name = "tag")
#Data
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String tagName;
#Column(name = "created_at")
#CreationTimestamp
private Timestamp createdAt;
#Column(name = "updated_at")
#UpdateTimestamp
private Timestamp updatedAt;
#ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
#JoinTable(
name = "post_tag",
joinColumns = #JoinColumn(name = "tag_id"),
inverseJoinColumns = #JoinColumn(name = "post_id"))
private List<Post> posts;
So with above classes I ran into infinite loop problem, If I use getUserById post object is user object is showing Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception. If call getAllPosts OR getAllTags tags object in post object is showing the same error or vice versa
I had a similar problem with #OneToMany and #ManyToOne relations. I'm simply going to explain the route I took to fix my code. Hopefully, it will make a difference.
Add #JsonBackReference to your user class, this should resolve your loop issue. Also remove cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH} line from all of your classes. Cascades were the reason I was unable to perform update method.
Please try the codes I provided below. You should be able to see Users as a part of the Post output stream when you test it. Also, you should be able to list users without encountering the loop problem. Sadly, I'm not so sure about the many-to-many relation since I have no experince on it.
User.java
#Entity
#Table(name = "user")
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "username")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "created_at")
#CreationTimestamp
private Timestamp createdAt;
#Column(name = "updated_at")
#UpdateTimestamp
private Timestamp updatedAt;
#JsonBackReference
#OneToMany(mappedBy = "user")
private List<Post> posts;
Post.java
#Entity
#Table(name = "post")
#Data
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#Column(name = "title")
private String postTitle;
#Column(name = "content")
private String postContent;
#Column(name = "status")
private String postStatus;
#Column(name = "created_at")
#CreationTimestamp
private Timestamp createdAt;
#Column(name = "updated_at")
#UpdateTimestamp
private Timestamp updatedAt;
#ManyToMany
#JoinColumn(name = "tag_id")
private List<Tag> tags;
Tag.java
#Entity
#Table(name = "tag")
#Data
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String tagName;
#Column(name = "created_at")
#CreationTimestamp
private Timestamp createdAt;
#Column(name = "updated_at")
#UpdateTimestamp
private Timestamp updatedAt;
#ManyToMany(mappedBy = "tags"))
private List<Post> posts;
I have removed all the mappings, I can get user, post and post tags in three different calls and its working fine i have tried all the mapping explained or shown above but am getting error while read/write operations and to avoid all those i have made the change so that it does not have any mapping
you can use data transfer object to swow that field what you neen, maybe you can replace array of users in json to array users ids like this
#Transactional
public PostDto savePost(Post post) {
Post save = postRepository.save(post);
return Optional.of(save).map(this::transformPostEntityToDto).orElse(null);
}
#Override
public List<PostDto> getAllPosts() {
return postRepository.findAll().stream()
.map(this::transformPostEntityToDto).collect(Collectors.toList());
}
private PostDto transformPostEntityToDto(Post post) {
return PostDto.builder()
.id(post.getId())
.createdAt(post.getCreatedAt())
.postContent(post.getPostContent())
.postStatus(post.getPostStatus())
.postTitle(post.getPostTitle())
.tags(Objects.nonNull(post.getTags())
? post.getTags().stream().map(this::transformTagEntityToDto).collect(Collectors.toList())
: Collections.emptyList())
.updatedAt(post.getUpdatedAt())
.user(Optional.ofNullable(post.getUser()).map(this::transformUserEntityToDto).orElse(null))
.build();
}
private TagDto transformTagEntityToDto(Tag tag) {
return TagDto.builder()
.id(tag.getId())
.createdAt(tag.getCreatedAt())
.tagName(tag.getTagName())
.updatedAt(tag.getUpdatedAt())
.postsIds(Objects.nonNull(tag.getPosts()) ? tag.getPosts().stream().map(Post::getId).collect(Collectors.toList())
: null)
.build();
}
private UserDto transformUserEntityToDto(User user) {
return UserDto.builder()
.createdAt(user.getCreatedAt())
.firstName(user.getFirstName())
.id(user.getId())
.lastName(user.getLastName())
.password(user.getPassword())
.updatedAt(user.getUpdatedAt())
.userName(user.getUserName())
.postsIds(Objects.nonNull(user.getPosts()) ? user.getPosts().stream().map(Post::getId).collect(Collectors.toList())
: null)
.build();
}
it is flexiable but requare several dto classes for views
Regarding the LazyInitializationException, it has to do with a 'fetch' mode and nothing to do with Jackson serialization. Take a look at this question: Default fetch type for one-to-one, many-to-one and one-to-many in Hibernate. Solutions for that are either setting loading to eager: https://www.baeldung.com/hibernate-lazy-eager-loading, or fetch joins: https://www.baeldung.com/jpa-join-types#fetch. If you are using hibernate without JPA abstraction over it, you can also take a look at this: Hibernate: best practice to pull all lazy collections
Regarding the infinite recursion problem when using jackson, take a look at: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion. #JsonManagedReference and #JsonBackReference are a nice option, however you cannot have a situation where you use same classes to get user with posts and same classes to get post with its user. Either you have to "ignore" user when serializing post or "ignore" post when serializing user. Otherwise you always get the infinite loop. Solution for that is using DTOs (Data Transfer Object)
Another important thing is that when using bidirectional mappings with JPA, you have to yourself set the "reference to the owner" of the collection (in this case, when adding a post to a user, make sure you also set the user reference on the post object to that user). In this example you see how in the class Post in line 22 you set the reference on the currently being added PostComment object for the post attribute to this
Let's make it work step by step.
Since you're using Spring Boot, I suspect these entities are returned directly from the REST controller(s). So when you try to return a user and call getUserById() it does the following:
Hibernate fetches the user by id and sets the lazy collection of posts
Spring is trying to create a JSON of this user using Jackson, which is calling all the getters
since posts are not loaded yet hibernate will either
a. load all posts in an additional SQL SELECT if the session is still open
b. throw LazyInitializationExcepiton is the session is closed
So your homework task #1 is to make sure the session is still open (if you really need this). By default in the Spring Boot app Hibernate session boundaries are the same as Spring #Transactional boundaries. (HOWEVER, in case you are using Spring Boot 2+, find property spring.jpa.open-in-view. It's true by default and it registers OpenEntityManagerInViewInterceptor that gives you open Hibernate session during the whole lifetime of a web request.)
When the session is open and the lazy loading will work, the following will happen:
one user is loaded into the session by id
all posts of that user are lazy loaded when Jackson calls the getter
since Jackson recursively goes and calls all the getters, each post.getTags() will be called
now each tag.getPosts() will be called
and again each post.getUser() and post.getTags() will be called
...
as you can see, you will load all of your DB to the application + you'll get StackOverflowException :(
So you homework task #2 is to put back #JsonMappedReference, #JsonBackReference (for instance if you load all tags for posts then you should not load all posts for tags).
I have to mention that this is not the right way to do it. It is much better to load everything you need first (for instance using join fetch) and then start building a JSON. But since you've asked... :)
Regarding the write operation, it is a bit trickier and it depends on HOW you actually do it. But at least make sure that session and transaction are open when you're trying to write data to the DB.

Hibernate OneToMany mapping with cascade

I'll put it simple. I've got User class and Privilege class. User has many Privileges.
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
private Set<Privilege> privileges;
Privilege has one and only one User.
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private User user;
As you see I've specified CascadeType to ALL, but whenever I want to persist my User:
Set<Privilege> privs = new HashSet<>();
Privilege priv = new Privilege("anything");
//priv.setUser(user); it works with this line, of course
privs.add(priv);
user.setPrivileges(privs);
//session.save(user);
Privilege has not binded user.
Any ideas?
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#Column(name="email", nullable = false, unique = true)
private String email;
#Column(name="password")
private String password;
#Column(name="user_type")
#Enumerated(EnumType.STRING)
private UserType userType;
#OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY, mappedBy = "user")
private Set<Privilege> privileges = new HashSet<>();
//getters, setters
#Entity
#Table(name = "privileges", uniqueConstraints = #UniqueConstraint(columnNames = {"user_id", "privilege"}))
public class Privilege {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "privilege")
private String privilege;
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private User user;
//getters setters
To make sure I got your statement "Privilege has not binded user" . If you uncomment priv.setUser(user) then hibernate is able to associate user with the privilege in the database (i.e., user_id field is getting populated properly in the Privilege table). And if you comment out this line you don't see user_id being associated in the privilege table. Is that right?
If so, the reason is, you have specified mappedBy=user in the oneToMany annotation. This informs the hibernate that the association is mananged by the User field in the Privilege. So when hibernate is inserting the privilege record it looks into the user field to populate the userID.
With priv.setUser(user) hibernate would now know to which user this privilege has to be associated with and if you don't set this it will be null and you would see a null value against user_id column.
Or, let me know if I misinterpreted the question.
When you the Hibernate save process will causing a ACTION_SAVE_UPDATE action, but the JPA will pass a ACTION_PERSIST and ACTION_MERGE, it will not match and causing the cascade failed to execute.
If you delete the JPA cascade – javax.persistence.CascadeType, replace it with Hibernate cascade – org.hibernate.annotations.Cascade, with CascadeType.SAVE_UPDATE.
It must work.

Categories

Resources