Hibernate Search 手动索引抛出“org.hibernate.TransientObjectException:实例未与此会话关联”
Posted
技术标签:
【中文标题】Hibernate Search 手动索引抛出“org.hibernate.TransientObjectException:实例未与此会话关联”【英文标题】:Hibernate Search manual indexing throw a "org.hibernate.TransientObjectException: The instance was not associated with this session" 【发布时间】:2019-07-06 14:37:23 【问题描述】:我在我的 Spring Boot 2 应用程序上使用 Hibernate Search 5.11,允许进行全文研究。 这个库需要索引文档。
当我的应用程序启动时,我尝试每五分钟手动重新索引索引实体 (MyEntity.class) 的数据(出于特定原因,由于我的服务器上下文)。
我尝试索引 MyEntity.class 的数据。
MyEntity.class 有一个属性 attachFiles,它是一个哈希集,填充了一个连接 @OneToMany(),启用了延迟加载模式:
@OneToMany(mappedBy = "myEntity", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<AttachedFile> attachedFiles = new HashSet<>();
我编写了所需的索引过程,但是当给定实体的 attachFiles 属性填充了一个或多个项目时,“fullTextSession.index(result);”会引发异常:
org.hibernate.TransientObjectException: The instance was not associated with this session
在这种情况下,调试模式会在实体哈希集值上显示类似“无法加载 [...]”的消息。
如果 HashSet 为空(不为空,只有空),则不抛出异常。
我的索引方法:
private void indexDocumentsByEntityIds(List<Long> ids)
final int BATCH_SIZE = 128;
Session session = entityManager.unwrap(Session.class);
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.setFlushMode(FlushMode.MANUAL);
fullTextSession.setCacheMode(CacheMode.IGNORE);
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<MyEntity> criteria = builder.createQuery(MyEntity.class);
Root<MyEntity> root = criteria.from(MyEntity.class);
criteria.select(root).where(root.get("id").in(ids));
TypedQuery<MyEntity> query = fullTextSession.createQuery(criteria);
List<MyEntity> results = query.getResultList();
int index = 0;
for (MyEntity result : results)
index++;
try
fullTextSession.index(result); //index each element
if (index % BATCH_SIZE == 0 || index == ids.size())
fullTextSession.flushToIndexes(); //apply changes to indexes
fullTextSession.clear(); //free memory since the queue is processed
catch (TransientObjectException toEx)
LOGGER.info(toEx.getMessage());
throw toEx;
有人有想法吗?
谢谢!
【问题讨论】:
【参考方案1】:这可能是由循环中的“清除”调用引起的。
本质上,你正在做的是:
加载所有实体以重新索引到会话中 索引一批实体 从会话中删除所有实体 (fullTextSession.clear()
)
尝试索引下一批实体,即使它们不再在会话中...?
您需要做的是仅在会话清除后加载每批实体,以便在您为它们编制索引时确定它们仍在会话中。
在文档中有一个如何使用滚动和适当批量大小的示例:https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#search-batchindex-flushtoindexes
或者,您可以将 ID 列表拆分为包含 128 个元素的较小列表,然后针对每个列表运行查询以获取相应的实体,重新索引所有这 128 个实体,然后刷新和清除。
【讨论】:
【参考方案2】:感谢@yrodiere 的解释,他们帮了我很多!
我选择了您的替代解决方案:
或者,您可以将 ID 列表拆分为包含 128 个元素的较小列表,然后针对每个列表运行查询以获取相应的实体,重新索引所有这 128 个实体,然后刷新和清除。
...一切正常!
很好看!
见下面的代码解决方案:
private List<List<Object>> splitList(List<Object> list, int subListSize)
List<List<Object>> splittedList = new ArrayList<>();
if (!CollectionUtils.isEmpty(list))
int i = 0;
int nbItems = list.size();
while (i < nbItems)
int maxLastSubListIndex = i + subListSize;
int lastSubListIndex = (maxLastSubListIndex > nbItems) ? nbItems : maxLastSubListIndex;
List<Object> subList = list.subList(i, lastSubListIndex);
splittedList.add(subList);
i = lastSubListIndex;
return splittedList;
private void indexDocumentsByEntityIds(Class<Object> clazz, String entityIdPropertyName, List<Object> ids)
Session session = entityManager.unwrap(Session.class);
List<List<Object>> splittedIdsLists = splitList(ids, 128);
for (List<Object> splittedIds : splittedIdsLists)
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.setFlushMode(FlushMode.MANUAL);
fullTextSession.setCacheMode(CacheMode.IGNORE);
Transaction transaction = fullTextSession.beginTransaction();
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Object> criteria = builder.createQuery(clazz);
Root<Object> root = criteria.from(clazz);
criteria.select(root).where(root.get(entityIdPropertyName).in(splittedIds));
TypedQuery<Object> query = fullTextSession.createQuery(criteria);
List<Object> results = query.getResultList();
int index = 0;
for (Object result : results)
index++;
try
fullTextSession.index(result); //index each element
if (index == splittedIds.size())
fullTextSession.flushToIndexes(); //apply changes to indexes
fullTextSession.clear(); //free memory since the queue is processed
catch (TransientObjectException toEx)
LOGGER.info(toEx.getMessage());
throw toEx;
transaction.commit();
【讨论】:
以上是关于Hibernate Search 手动索引抛出“org.hibernate.TransientObjectException:实例未与此会话关联”的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate Search + Infinispan + S3 -- 防止字母数字文件名