Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/359.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Hibernate.initialize()的工作原理_Java_Hibernate_Proxy_Lazy Initialization - Fatal编程技术网

Java Hibernate.initialize()的工作原理

Java Hibernate.initialize()的工作原理,java,hibernate,proxy,lazy-initialization,Java,Hibernate,Proxy,Lazy Initialization,我知道要在会话之外使用惰性加载对象/集合,我们需要Hibernate.initialize(Object obj),以便作为参数传递给initialize()方法的对象被初始化,并且可以在会话范围之外使用 但我无法理解它是如何工作的。我的意思是,如果我们这样做了,那么我们最终会进行急切的抓取,那么为什么我们会在配置中偷懒,并最终在运行时进行急切的抓取呢 换句话说,我想知道使用Hibernate.initialize()和急切地加载该对象之间的区别 我是弄错了还是遗漏了什么?区别在于适用范围 使集

我知道要在会话之外使用惰性加载对象/集合,我们需要
Hibernate.initialize(Object obj)
,以便作为参数传递给initialize()方法的对象被初始化,并且可以在会话范围之外使用

但我无法理解它是如何工作的。我的意思是,如果我们这样做了,那么我们最终会进行急切的抓取,那么为什么我们会在配置中偷懒,并最终在运行时进行急切的抓取呢

换句话说,我想知道使用
Hibernate.initialize()
和急切地加载该对象之间的区别


我是弄错了还是遗漏了什么?

区别在于适用范围

使集合关联变为惰性的原因是避免在不需要父对象的情况下,每次加载父对象时都让它加载集合

如果您在正常情况下延迟加载集合,但对于特定用途,您需要确保在会话关闭之前已加载集合,您可以使用
Hibernate.initialize(Object obj)


如果您实际上总是需要加载集合,那么您确实应该急切地加载它。然而,在大多数软件中,情况并非如此。

考虑@Don Ruby answer


下一个区别是,
Hibernate.initialize
生成并执行额外的sql以获取数据。所以您可以在会话关闭后使用它。当您在实体中使用
Eager
fetch时,在数据库中查找数据(在连接会话下)时,始终会提取集合,但在数据库中查找数据之后不会提取。

实际上,如果您使用Eager,当您有大集合时,它确实会影响您的性能。因此,在这种情况下使用
Hibernate.initialize
是一个好主意

看看这个:
考虑以下示例:

我有一个实体LoanApplication(在本例中,它是一个非常重的对象),其中包含各种字段(也可能很大)。考虑一下LoanApplication的次级贷款领域。
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "application_fk")
@Index(name = "appl_fk_idx_subloansLoanApp")
private Set<SubLoan> subLoans;

这主要有助于提高性能,因为每次检索LoanApplication时,大型对象集(即“subLoan”)最初将为空,除非您确实需要它们。

考虑您有一个表,该表可能与其他4个表有关系。在这种情况下,如果使用eager-then,那么在每次获取操作中都将获取所有四个相关表中的所有对应关系


但是,您可能需要这样的需求,即只需要从相关表中的一个表中获取数据,因此在这种情况下,您可以只获取所需的关系,而不是使用Hibernate初始化设备来装载整个四个相关表的数据。

< P> >代码> Hibernate。仅在使用二级缓存时有用。否则,您将发出第二个查询,该查询的效率低于仅使用初始查询初始化代理的效率

冒着N+1查询问题的风险 因此,在执行以下测试用例时:

LOGGER.info("Clear the second-level cache");

entityManager.getEntityManagerFactory().getCache().evictAll();
 
LOGGER.info("Loading a PostComment");
 
PostComment comment = entityManager.find(
    PostComment.class,
    1L
);
 
assertEquals(
    "A must read!",
    comment.getReview()
);
 
Post post = comment.getPost();
 
LOGGER.info("Post entity class: {}", post.getClass().getName());
 
Hibernate.initialize(post);
 
assertEquals(
    "High-Performance Java Persistence",
    post.getTitle()
);
LOGGER.info("Loading a Post");
 
Post post = entityManager.find(
    Post.class,
    1L
);
 
List<PostComment> comments = post.getComments();
 
LOGGER.info("Collection class: {}", comments.getClass().getName());
 
Hibernate.initialize(comments);
 
LOGGER.info("Post comments: {}", comments);
首先,我们将清除二级缓存,因为除非显式启用二级缓存并配置提供程序,否则Hibernate不会使用二级缓存

运行此测试用例时,Hibernate执行以下SQL语句:

-- Clear the second-level cache
 
-- Evicting entity cache: com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post
-- Evicting entity cache: com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment
 
-- Loading a PostComment
 
SELECT pc.id AS id1_1_0_,
       pc.post_id AS post_id3_1_0_,
       pc.review AS review2_1_0_
FROM   post_comment pc
WHERE  pc.id=1
 
-- Post entity class: com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post$HibernateProxy$5LVxadxF
 
SELECT p.id AS id1_0_0_,
       p.title AS title2_0_0_
