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.
Related
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.
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.
I'm working on a JSF/JPA project with the Sakila Database on MySQL with Hibernate. When I want to merge a customer object into the DB, it takes 20 seconds. I assume it has to do with the mapping of my Entity Class but I cannot figure out what I have done wrong.
Here is my Customer class:
#Entity
#Table(name = "customer")
public class Customer implements Serializable, IEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "customer_id")
private Short customerId;
#Basic(optional = false)
#Column(name = "first_name")
private String firstName;
#Basic(optional = false)
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Basic(optional = false)
#Column(name = "active")
private boolean active;
#Basic(optional = false)
#Column(name = "create_date")
#Temporal(TemporalType.TIMESTAMP)
private Date createDate;
#Basic(optional = false)
#Column(name = "last_update")
#Temporal(TemporalType.TIMESTAMP)
private Date lastUpdate;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "customerId")
private List<Rental> rentalList;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "customerId")
private List<Payment> paymentList;
#JoinColumn(name = "address_id", referencedColumnName = "address_id")
#ManyToOne(optional = false)
private Address addressId;
#JoinColumn(name = "store_id", referencedColumnName = "store_id")
#ManyToOne(optional = false)
private Store storeId;
//Constructor
//getter_setter
}
And in my CustomerService ejb I call the merge function:
#Named
#Stateless
public class CustomerService {
#PersistenceContext(name = "sakila")
EntityManager em;
public String merge(IEntity entity) {
try {
em.merge(entity);
return "success";
}catch(Exception e){
return "failure";
}
}
}
In the log I can see that hibernate executes 11037 select statements just like this one
select
paymentlis0_.customer_id as customer5_11_0_,
paymentlis0_.payment_id as payment_1_11_0_,
paymentlis0_.payment_id as payment_1_11_1_,
paymentlis0_.amount as amount2_11_1_,
paymentlis0_.customer_id as customer5_11_1_,
paymentlis0_.last_update as last_upd3_11_1_,
paymentlis0_.payment_date as payment_4_11_1_,
paymentlis0_.rental_id as rental_i6_11_1_,
paymentlis0_.staff_id as staff_id7_11_1_,
rental1_.rental_id as rental_i1_12_2_,
rental1_.customer_id as customer5_12_2_,
rental1_.inventory_id as inventor6_12_2_,
rental1_.last_update as last_upd2_12_2_,
rental1_.rental_date as rental_d3_12_2_,
rental1_.return_date as return_d4_12_2_,
rental1_.staff_id as staff_id7_12_2_,
customer2_.customer_id as customer1_5_3_,
customer2_.active as active2_5_3_,
customer2_.address_id as address_8_5_3_,
customer2_.create_date as create_d3_5_3_,
customer2_.email as email4_5_3_,
customer2_.first_name as first_na5_5_3_,
customer2_.last_name as last_nam6_5_3_,
customer2_.last_update as last_upd7_5_3_,
customer2_.store_id as store_id9_5_3_,
inventory3_.inventory_id as inventor1_9_4_,
inventory3_.film_id as film_id3_9_4_,
inventory3_.last_update as last_upd2_9_4_,
inventory3_.store_id as store_id4_9_4_,
staff4_.staff_id as staff_id1_13_5_,
staff4_.active as active2_13_5_,
staff4_.address_id as address10_13_5_,
staff4_.email as email3_13_5_,
staff4_.first_name as first_na4_13_5_,
staff4_.last_name as last_nam5_13_5_,
staff4_.last_update as last_upd6_13_5_,
staff4_.password as password7_13_5_,
staff4_.picture as picture8_13_5_,
staff4_.store_id as store_i11_13_5_,
staff4_.username as username9_13_5_,
staff5_.staff_id as staff_id1_13_6_,
staff5_.active as active2_13_6_,
staff5_.address_id as address10_13_6_,
staff5_.email as email3_13_6_,
staff5_.first_name as first_na4_13_6_,
staff5_.last_name as last_nam5_13_6_,
staff5_.last_update as last_upd6_13_6_,
staff5_.password as password7_13_6_,
staff5_.picture as picture8_13_6_,
staff5_.store_id as store_i11_13_6_,
staff5_.username as username9_13_6_,
address6_.address_id as address_1_1_7_,
address6_.address as address2_1_7_,
address6_.address2 as address3_1_7_,
address6_.city_id as city_id8_1_7_,
address6_.district as district4_1_7_,
address6_.last_update as last_upd5_1_7_,
address6_.phone as phone6_1_7_,
address6_.postal_code as postal_c7_1_7_,
store7_.store_id as store_id1_14_8_,
store7_.address_id as address_3_14_8_,
store7_.last_update as last_upd2_14_8_,
store7_.manager_staff_id as manager_4_14_8_,
store8_.store_id as store_id1_14_9_,
store8_.address_id as address_3_14_9_,
store8_.last_update as last_upd2_14_9_,
store8_.manager_staff_id as manager_4_14_9_
from
payment paymentlis0_
left outer join
rental rental1_
on paymentlis0_.rental_id=rental1_.rental_id
left outer join
customer customer2_
on rental1_.customer_id=customer2_.customer_id
left outer join
inventory inventory3_
on rental1_.inventory_id=inventory3_.inventory_id
left outer join
staff staff4_
on rental1_.staff_id=staff4_.staff_id
inner join
staff staff5_
on paymentlis0_.staff_id=staff5_.staff_id
inner join
address address6_
on staff5_.address_id=address6_.address_id
inner join
store store7_
on staff5_.store_id=store7_.store_id
left outer join
store store8_
on staff5_.staff_id=store8_.manager_staff_id
where
paymentlis0_.customer_id=?
It takes 20 seconds before Hibernate finally sends the update query
Information: Hibernate:
update
customer
set
active=?,
address_id=?,
create_date=?,
email=?,
first_name=?,
last_name=?,
last_update=?,
store_id=?
where
customer_id=?
I assume my mapping is wrong and customer is configured to be lazily loaded. Can someone point out my mistake?
Obviously in order to merge, JPA implementation (Hibernate here) needs to know if there are any changes made to detached (or lazy, not yet fetched) entities or not, thus it is checking its state with SELECT. Since you are cascadig (with CascadeType.ALL) your merge to all related entities - whole graph is refreshed.
Either do not cascade merges, or insteed merge use JPQL (or CriteriAPI) update if you need only to update parent entity disregarding it children.
iam working on an app where the association between entities is as follows . Here comment is the owner and iteration and user are the inverse tables . The requirement is -
A user can have many comments similarly a comment can be given by multiple users .
Also an iteration can have multiple comments and a single comment can belong to multiple iterations .
The code is as follows -
Comments entity -
#Table(name="RCOMMENTS")
#Entity
public class RComment{
#Id
#Column(name="COMMENTID")
#GeneratedValue(strategy=GenerationType.AUTO)
private long commentid;
#Column(name="DESCRIPTION")
private String description;
#Column(name="TYPE")
private String type;
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name = "COMMENTS_USERS", joinColumns = { #JoinColumn(name = "COMMENTID") },
inverseJoinColumns = { #JoinColumn(name = "USERID") })
private Set <RUsers> users = new HashSet<RUsers>();
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name = "COMMENTS_ITERATIONS", joinColumns = { #JoinColumn(name = "COMMENTID") },
inverseJoinColumns = { #JoinColumn(name = "ITERATIONID") })
private Set <RIteration> iteration = new HashSet<RIteration>();
Users-
#Entity
#Table(name = "RUSER")
public class RUsers {
#Id
#Column(name = "USERID")
#GeneratedValue(strategy=GenerationType.AUTO)
private long userid;
#Column(name = "USERNAME")
private String username;
#Column(name = "PASSWORD")
private String password;
#ManyToMany(mappedBy="users")
private Set <RComment> comment ;
Iteration -
#Table(name = "RITERATION")
#Entity
public class RIteration {
#Id
#Column(name = "iterationid")
#GeneratedValue(strategy=GenerationType.AUTO)
private long iterationid;
private Date startdate;
private Date enddate;
private long iter;
#ManyToMany(mappedBy="iteration")
private Set <RComment> comment = new HashSet<RComment>();
Test code -
RUsers user = (RUsers) session.get(RUsers.class, this.userid);
Set<RComment> comments = user.getComment();
Now the issue that iam facing is the mapping tables are empty .
Also user.getComment() yields empty set
Before i start my web app my db entity tables prepopulated with dummy values . But during run time when i debug the code user.getcomment returns empty set .
Could anyone please help me out what could be the issue .
If you have an bidirectional relationship between your Entities, then Hibernate will pay attention read only one of the two sides when it stores them in the database.
The side of the relation that is the important one, is the one that's ManyToMany annotation does NOT contain the mappedBy variable. And that is the side where you need to assign the value:
RUsers user = ...
RComment comment = ...
comment.users.add(user); <--- that is the assignment hat hibernate will store!
user.comments.add(comment); <--- that assigment will NOT been stored by hibernate, but hibernate will load it from database
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.