11

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?

Amit
  • 30,756
  • 6
  • 57
  • 88
  • Try to print id of User – Jay Smith Apr 12 '17 at 05:51
  • Tried. Its printing the expected one...no issues in user object. –  Apr 12 '17 at 05:53
  • What id generation strategy do you use? – Arnold Galovics Apr 12 '17 at 06:16
  • `@Id @GeneratedValue(strategy = GenerationType.IDENTITY)` this one –  Apr 12 '17 at 06:22
  • @sumit can you show the actual methods that are annotated with `@Transactional`? – Eugene Apr 12 '17 at 07:10
  • Eugene added the entire @Transactional code –  Apr 12 '17 at 09:00
  • Can you merge the child class. Try to merge the child object –  Apr 12 '17 at 09:04
  • Vignesh - What you mean by merging child class? Can you please show sample code? Did you mean to call `TruckOwnerDAO.merge(entity)`? –  Apr 12 '17 at 09:06
  • Yes. You are using bi directional relationship. So that try to do the following. User user = userDAO.findById(userId); user.setPassword(newPassword); Truckowner truckOwner= user.getTruckOwner(); truckowner.setUser(user); truckOwner.merge(truckOwner); –  Apr 12 '17 at 09:09
  • 1
    Thats completely invalid answer. You cant save/merge `truckOwner` object in by calling `userDAO.merge` userDAO and truckOwnerDAO are separate. user and truck entities are totally different of each other joined by mapping only. –  Apr 12 '17 at 09:15
  • Okay. here is the thing. Set the user object into truck. User user = userDAO.findById(userId); user.setPassword(newPassword); TruckOwner to=new TruckOwner(); //setters for all truck field //to.setUser(user); //user.setTruckOwner(to); userDAO.merge(user); –  Apr 12 '17 at 09:25
  • Code `System.out.println(user.getTruckOwner().getTruckOwnerId());` seems valid because you doing it within same transaction. Could you tell us what exactly spring and hibernate version you're using? – xsalefter Apr 18 '17 at 19:55
  • Hi you should look at [This Link](http://stackoverflow.com/questions/16380008/persist-onetoone-relation-with-springdata-jpa) – Mitul Gedeeya Apr 20 '17 at 11:08

5 Answers5

3

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:

  1. as suggested by staszko032 you should change fetch type to EAGER
  2. When you load the user object by using the 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 session

I hope it's useful

Angelo

Angelo Immediata
  • 6,635
  • 4
  • 33
  • 65
3

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 ;
     }

}
developer_hatch
  • 15,898
  • 3
  • 42
  • 75
3

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.

Sergei Rybalkin
  • 3,337
  • 1
  • 14
  • 27
3

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.

Klaus Groenbaek
  • 4,820
  • 2
  • 15
  • 30
1

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.

staszko032
  • 802
  • 6
  • 16
  • 1
    Tried that. Not working ..same error coming..Also, in case of lazy fetch, hibernate should/must fetch the `TruckOwner` object when the `System.out.println(user.getTruckOwner().getTruckOwnerId()); ` line is executed if I am not wrong. –  Apr 12 '17 at 09:02