我在Spring Boot 2应用程序上使用Hibernate Search 5.11,可以进行全文研究。 此库要求将文档编入索引。
启动我的应用程序时,我尝试每五分钟手动重新索引一个索引实体(MyEntity.class)的数据(由于特定原因,由于我的服务器环境)。
我尝试为MyEntity.class的数据建立索引。
MyEntity.class具有一个属性AttachedFiles,它是一个哈希表,填充有一个@OnToMany()连接,并启用了延迟加载模式:
@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为空(不为null,仅为空),则不会引发异常。
我的索引方法:
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;
}
}
}
有人有主意吗?
谢谢!
答案 0 :(得分:1)
这可能是由于您在循环中进行的“清除”调用所致。
本质上,您正在做的是:
fullTextSession.clear()
)中删除所有实体您需要做的是仅在会话清除后加载每一批实体,以确保在为它们建立索引时它们仍在会话中。
在文档中有一个使用滚动和适当的批处理大小的示例:https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#search-batchindex-flushtoindexes
或者,您可以将ID列表分成128个元素的较小列表,然后对每个列表运行查询以获取相应的实体,为所有这128个实体重新索引,然后刷新并清除。
答案 1 :(得分:0)
感谢@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();
}
}