-- 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
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配合使用
因此,要查看何时真正值得使用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()
);
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执行SQL查询以加载PostComment集合:
-- 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, but item is unreadable/invalid :
region = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post.comments`,
key = `com.vladmihalcea.book.hpjp.hibernate.fetching.HibernateInitializeTest$Post.comments#1`
SELECT pc.post_id AS post_id3_1_0_,
pc.id AS id1_1_0_,
pc.id AS id1_1_1_,
pc.post_id AS post_id3_1_1_,
pc.review AS review2_1_1_
FROM post_comment pc
WHERE pc.post_id=1
-- Post comments: [
PostComment{id=1, review='A must read!'},
PostComment{id=2, review='Awesome!'},
PostComment{id=3, review='5 stars'}
]
但是,如果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`
7条答案
按热度按时间1szpjjfi1#
区别在于适用范围。
将集合关联设置为惰性的原因是为了避免在每次加载父对象时都让它加载集合(如果您实际上并不需要它)。
如果您通常是惰性加载集合,但对于特定用途,您需要确保在会话关闭之前已经加载了集合,则可以使用前面提到的
Hibernate.initialize(Object obj)
。如果你实际上总是需要加载这个集合,你应该急切地加载它,但在大多数软件中,情况并非如此。
w8f9ii692#
Hibernate.initialize(proxy)
只有在使用二级缓存时才有用,否则,您将发出第二个查询,这比仅使用初始查询初始化代理效率低。存在N+1查询问题的风险
因此,在执行以下测试用例时:
首先,我们将清除二级缓存,因为除非显式启用二级缓存并配置提供程序,否则Hibernate不会使用二级缓存。
运行此测试用例时,Hibernate将执行以下SQL语句:
我们可以看到,二级缓存被正确地逐出,并且在获取
PostComment
实体之后,post实体由HibernateProxy
示例表示,该示例仅包含从post_comment数据库表行的post_id
列检索到的Post
实体标识符。现在,由于对
Hibernate.initialize
方法的调用,执行了一个辅助SQL查询来获取Post
实体,这不是很有效,可能会导致N+1查询问题。搭配JPQL使用JOIN FETCH
在前一种情况下,应该使用JOIN FETCH JPQL指令提取
PostComment
及其post关联。这一次,Hibernate执行单个SQL语句,我们不再冒遇到N+1查询问题的风险:
将二级缓存与
Hibernate.initialize
配合使用因此,要查看何时真正值得使用
Hibernate.initialize
,需要使用二级缓存:这一次,我们不再收回二级缓存区域,并且,由于我们使用READ_WRITE缓存并发策略,实体在持久化后立即被缓存,因此在运行上面的测试用例时不需要执行SQL查询:
PostComment
和post
关联都是从二级高速缓存中提取的,如该高速缓存命中日志消息所示。因此,如果使用二级缓存,使用
Hibernate.initiaize
获取满足业务用例所需的额外关联是很好的。在这种情况下,即使有N+1个缓存调用,每个调用也应该运行得非常快,因为二级缓存配置正确,数据从内存返回。Hibernate.initialize
和代理集合Hibernate.initialize
也可以用于集合。现在,由于二级缓存集合是读通的,这意味着当运行以下测试用例时,它们在第一次加载时被存储该高速缓存中:Hibernate执行SQL查询以加载
PostComment
集合:但是,如果
PostComment
集合已缓存:当运行前面的测试用例时,Hibernate只能该高速缓存中获取所有数据:
ffscu2ro3#
请考虑以下示例:
我有一个实体LoanApplication(在本例中是一个非常重的对象),其中包含各种字段(也可能很大)。
在这个例子中,FetchType是LAZY。现在,如果在一些控制器的方法中执行一些操作时,你遇到LoanApplication,那么subLoans集最初将是null,除非你想使用它。在这种情况下,你使用Hibernate。初始化如下:
这主要有助于提高性能,因为每次检索LoanApplication时,大量对象(即'subLoan')最初都是空的,除非您确实需要它们。
lpwwtiir4#
考虑@Don Ruby的回答
下一个区别是
Hibernate.initialize
生成并执行额外的sql来获取数据。所以你可以在会话关闭后使用它。当你在实体中使用Eager
fetch时,它总是在数据库中查找数据(在连接会话下)的过程中获取集合,而不是在它之后。iklwldmw5#
实际上,如果你使用EAGER,当你有大的集合时,它确实会影响你的性能。所以,在这种情况下使用
Hibernate.initialize
是个好主意。看看这个:Hibernate Lazy Fetch vs Eager Fetch Type
unftdfkk6#
假设您有一个表,它可能与其他4个表有关系。在这种情况下,如果您使用eager then,则在每次获取操作中,将获取所有4个相关表的所有对应关系。
但是,考虑到您可能只需要相关表中的一个表的数据,因此在这种情况下,您可以只获取所需的关系,而不是使用Hibernate.initialize工具装载整个四个相关表的数据。
t5fffqht7#
下面是我所做的:
@自动连线会话工厂(org. hib.会话工厂)
假设会话已经打开,则强制初始化(也称为强制加入)集合/代理(1-m/1-1)。如果会话不存在,则打开一个新会话,执行操作并再次关闭它。
如果你发现这里面有什么瑕疵就告诉我