1

In the below mappings between Book and author, I have the one to one mapping done using foreign key column author_id in book table and relationship is marked as optional false from book->author, but when I query session.createQuery("from Book").list();

1) it is eagerly fetching author info when it is marked optional is false and why not proxy it, can someone explain why it does not work with foreign key approach like it does in primary key join column?

Hibernate: select book0_.BOOK_ID as BOOK_ID1_1_, book0_.AUTHOR_ID as AUTHOR_I5_1_, book0_.description as descript2_1_, book0_.PUBLISHED as PUBLISHE3_1_, book0_.title as title4_1_ from BOOK book0_

Hibernate: select author0_.AUTHOR_ID as AUTHOR_I1_0_0_, author0_.email as email2_0_0_, author0_.name as name3_0_0_ from AUTHOR author0_ where author0_.AUTHOR_ID in (?, ?, ?)

Hibernate: select author0_.AUTHOR_ID as AUTHOR_I1_0_0_, author0_.email as email2_0_0_, author0_.name as name3_0_0_ from AUTHOR author0_ where author0_.AUTHOR_ID in (?, ?, ?)
@Entity
@Table(name = "BOOK")
public class Book {

     private long id;
        private String title;
        private String description;
        private Date publishedDate;

        private Author author;

        public Book() {
        }

        @Id
        @Column(name = "BOOK_ID")
        @GeneratedValue
        public long getId() {
            return id;
        }

        public void setId(long id) {
            this.id = id;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        @Temporal(TemporalType.DATE)
        @Column(name = "PUBLISHED")
        public Date getPublishedDate() {
            return publishedDate;
        }

        public void setPublishedDate(Date publishedDate) {
            this.publishedDate = publishedDate;
        }

        @OneToOne(cascade = CascadeType.ALL,optional=false)
        @JoinColumn(name = "AUTHOR_ID")
        public Author getAuthor() {
            return author;
        }

        public void setAuthor(Author author) {
            this.author = author;
        }
    }
@Entity
@BatchSize(size=3)
@Table(name = "AUTHOR")
public class Author {
    private long id;
    private String name;
    private String email;


    public Author() {
    }

    public Author(String name, String email) {
        this.name = name;
        this.email = email;
    }

