Hibernate Search 不返回新创建实体的结果
Hibernate Search not returning results on newly created entities
我在多租户 Hibernate 应用程序中使用 Hibernate Search。根据 this link,我希望它可以开箱即用,但搜索结果似乎不是 return 新创建的实体。
这是我的映射:
@Entity
@Indexed
public class ItemEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field
private String name;
@Field
private String description;
@OneToMany(mappedBy = "item", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy("versionNo DESC, lastSaveDate DESC")
@IndexedEmbedded
private List<ItemVersionEntity> versions;
...
}
@Entity
public class ItemVersionEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field
private String description;
@OneToMany(mappedBy = "version", cascade = CascadeType.ALL, orphanRemoval = true)
@Fetch(FetchMode.SUBSELECT)
@IndexedEmbedded
private List<ItemEdgeEntity> edges;
@OneToMany(mappedBy = "version", cascade = CascadeType.ALL, orphanRemoval = true)
@Fetch(FetchMode.SUBSELECT)
@IndexedEmbedded
private List<ItemNodeEntity> nodes;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "item_id")
@ContainedIn
private ItemEntity item;
}
@Entity
public class ItemEdgeEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field
private String text;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "version_id")
@ContainedIn
private ItemVersionEntity version;
}
@Entity
public class ItemNodeEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field
private String text;
@Field
private String description;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "version_id")
@ContainedIn
private ItemVersionEntity version;
}
添加了一个叫foobar
的ItemNodeEntity
后,我正在用this工具查看它出现在索引中(虽然我不太明白索引的结构, 不确定不同的 Segments 是否有问题):
Marple image
Luke image
这是 returns 0 结果的一些查询代码:
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
Session session = sessionFactory.withOptions().tenantIdentifier(tenantIdResolver.resolveCurrentTenantIdentifier()).openSession();
FullTextSession fullTextEM = Search.getFullTextSession(session);
QueryBuilder itemQB = fullTextEM.getSearchFactory().buildQueryBuilder().forEntity(ItemEntity.class).get();
Query mq = itemQB
.keyword()
.onField("versions.nodes.text")
.matching("foobar")
.createQuery();
FullTextQuery ft = fullTextEM.createFullTextQuery(mq, ItemEntity.class);
List<Object> result = ft.getResultList();
请注意,如果我重新索引整个数据库,上述搜索有效:
FullTextSession fullTextEntityManager = Search.getFullTextSession(session);
fullTextEntityManager.createIndexer().startAndWait();
休眠:5.2.13
休眠搜索:5.9.3
编辑:
似乎在保存 ItemVersionEntity
之后,__HSearch_TenantId
字段从 Lucene 文档中删除:
Luke document image
这是实现多租户的方式(基本上遵循 this 指南):
@Configuration
public class HibernateConfig {
@Autowired
private JpaProperties jpaProperties;
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
Map<String, Object> properties = new HashMap<>();
properties.putAll(jpaProperties.getHibernateProperties(new HibernateSettings()));
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(...);
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setJpaPropertyMap(properties);
return em;
}
}
您的映射似乎缺少从 ItemVersionEntity
到 ItemEntity
的关联。您需要此关联以及 @ContainedIn
注释,以便 Hibernate Search 能够检索相关的 ItemEntity
实例并在 ItemVersionEntity
(或嵌入式节点)更改时重新索引它.
基本上,如果您希望自动重建索引正常工作,只要实体 A 在与类型 B 的关联上带有 @IndexedEmbedded
注释,您就需要实体 B 声明该关联的反面为键入 A,并在该反向关联上添加 @ContainedIn
注释。
正如我在其他答案的评论中所讨论的那样,这是一个仅在非常特定的情况下使用多租户时才会出现的错误(显然是 @IndexedEmbedded
的 2 层或更多层):https://hibernate.atlassian.net/browse/HSEARCH-3647
要解决此错误,最好的办法是在合并后显式重新索引:
Search.getFullTextSession(s).index(updatedVersion.getItem());
如果您实际上无法提前知道哪些实体需要重新编制索引,您可以使用可能的 hack...但这确实是 hack:一定要正确测试它。这个想法是让 Hibernate Search 执行一个不会改变任何东西的操作,但是会产生正确初始化一些内部结构的副作用。
开始交易后,只需触发一个您绝对确定不会产生任何影响的清除操作,方法是使用您绝对确定不匹配任何实体的实体 ID:
Search.getFullTextSession(s).purge(ItemEntity.class, -1);
然后该实体类型的内部结构将被正确初始化,@ContainedIn 处理将对事务的其余部分正常工作。
如果您有其他索引类型,则必须对可能受您的交易影响的每种类型执行相同的操作。
请注意,清除操作仍将执行,只是不会有任何效果,因为 ID 不匹配任何内容。
我在多租户 Hibernate 应用程序中使用 Hibernate Search。根据 this link,我希望它可以开箱即用,但搜索结果似乎不是 return 新创建的实体。
这是我的映射:
@Entity
@Indexed
public class ItemEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field
private String name;
@Field
private String description;
@OneToMany(mappedBy = "item", cascade = CascadeType.ALL, orphanRemoval = true)
@OrderBy("versionNo DESC, lastSaveDate DESC")
@IndexedEmbedded
private List<ItemVersionEntity> versions;
...
}
@Entity
public class ItemVersionEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field
private String description;
@OneToMany(mappedBy = "version", cascade = CascadeType.ALL, orphanRemoval = true)
@Fetch(FetchMode.SUBSELECT)
@IndexedEmbedded
private List<ItemEdgeEntity> edges;
@OneToMany(mappedBy = "version", cascade = CascadeType.ALL, orphanRemoval = true)
@Fetch(FetchMode.SUBSELECT)
@IndexedEmbedded
private List<ItemNodeEntity> nodes;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "item_id")
@ContainedIn
private ItemEntity item;
}
@Entity
public class ItemEdgeEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field
private String text;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "version_id")
@ContainedIn
private ItemVersionEntity version;
}
@Entity
public class ItemNodeEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field
private String text;
@Field
private String description;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "version_id")
@ContainedIn
private ItemVersionEntity version;
}
添加了一个叫foobar
的ItemNodeEntity
后,我正在用this工具查看它出现在索引中(虽然我不太明白索引的结构, 不确定不同的 Segments 是否有问题):
Marple image
Luke image
这是 returns 0 结果的一些查询代码:
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
Session session = sessionFactory.withOptions().tenantIdentifier(tenantIdResolver.resolveCurrentTenantIdentifier()).openSession();
FullTextSession fullTextEM = Search.getFullTextSession(session);
QueryBuilder itemQB = fullTextEM.getSearchFactory().buildQueryBuilder().forEntity(ItemEntity.class).get();
Query mq = itemQB
.keyword()
.onField("versions.nodes.text")
.matching("foobar")
.createQuery();
FullTextQuery ft = fullTextEM.createFullTextQuery(mq, ItemEntity.class);
List<Object> result = ft.getResultList();
请注意,如果我重新索引整个数据库,上述搜索有效:
FullTextSession fullTextEntityManager = Search.getFullTextSession(session);
fullTextEntityManager.createIndexer().startAndWait();
休眠:5.2.13 休眠搜索:5.9.3
编辑:
似乎在保存 ItemVersionEntity
之后,__HSearch_TenantId
字段从 Lucene 文档中删除:
Luke document image
这是实现多租户的方式(基本上遵循 this 指南):
@Configuration
public class HibernateConfig {
@Autowired
private JpaProperties jpaProperties;
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
Map<String, Object> properties = new HashMap<>();
properties.putAll(jpaProperties.getHibernateProperties(new HibernateSettings()));
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(...);
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setJpaPropertyMap(properties);
return em;
}
}
您的映射似乎缺少从 ItemVersionEntity
到 ItemEntity
的关联。您需要此关联以及 @ContainedIn
注释,以便 Hibernate Search 能够检索相关的 ItemEntity
实例并在 ItemVersionEntity
(或嵌入式节点)更改时重新索引它.
基本上,如果您希望自动重建索引正常工作,只要实体 A 在与类型 B 的关联上带有 @IndexedEmbedded
注释,您就需要实体 B 声明该关联的反面为键入 A,并在该反向关联上添加 @ContainedIn
注释。
正如我在其他答案的评论中所讨论的那样,这是一个仅在非常特定的情况下使用多租户时才会出现的错误(显然是 @IndexedEmbedded
的 2 层或更多层):https://hibernate.atlassian.net/browse/HSEARCH-3647
要解决此错误,最好的办法是在合并后显式重新索引:
Search.getFullTextSession(s).index(updatedVersion.getItem());
如果您实际上无法提前知道哪些实体需要重新编制索引,您可以使用可能的 hack...但这确实是 hack:一定要正确测试它。这个想法是让 Hibernate Search 执行一个不会改变任何东西的操作,但是会产生正确初始化一些内部结构的副作用。 开始交易后,只需触发一个您绝对确定不会产生任何影响的清除操作,方法是使用您绝对确定不匹配任何实体的实体 ID:
Search.getFullTextSession(s).purge(ItemEntity.class, -1);
然后该实体类型的内部结构将被正确初始化,@ContainedIn 处理将对事务的其余部分正常工作。 如果您有其他索引类型,则必须对可能受您的交易影响的每种类型执行相同的操作。
请注意,清除操作仍将执行,只是不会有任何效果,因为 ID 不匹配任何内容。