Reputation:
Using Hibernate 5, Spring 4
Please consider below codes and mapping between two entities:
User class
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
private TruckOwner truckOwner;
//getter setters below
TruckOwner class
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
//getter setter below
When my code tries to update values inside user
class like below code:
UserServiceImpl class
@Override
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void resetPassword(Long userId,String newPassword) {
User user = userDAO.findById(userId);
user.setPassword(newPassword);
System.out.println(user.getTruckOwner().getTruckOwnerId());
userDAO.merge(user);
}
When calling userDAO.merge(user);
I get below error:
non-transient entity has a null id: com.mymodel.TruckOwner
I am facing this kind of problem in many places in my project, please help me with a proper solution to this problem and why is TruckOwner class has everything null set by hibernate?
Upvotes: 11
Views: 4025
Reputation: 5035
First you should not be using merge
here! You should almost never use merge.
Merge should be used when you have an unmanaged entity (serialized or loaded by a previous persistence context) and wish to merge it into the current persistence context, to make it a managed entity. Your entity is already managed since a persistence context was loaded with your DAO inside a container managed transaction. This means you don't have to do even have to call save, any changed to a managed entity will be detected and persisted when the transaction commits.
On the surface JPA looks easy, because a lot of the complexity is not visible on the surface, god knows I banged my head against the wall when I started with TopLink 7 years ago, but after reading about Object life cycles and application versus container managed persistence context, I made a lot more mistakes, and it was much easier to decipher the error messages.
Upvotes: 3
Reputation: 3453
Eager mode is not a solution if you are making a production application. Problem is in your session is already closed when your are trying to getTruckOwner. Try to propagate session to all resetPassword
method.
Upvotes: 3
Reputation: 16224
Try this for the truck class:
@Entity
@Table(name = "truckOwner")
public class TruckOwner{
...
private User user;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "truckOwner", cascade = CascadeType.ALL)
public User getUser() {
return this.user;
}
}
And this for the User class:
@Entity
@Table(name = "user")
public class User{
private TruckOwner truckOwner;
@OneToOne(fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
public TruckOwner getTruckOwner() {
return this.truckOwner ;
}
}
Upvotes: 3
Reputation: 6944
We should know the implementation of the userdao merge
method but I guess it's called the merge
method of hibernate Session
interface
In any case the not transient object is the TruckOwner
object; hibernat will not fetch the object when you call System.out.println(user.getTruckOwner().getTruckOwnerId());
moreover in that point you are out from hibernate session and if you call any other getter of truckOwner except getTruckOwnerId() you should get the org.hibernate.LazyInitializationException
(or similar.. I don't remember correctly)
I guess you have 2 option:
userDAO.findById(userId);
you should fetch
the truckOwner object inside the hibernate session by calling any other method inside the userDAO.findById(userId);
implementation and inside the hibernate sessionI hope it's useful
Angelo
Upvotes: 3
Reputation: 862
Another solution will be changing the fetch type to EAGER mode in User class. With LAZY mode, hibernate doesn't retrieve TruckOwner connected with user, as it is not explicitly needed in your case. Eventually TruckOwner is null for user, however it has nullable = false option set, and that's why merge fails.
Upvotes: 1