1

I have LoginCredential class:

@Data
@Entity
public class LoginCredential implements Serializable {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   Long userID;
   String eMail;
   String passwordHash;
   @GeneratedValue(strategy = GenerationType.AUTO)
   @OneToOne(mappedBy = "loginCredential", fetch = FetchType.LAZY)
   User user;
}

And here is my User class:

@Data
@Entity
public class User {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   Long userID;
   @OneToOne(fetch = FetchType.LAZY,targetEntity = LoginCredential.class)
   @JoinColumn(name = "userID",referencedColumnName = "userID")
   private LoginCredential loginCredential;
}

And my LoginCredentialController's POST-ing method is simple :

@PostMapping("/login")
LoginCredential newLoginCredential(@RequestBody LoginCredential newLoginCredential) {
    logger.debug(newLoginCredential);
    LoginCredential a=repository.save(newLoginCredential);
    logger.debug(a);
    return a;
}

Now when I tried this command : curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"1\"}" http://localhost:8080/login

I get a LoginCredential without any error, to be mentioned user field is null.

That's why I tried this command curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"1\",\"user\":{} }" http://localhost:8080/login

Which gives me error saying :

{
  "status" : 500,
  "error" : "Internal Server Error",
  "message" : "org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.mua.cse616.Model.LoginCredential.user -> com.mua.cse616.Model.User; org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.mua.cse616.Model.LoginCredential.user -> com.mua.cse616.Model.User",
  "trace":....
}

Application.properties

pom.xml

buræquete
  • 14,226
  • 4
  • 44
  • 89
Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86

3 Answers3

2

You have three problems

  1. Lacking a cascade option to trigger User entity creation from LoginCredential save.
  2. Lacking @MapsId annotation on User so that they share the same id, otherwise LoginCredential and its created User will have different id values for both of them has @GeneratedValue(strategy = GenerationType.AUTO) on their @Id columns
  3. Not setting both sides of the relationship...

To fix all you need to change your entities to below (I also removed some useless annotations and values);

@Data
@Entity
public class User {

    @Id
    Long userID;

    @JsonBackReference
    @MapsId
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userID", referencedColumnName = "userID")
    @ToString.Exclude
    private LoginCredential loginCredential;
}

and

@Data
@Entity
public class LoginCredential {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long userID;
    String eMail;
    String passwordHash;

    @JsonManagedReference
    @OneToOne(mappedBy = "loginCredential", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    private User user;
}

Also need to set both sides of the relationship before finalizing your endpoint;

Optional.ofNullable(loginCredential.getUser())
        .ifPresent(user -> user.setLoginCredential(loginCredential));
loginCredentialRepo.save(loginCredential);
Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86
buræquete
  • 14,226
  • 4
  • 44
  • 89
  • Not working - when I tried `curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"1\"}" http://localhost:8080/login` It gave me **"attempted to assign id from null one-to-one property [com.mua.cse616.Model.LoginCredential.user]; nested exception is org.hibernate.id.IdentifierGenerationException: attem pted to assign id from null one-to-one property [com.mua.cse616.Model.LoginCredential.user]"** – Maifee Ul Asad Oct 04 '19 at 04:46
  • can you see [this video](https://drive.google.com/file/d/1qACd37mSwn0I0nk0LXvHW6jgBqvvgyz_/view?usp=sharing) please .. – Maifee Ul Asad Oct 04 '19 at 04:54
  • 1
    @MaifeeUlAsad ah of course, that is due to infinite recursion loop. Updated answer, check `@JsonBackReference` & `@JsonManagedReference`. When you try to print `LoginCredential`, it tries to print `User`, then that `User` tries to print `LoginCredential` again, then that tries to print `User` again, creating this endless recursive call stack until it kills everything. – buræquete Oct 04 '19 at 05:00
  • It gives me error : **"attempted to assign id from null one-to-one property [com.mua.cse616.Model.User.loginCredential]; nested exception is org.hibernate.id.IdentifierGenerationException: attem pted to assign id from null one-to-one property [com.mua.cse616.Model.User.loginCredential]"** see [here](https://i.stack.imgur.com/myuE3.png) – Maifee Ul Asad Oct 04 '19 at 05:49
  • 1
    @MaifeeUlAsad that does not happen in my local, are you sure you copied everything from my answer? I get `{"userID":1,"passwordHash":null,"user":null,"email":"hey"}` back when I pass `{"email": "hey"}`. It seems you are inserting an empty `User` in `LoginCredential`, either keep it null, or do `user.setLoginCredential()` – buræquete Oct 04 '19 at 05:50
  • I copied exactly the same... error *message* is **"No message available"** and trace is something like this : **java.lang.StackOverflowError\r\n\tat com.mua.cse616.Model.LoginCredential.toString...** do I need to override toString method ? – Maifee Ul Asad Oct 04 '19 at 06:09
  • 1
    @MaifeeUlAsad no no, that is because of that stack overflow case I told you, why are you trying to print an entity? Just return it in your controller method, should be OK. Otherwise try to use `@Autowired ObjectMapper objectMapper` to print it, do `System.out.println(objectMapper.writeValueAsString(loginCredential))`. With `@JsonBackReference` & `@JsonManagedReference` annotations, jackson fixes that recursion issue. But `toString()` will still throw that error. – buræquete Oct 04 '19 at 06:11
  • 1
    @MaifeeUlAsad you already fixed every issue, just `toString()` issue, just put `@ToString(exclude = "loginCredential")` from lombok on `User` class – buræquete Oct 04 '19 at 06:13
0

You don't need @GeneratedValue for User object

Add CascadeType for OneToOne

By doing this you tell hibernate to save User to the database when saving LoginCredential.

@Data
@Entity
public class LoginCredential implements Serializable {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   Long userID;

   String eMail;

   String passwordHash;

   @OneToOne(cascade = {CascadeType.ALL}, mappedBy = "loginCredential", fetch = FetchType.LAZY)
   User user;
}

Refer this for more detail

MyTwoCents
  • 7,284
  • 3
  • 24
  • 52
  • Blues are what I'm receiving , red saving, yellow getting.. user is null.. please see [here](https://i.stack.imgur.com/tCNqf.png) – Maifee Ul Asad Oct 04 '19 at 04:35
0

Make User class Serializable as well.

Shardendu
  • 3,480
  • 5
  • 20
  • 28
  • User should not be null inside Login Credential object. while you receiving in newLoginCredential(@RequestBody LoginCredential newLoginCredential) method – Shardendu Oct 04 '19 at 06:25