Home > Back-end >  JPA OneToOne and shared primary key need manual assignment
JPA OneToOne and shared primary key need manual assignment

Time:10-31

I'm using Springboot and JPA to create two tables sharing the same primary key.

For the first table I write:

public class UserAccount implements Serializable
 {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
   @OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, 
                                         CascadeType.REFRESH}, fetch=FetchType.LAZY)
   @PrimaryKeyJoinColumn
   private UserLogin login;
 }  
 

For the second table I write:

public class UserLogin implements Serializable
  {
    @Id
    private Long user_id;
    @OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH}, 
               fetch=FetchType.LAZY)
   @MapsId("user_id")
   @JoinColumn(name = "user_id", referencedColumnName = "id")
   @Setter(AccessLevel.NONE)
   private UserAccount user;
   public void setUser(UserAccount user)
    {
     this.user = user;
     this.user_id = user.getId();    
    }
  }

Other stuff are omitted for conciseness. The code works because I manually set the id of UserLogin by writing the statement

this.user_id = user.getId();

otherwise I get the error:

Hibernate error: ids for this class must be manually assigned before calling save():

I guess that the ids can be manually managed but I cannot get the right configuration.

CodePudding user response:

Try this :

public class UserLogin implements Serializable
  {
    @Id
    private Long user_id;

   @OneToOne(fetch=FetchType.LAZY)
   @MapsId
   @JoinColumn(name = "user_id")
   private UserAccount user;

   public UserAccount getUser() {
     return user;
   }
   public void setUser(UserAccount user) {
     this.user = user;
   }
  }

public class UserAccount implements Serializable
 {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
 }

To persist UserLogin :

EntityManager em;
UserAccount user = em.find(UserAccount.class, 1L)
UserLogin login = new UserLogin();
login.setUser(user);
em.persist(login);

CodePudding user response:

You have a bidirectional relationship, but have mapped it with a few competing options that don't work well together. First, in UserAccount, it isn't clear why you have an ID that is generated, yet try to also map it with the relationship (specifically using a PrimaryKeyJoinColumn). If you want it generated, it can't also be a foreign key value in a reference - and you've already got this relationship setup as the 'other' side via the 'mappedBy' setting. It should just be:

public class UserAccount implements Serializable {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
   @OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, 
                                         CascadeType.REFRESH}, fetch=FetchType.LAZY)
   private UserLogin login;
 }

User login then should just be:

public class UserLogin implements Serializable {
   @Id
   private Long user_id;
   @OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH}, 
               fetch=FetchType.LAZY)
   @MapsId("user_id")
   @JoinColumn(name = "user_id", referencedColumnName = "id")
   @Setter(AccessLevel.NONE)
   private UserAccount user;
   public void setUser(UserAccount user) {
     this.user = user;   
   }
}

Note because you have the mapsId annotation on the user relationship, JPA will set the user_id property for you once the ID is assigned - there is no need to manually set it yourself. You can, but if you do you must insure it was assigned previously - which requires a save/flush on the UserAccount. If you don't actually use the Long user_id property, you don't really even need to map it; you can just mark the user property as the ID:

public class UserLogin implements Serializable {
   @Id
   @OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH}, 
               fetch=FetchType.LAZY)
   @JoinColumn(name = "user_id", referencedColumnName = "id")
   @Setter(AccessLevel.NONE)
   private UserAccount user;
   public void setUser(UserAccount user) {
     this.user = user;   
   }
}

The Long ID from UserAccount then can be used to lookup UesrAccounts and UserLogin instances.

  • Related