2

Spring Boot version 2.1.6.

I have User class :

@Data
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long userID;
    String eMail;
    public User()
    {

    }
}

And a LoginCredential class :

@Data
@Entity
public class LoginCredential {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long userID;
    String eMail;
    String passwordHash;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "id")
    User user;
    public LoginCredential()
    {

    }
}

Shouldn't it create an instance of User when I create an instance of LoginCredential.

Cause I run this command : curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"a\", \"passwordHash\": \"b\" }" http://localhost:8080/login

And I got an instance of LoginCredential but not of an User.

Response :

{"userID":1,"passwordHash":"b","user":null,"email":"a"}

And then I run this command : curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"c\", \"passwordHash\": \"d\" ,\"user\":{}}" http://localhost:8080/login

And what I got is nothing, not of same ID.

Response :

{"userID":2,"passwordHash":"d","user":{"userID":3,"email":null},"email":"c"}

Am I missing something ? How this can be resolved ?

LoginCredentialController partially :

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

My pom.xml

My application.properties

Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86
  • 1
    Post your curl requests response – rimonmostafiz Sep 24 '19 at 15:08
  • 1
    You asked `Shouldn't it create an instance of User when I create an instance of LoginCredential?` That depends on what you are doing on your /login controller method. Please also post it here. – rimonmostafiz Sep 24 '19 at 15:14
  • edited, please see @rimonmostafiz – Maifee Ul Asad Sep 24 '19 at 15:16
  • Both results are exactly as expected - the first request does not contain a `User` so you receive no one, the second requests contains an empty `User` so you get one, but please consider that fact that the `LoginCredential#userID` has nothing to do with a `User#userID`. – Smutje Sep 25 '19 at 07:10
  • @Smutje please see [this](https://youtu.be/qYTSCi6bh00?t=1030), just where I have set the starting. See he uses something that **creates child with the same id of parents **. I tried his code from git, and tutorial. doesn't work. And his tut doesn't even match with git code. – Maifee Ul Asad Sep 25 '19 at 07:56

2 Answers2

1
@Data
@Entity
public class LoginCredential implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long credentailID;
    String eMail;
    String passwordHash;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "id", nullable = false)
    @OneToOne(fetch = FetchType.EAGER) //<-- EAGER is default, but try setting it explicitly
    User user;
    public LoginCredential()
    {

    }
}



@PostMapping("/login")
LoginCredential newLoginCredential(@RequestBody LoginCredential newLoginCredential)
{   
    LoginCredential saveResult = repository.save(newLoginCredential);
    //load it again after saving 
    return repository.findbyId(saveResult.getuserId);
}

or better:

   @PostMapping("/login")
    ResponseEntity newLoginCredential(@RequestBody LoginCredential newLoginCredential)
    {   
      try{
        LoginCredential saveResult = repository.save(newLoginCredential);
        //load it again after saving 
        LoginCredential loaded = repository.findbyId(saveResult.getuserId);
//if you set a breakpoint here, you should already see if it is working, if its ok here, then it might have to do with serilalization ( jackson )...
            return new ResponseEntity<>(loaded,HttpStatus.OK);
          }
           catch (Exception e){
                log.error(e.getMessage());
                return       ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
            }
        }

and, do you have a dependency for jackson ? -> its responsible for serializing the object to json ...

<dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

and this in your properties:

spring.jackson.serialization.indent_output=true
spring.jackson.default-property-inclusion=ALWAYS

hope that helps, cheers

womd
  • 3,077
  • 26
  • 20
  • Classes are annotated with `Entity` and `Data` too – Maifee Ul Asad Sep 24 '19 at 14:36
  • so what should I do ? I couldn't get your answer – Maifee Ul Asad Sep 24 '19 at 14:41
  • First of all there were a lot of error, in your code I fixed them. But it gave me [this](https://i.stack.imgur.com/SANQT.png), just in a styled way. But see carefully there `id/userID` is different. The key is not working. @womd – Maifee Ul Asad Sep 25 '19 at 07:32
  • 1
    ok, thx for fixing, should just illustrate basicly, but isn't this what you wanted ? - you get the UserCredentials with its Child User ....? .. the styled output comes from .intent_output=True , but the crucial thing is: spring.jackson.default-property-inclusion=ALWAYS ... when the key's dont match - verify your database manually - are you sure its not saved that way ? – womd Sep 25 '19 at 07:36
  • thanks for this much help, we are almost there. I have the child in parent right now. but I want their `userID/id` to be same. – Maifee Ul Asad Sep 25 '19 at 07:48
  • @Maifee Ul Asad - i would remove the userId from credentails or rename it to credentialId - if you want the id'S to be the same, you got to change ID-Stragegy for User-Entity to set it manually! since you told jpa/hibernate to generate ids itself, and it doese so on every save .... ( a quick an dirty would be to remove/recreate the tables and start from 1 with ids but that would only be temporarily correct ....-> see here for strategies: https://stackoverflow.com/questions/10041938/how-to-choose-the-id-generation-strategy-when-using-jpa-and-hibernate – womd Sep 25 '19 at 07:54
  • I'm not clear with these `strategy`. Can you edit your answer please. – Maifee Ul Asad Sep 25 '19 at 07:58
  • @Maifee Ul Asad - i would just rename the field of userCredentail - when you need the userId you use usercredentail.user.getId() - try clearing your table, it looks like the data is persiseted that way .... you could verify with a manual query ... btw. what is your db ? – womd Sep 25 '19 at 08:06
0

Here is the solution, I later asked another question and found out an answer

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