Search code examples
javahibernateone-to-one

Hibernate - @OneToOne relation


I'm trying to create a one-to-one relationship. On the owner side, the bookIsn (non-primary) is referring to the primary key of the target entity. Now the issue is if I want to read all reviews (reviewRepository.findAll()) the following error is thrown if no book is available for the given bookIsn:

Unable to find com.repository.model.Book with id 1

But the expecting behavior would be that the book entity simply is set to null if no book could be found. Like it does if I use the reviewId for joining the column @JoinColumn( name = "review_id", ... ) instead of the review_isn.

Can somebody explain why it's working with a primary key, but not for a non-primary attribute? What needs to be done to make it work for non-primary attributes as well?

Below the two classes:

Review.java:

@Entity
@Data
@Table(name = "review")
public class Review {

  @Id
  @Column(name="review_id")
  private String reviewId;

  @Column(name="book_isn")
  private String bookIsn;

  @OneToOne
  @JoinColumn(
    name = "book_isn",
    referencedColumn = "book_isn",
    insertable = false,
    updatable = false)
  private Book book;
}

Book.java:

@Entity
@Data
@Table(name = "book")
public class Book {

  @Id
  @Column(name="book_isn")
  private String bookId;

  @Column(name="book_name")
  private String bookName;

}

Solution

  • First of all I have to say that I would not suggest you to use @Data lombok annotation with hibernate entity class. See for example this article.

    Then, I would suggest you to correct your Review entity mapping in this way:

    import javax.persistence.Transient;
    
    @Entity
    @Table(name = "review")
    public class Review
    {
       private String reviewId;
       private Book book;
       
       @Id
       @Column(name = "review_id")
       public String getReviewId()
       {
          return reviewId;
       }
       
       @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
       @JoinColumn(name = "book_isn")
       public Book getBook()
       {
          return book;
       }
       
       // setters omitted for brevity
    
       @Transient
       public String getBookId()
       {
          return book != null ? book.getBookId() : null;
       }
    }
    

    You can persist your Review entities in the following way:

    Book b1 = new Book();
    b1.setBookId("BK1");
    b1.setBookName("Book 1");
          
    Review r1 = new Review();
    r1.setReviewId("R1");
    r1.setBook(b1);
          
    session.persist(r1);
    
    Review r2 = new Review();
    r2.setReviewId("R2");
    session.persist(r2); // book_isn is NULL for the R2 Review
    

    P.S. Please also note that it is not recommended to use string as a type for primary key for big tables due to the potential performance problem.