一对一外键映射

时间:2015-07-01 01:59:48

标签: hibernate jpa persistence

在Book和author之间的下面的映射中,我使用book表中的外键列author_id进行了一对一的映射,并且关系在book-> author中被标记为可选false,但是当我查询时 session.createQuery("来自Book")。list();

1)当标记为可选为false时,它急切地获取作者信息,为什么不代理它,有人可以解释为什么它不能像在主键连接列中那样使用外键方法吗?

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)当我从author-> book中进行双向操作时,运行查询 session.createQuery("来自作者")。list();

它首先按照预期从作者中提取记录,然后使用2个连接运行如下的单个查询。 为什么它必须使用作者ID?

进行连接而不是直接查询book表
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=?

2 个答案:

答案 0 :(得分:0)

Hibernate只代理标记为LAZY = true的模型。以下线程可以帮助您回答某些查询。 Hibernate lazy loading, optional = false

答案 1 :(得分:0)

1)JPA默认情况下ToOne关联是急切的。为了让它变得懒惰:

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

您是否使用@PrimaryKeyJoinColumn并不重要。

2)没错,这是不必要的,Hibernate可以做得更好;请看这question。关键是Hibernate在急切地获取关联实体时想要避免其他查询。

那么,这里发生了什么?

首先,您可能没有在Author方面(left outer join来自哪里)声明关联是强制性的。

其次,Hibernate通过提供的作者ID(对于第一个查询返回的每个作者)读取本书。如果你手动执行它,你会得到相同的查询:

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

Hibernate使用第一个连接表来阅读本书,第二个用于阅读本书的作者,第三个用于阅读作者的书。

第三个连接表在这里是非常不必要的,我期望Hibernate在未来的版本中改进它。

如果您手动执行上述查询(在同一查询中而不是在新查询中读取本书的作者),则第二个连接表是一个很好的优化,但在您的示例中是不必要的,因为已经加载了所需的作者。我希望在未来的版本中也能得到改进。

PS AuthorBook之间的真实世界关联是一对多。