    @Id
    @Column(name = "AUTHOR_ID")
    @GeneratedValue
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

2) when I make this bi-directional from author->book, and run the query session.createQuery("from Author").list();

It first fetches records from Author as expected , then runs individual queries as below with 2 joins. Why it has to do a join and not directly query the book table using the author Id?

Hibernate: select author0_.AUTHOR_ID as AUTHOR_I1_0_, author0_.email as email2_0_, author0_.name as name3_0_ from AUTHOR author0_

Hibernate: select book0_.BOOK_ID as BOOK_ID1_1_2_, book0_.AUTHOR_ID as AUTHOR_I5_1_2_, book0_.description as descript2_1_2_, book0_.PUBLISHED as PUBLISHE3_1_2_, book0_.title as title4_1_2_, author1_.AUTHOR_ID as AUTHOR_I1_0_0_, author1_.email as email2_0_0_, author1_.name as name3_0_0_, book2_.BOOK_ID as BOOK_ID1_1_1_, book2_.AUTHOR_ID as AUTHOR_I5_1_1_, book2_.description as descript2_1_1_, book2_.PUBLISHED as PUBLISHE3_1_1_, book2_.title as title4_1_1_ from BOOK book0_ inner join AUTHOR author1_ on book0_.AUTHOR_ID=author1_.AUTHOR_ID left outer join BOOK book2_ on author1_.AUTHOR_ID=book2_.AUTHOR_ID where book0_.AUTHOR_ID=?

Hibernate: select book0_.BOOK_ID as BOOK_ID1_1_2_, book0_.AUTHOR_ID as AUTHOR_I5_1_2_, book0_.description as descript2_1_2_, book0_.PUBLISHED as PUBLISHE3_1_2_, book0_.title as title4_1_2_, author1_.AUTHOR_ID as AUTHOR_I1_0_0_, author1_.email as email2_0_0_, author1_.name as name3_0_0_, book2_.BOOK_ID as BOOK_ID1_1_1_, book2_.AUTHOR_ID as AUTHOR_I5_1_1_, book2_.description as descript2_1_1_, book2_.PUBLISHED as PUBLISHE3_1_1_, book2_.title as title4_1_1_ from BOOK book0_ inner join AUTHOR author1_ on book0_.AUTHOR_ID=author1_.AUTHOR_ID left outer join BOOK book2_ on author1_.AUTHOR_ID=book2_.AUTHOR_ID where book0_.AUTHOR_ID=?

Hibernate: select book0_.BOOK_ID as BOOK_ID1_1_2_, book0_.AUTHOR_ID as AUTHOR_I5_1_2_, book0_.description as descript2_1_2_, book0_.PUBLISHED as PUBLISHE3_1_2_, book0_.title as title4_1_2_, author1_.AUTHOR_ID as AUTHOR_I1_0_0_, author1_.email as email2_0_0_, author1_.name as name3_0_0_, book2_.BOOK_ID as BOOK_ID1_1_1_, book2_.AUTHOR_ID as AUTHOR_I5_1_1_, book2_.description as descript2_1_1_, book2_.PUBLISHED as PUBLISHE3_1_1_, book2_.title as title4_1_1_ from BOOK book0_ inner join AUTHOR author1_ on book0_.AUTHOR_ID=author1_.AUTHOR_ID left outer join BOOK book2_ on author1_.AUTHOR_ID=book2_.AUTHOR_ID where book0_.AUTHOR_ID=?

Hibernate: select book0_.BOOK_ID as BOOK_ID1_1_2_, book0_.AUTHOR_ID as AUTHOR_I5_1_2_, book0_.description as descript2_1_2_, book0_.PUBLISHED as PUBLISHE3_1_2_, book0_.title as title4_1_2_, author1_.AUTHOR_ID as AUTHOR_I1_0_0_, author1_.email as email2_0_0_, author1_.name as name3_0_0_, book2_.BOOK_ID as BOOK_ID1_1_1_, book2_.AUTHOR_ID as AUTHOR_I5_1_1_, book2_.description as descript2_1_1_, book2_.PUBLISHED as PUBLISHE3_1_1_, book2_.title as title4_1_1_ from BOOK book0_ inner join AUTHOR author1_ on book0_.AUTHOR_ID=author1_.AUTHOR_ID left outer join BOOK book2_ on author1_.AUTHOR_ID=book2_.AUTHOR_ID where book0_.AUTHOR_ID=?
Tiny
  • 24,933
  • 92
  • 299
  • 571
user2221654
  • 271
  • 1
  • 5
  • 17

2 Answers2

0

Hibernate only proxies the models marked as LAZY=true. The following thread might help you answer certain queries. Hibernate lazy loading, optional = false

Community
  • 1
  • 1
Johnson Abraham
  • 741
  • 4
  • 12
0

1) ToOne associations are eager by default in JPA. To make it lazy:

@OneToOne(cascade = CascadeType.ALL, optional = false, , fetch = FetchType.LAZY)

It does not matter whether you use @PrimaryKeyJoinColumn or not.

2) True, this is unnecessary and Hibernate could do it better; please see this question. The point is that Hibernate wants to avoid additional queries when eagerly fetching associated entities.

So, what's happening here?

Firstly, you probably didn't declare the association as mandatory on the Author side (that's where left outer join comes from).

Secondly, Hibernate reads the book by the provided author id (for each author returned by the first query). You would get the same query if you manually did it:

select b from Book b where b.author.id = :authorId

Hibernate uses the first join table to read the book, the second one to read the book's author, and the third one to read the author's book.

The third join table is quite unnecessary here and I expect Hibernate to improve this in future versions.

The second join table is a nice optimization if you manually execute the above query (read the book's author in the same query instead of in a new one), but is unnecessary in your example, since the required authors have already been loaded. I would expect this to be improved in future versions as well.

PS The real-world association between Author and Book is one-to-many.

Community
  • 1
  • 1
Dragan Bozanovic
  • 21,631
  • 4
  • 36
  • 100