【发布时间】:2021-08-21 05:50:44
【问题描述】:
我是 Java Persistence API 和 Hibernate 的新手,我使用 Spring JPA 存储库在 DB 中进行查询。现在我在 Parent Child 关系中有两个实体,Parent 实体与 @OneToMany 和 Child 实体与 @ManyToOne 映射。
父实体:-
@Entity
@Table(name = "PERSONS")
public class Persons {
...
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
public List<Cards> cards = new ArrayList<Cards>();
...
}
子实体:-
@Entity
@Table(name = "CARDS")
public class Cards {
...
@ToString.Exclude
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PERSON_ID", nullable = false, insertable = false, updatable = false)
public Person person;
...
}
我正在使用我的 PersonsRepository,如下所示:-
@Repository
public interface PersonsRepository extends JpaRepository<Persons, String> {
....
}
现在关系中使用的 fetchType 在两端都是 LAZY。现在,每当我尝试遍历列表并尝试使用 person.getCards() 处理每个卡片时,都会出现以下错误:-
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.xxx.abc.Persons.cards, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
at java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821)
at java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
现在我发现每个人都说在 Hibernate 中使用 LAZY 是最好的方法,而且它还更多地说明了代码的正确设计。我同意我使用 person.getCards() 不会有任何打开的会话,这就是它给我 LazyInitializationException 的原因,但这背后的意图是保存更多的数据库调用。
假设我有 1000 个人员列表,这意味着我必须为每个人进行 1000 次单独调用 getCards()。这就是为什么如果我在 Person @OneToMany 中使用 FETCHTYPE.EAGER,性能影响是什么,因为所有内容都会被急切地获取。
需要有关针对此类问题所遵循的最佳做法的建议。 TIA。
编辑:- 我在服务类中有一个方法,我在其中使用@transactional,如下所示:-
@Transactional(readOnly = true)
public void fetchData(Integer param1, Timestamp param2, Timestamp param3, List<String> param4, NavigableMap<Long, List<Cards>> param5) {
List<Persons> validPersons = personRepo.getCardsPerPerson(param2, param3);
if(validPersons != null && !validPersons.isEmpty()) {
// store the cards on the basis of epoch timestamp
prepareTimestampVsCardsMap(validPersons, param4, param5);
}
}
private void prepareTimestampVsCardsMap(List<Persons> validPersons, List<String> uList, NavigableMap<Long, List<Cards>> timestampVsCardsList) {
for(Person person : validPersons) {
Long epoch = order.getOrderTime().getTime();
Set<Cards> cardsPerPerson = person.getCards();
}
}
此外,存储库中用于获取与某人关联的卡片的查询正在使用 join fetch,如下所示:-
@Query(value = "select p from Person p join fetch Cards c on p.id = c.id WHERE p.orderTime BETWEEN ?1 AND ?2 ORDER BY orderTime ASC")
public List<Person> getCardsPerPerson(Timestamp param1, Timestamp param2);
我仍然遇到上面提到的 LazyInitializationException。谁能帮忙。
【问题讨论】:
-
你在哪里调用 person.getCards()?在服务类中对吗?我假设您没有在您的服务方法中使用“@Transactional”,在这种情况下,当您从存储库中获取人员时,会话将关闭并且您的延迟初始化如果没有用。您的服务方法上应该有“@Transactional”,以便在您的所有业务案例在单个事务中完成后关闭会话。
-
最好将获取策略设置为
Lazy。如果你需要卡片和人,你应该通过你的存储库调用来获取它(例如,在方法上使用@Query("select p from Person p join fetch Cards c on p.id = c.person.id"),或者使用实体图)。这样,您只需调用一次 DB 即可获取您需要的所有数据。 -
@Lucia 请看我的编辑。我已经添加了所有东西并使用“@Transactional”但得到了同样的错误
-
@StefanGolubović 我以同样的方式使用了查询,使用 join fetch 来获取与某人关联的卡片列表。请在我的编辑中查看。仍然没有成功并得到同样的异常
-
@user2594 抱歉,我混合使用了 SQL 和 JPQL。你可以试试
@Query("select p from Person p join fetch p.cards where ...")。您总是可以在 vladmihalcea.com 博客上找到解决持久性问题的方法。
标签: spring-boot hibernate jpa spring-data-jpa hibernate-mapping