FROM   post p
WHERE  p.id=1
我们可以看到第二级缓存被正确地逐出,并且在获取
PostComment
实体之后,post实体由一个
HibernateProxy
实例表示,该实例仅包含从post\u注释数据库表行的
post\id
列检索的
post
实体标识符

现在,由于调用了
Hibernate.initialize
方法,执行了一个辅助SQL查询来获取
Post
实体,这不是很有效,可能会导致N+1查询问题

在JPQL中使用joinfetch 在前一种情况下,应该使用JOIN-FETCH-JPQL指令获取
post命令

LOGGER.info("Clear the second-level cache");
 
entityManager.getEntityManagerFactory().getCache().evictAll();
 
LOGGER.info("Loading a PostComment");
 
PostComment comment = entityManager.createQuery(
    "select pc " +
    "from PostComment pc " +
    "join fetch pc.post " +
    "where pc.id = :id", PostComment.class)
.setParameter("id", 1L)
.getSingleResult();
 
assertEquals(
    "A must read!",
    comment.getReview()
);
 
Post post = comment.getPost();
 
LOGGER.info("Post entity class: {}", post.getClass().getName());
 
assertEquals(
    "High-Performance Java Persistence",
    post.getTitle()
);
这一次,Hibernate只执行一条SQL语句,我们不再有遇到N+1查询问题的风险:

-- Clear the second-level cache
 
-- Evicting entity cache: com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post
-- Evicting entity cache: com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment
 
-- Loading a PostComment
 
SELECT pc.id AS id1_1_0_,
       p.id AS id1_0_1_,
       pc.post_id AS post_id3_1_0_,
       pc.review AS review2_1_0_,
       p.title AS title2_0_1_
FROM   post_comment pc
INNER JOIN post p ON pc.post_id=p.id
WHERE  pc.id=1
 
-- Post entity class: com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post
将二级缓存与休眠一起使用。初始化 因此,要查看
Hibernate.initialize
何时真正值得使用,您需要使用二级缓存:

LOGGER.info("Loading a PostComment");
 
PostComment comment = entityManager.find(
    PostComment.class,
    1L
);
 
assertEquals(
    "A must read!",
    comment.getReview()
);
 
Post post = comment.getPost();
 
LOGGER.info("Post entity class: {}", post.getClass().getName());
 
Hibernate.initialize(post);
 
assertEquals(
    "High-Performance Java Persistence",
    post.getTitle()
);
这一次,我们不再逐出二级缓存区域,而且,由于我们使用读写缓存并发策略,实体在持久化后立即被缓存,因此在运行上述测试用例时不需要执行SQL查询:

-- Loading a PostComment
 
-- Cache hit : 
region = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment`, 
key = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment#1`
 
-- Proxy class: com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post$HibernateProxy$rnxGtvMK
 
-- Cache hit : 
region = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post`, 
key = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post#1`
如缓存命中日志消息所示,
PostComment
post
关联都从二级缓存中获取

因此,如果您使用的是二级缓存,那么可以使用
Hibernate.initiaize
获取满足业务用例所需的额外关联。在这种情况下,即使有N+1个缓存调用,每个调用都应该运行得非常快,因为二级缓存配置正确,并且数据从内存返回

休眠。初始化
和代理集合
Hibernate.initialize
也可用于集合。现在,因为二级缓存集合是通读的,这意味着它们在运行以下测试用例时第一次加载时存储在缓存中:

LOGGER.info("Clear the second-level cache");

entityManager.getEntityManagerFactory().getCache().evictAll();
 
LOGGER.info("Loading a PostComment");
 
PostComment comment = entityManager.find(
    PostComment.class,
    1L
);
 
assertEquals(
    "A must read!",
    comment.getReview()
);
 
Post post = comment.getPost();
 
LOGGER.info("Post entity class: {}", post.getClass().getName());
 
Hibernate.initialize(post);
 
assertEquals(
    "High-Performance Java Persistence",
    post.getTitle()
);
LOGGER.info("Loading a Post");
 
Post post = entityManager.find(
    Post.class,
    1L
);
 
List<PostComment> comments = post.getComments();
 
LOGGER.info("Collection class: {}", comments.getClass().getName());
 
Hibernate.initialize(comments);
 
LOGGER.info("Post comments: {}", comments);
但是,如果已缓存
PostComment
集合:

doInJPA(entityManager -> {
    Post post = entityManager.find(Post.class, 1L);
 
    assertEquals(3, post.getComments().size());
});
运行上一个测试用例时,Hibernate只能从缓存中获取所有数据:

-- Loading a Post
 
-- Cache hit : 
region = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post`, 
key = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post#1`
 
-- Collection class: org.hibernate.collection.internal.PersistentBag
 
-- Cache hit : 
region = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post.comments`, 
key = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post.comments#1`
 
-- Cache hit : 
region = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment`, 
key = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment#1`
 
-- Cache hit : 
region = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment`, 
key = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment#2`
 
-- Cache hit : 
region = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment`, 
key = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$PostComment#3`

如果您有一个将在会话外部访问的集合,并且您只执行Hibernate.initialize,那么在访问成员时将出现一个很好的LazyInitializationException。