如何使用 Hibernate Search 以单向 ManyToMany 和 ManyToOne 关系加入数据
How to join data in unidirectional ManyToMany and ManyToOne relation with Hibernate Search
我是 Hibernate Search 的新手,我正在使用 Hibernate Search 6.1.4.Final
以及 Spring Boot 2.6.7
、Spring Data JPA 2.6.7
和 PostgreSQL 14
。我有以下架构:
CREATE TABLE manufacturer
(
id UUID,
PRIMARY KEY (id)
);
CREATE TABLE flavor
(
id UUID,
PRIMARY KEY (id)
);
CREATE TABLE tobacco
(
id UUID,
name VARCHAR NOT NULL,
manufacturer_id UUID NOT NULL,
PRIMARY KEY (id)
);
ALTER TABLE tobacco
ADD CONSTRAINT fk_manufacturer FOREIGN KEY (manufacturer_id) REFERENCES manufacturer (id);
CREATE TABLE tobacco_flavor
(
tobacco_id UUID REFERENCES tobacco (id) ON UPDATE CASCADE ON DELETE CASCADE,
flavor_id UUID REFERENCES flavor (id) ON UPDATE CASCADE,
CONSTRAINT tobacco_flavor_pk PRIMARY KEY (tobacco_id, flavor_id)
);
我在 Tobacco 和 Manufacturer 之间有一个单向的 ManyToOne 关系,在 Tobacco 和 Flavor 之间有另一个单向的 ManyToMany 关系。索引实体是:
@Data
@Entity
@Indexed
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@EqualsAndHashCode(of = "id")
@EntityListeners(AuditingEntityListener.class)
public class Tobacco {
@Id
@GeneratedValue
private UUID id;
@NotBlank
@FullTextField(analyzer = "name")
@FullTextField(name = "name_prefix", analyzer = "name_prefix", searchAnalyzer = "name")
private String name;
@ManyToOne(fetch = FetchType.EAGER) // this is EAGER because hibernate cannot serialize the proxy wrapper when using LAZY
@JoinColumn(name = "manufacturer_id", nullable = false)
private Manufacturer manufacturer;
@ManyToMany(cascade = {
CascadeType.MERGE
})
@JoinTable(name = "tobacco_flavor",
joinColumns = @JoinColumn(name = "tobacco_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "flavor_id", referencedColumnName = "id")
)
private Set<Flavor> flavors;
}
我正在使用以下 lucene 配置器:
@Component("luceneTobaccoAnalysisConfigurer")
public class LuceneTobaccoAnalysisConfigurer implements LuceneAnalysisConfigurer {
@Override
public void configure(LuceneAnalysisConfigurationContext context) {
context.analyzer("name").custom()
.tokenizer("standard")
.tokenFilter("lowercase")
.tokenFilter("asciiFolding");
context.analyzer("name_prefix").custom()
.tokenizer("standard")
.tokenFilter("lowercase")
.tokenFilter("asciiFolding")
.tokenFilter("edgeNGram")
.param("minGramSize", "2")
.param("maxGramSize", "7");
}
}
我正在执行以下查询:
public List<Tobacco> find(String query) {
return Search.session(entityManager)
.search(Tobacco.class)
.where(f -> f.match()
.fields("barcode", "name").boost(2.0f)
.fields("name_prefix")
.matching(query)
.fuzzy()
)
.fetchHits(10);
}
当我执行查询时,Hibernate 使用连接执行 5 个 SELECT 个查询,而不是 1 个。我想始终按烟草名称查询并检索与其关联的所有实体(口味和制造商),是否有任何方法可以指示 Hibernate Search 通过连接有效地执行查询?
要控制 Hibernate Search 加载的内容,您可以在构建搜索查询时通过调用 .loading( o -> o.graph( someGraph, GraphSemantic.FETCH ) )
来利用 JPA entity graphs in Hibernate Search。
是这样的吗?
public List<Tobacco> find(String query) {
EntityGraph<Tobacco> graph = entityManager.createEntityGraph( Tobacco.class );
graph.addAttributeNodes( "manufacturer" );
graph.addAttributeNodes( "flavors" );
return Search.session(entityManager)
.search(Tobacco.class)
.where(f -> f.match()
.fields("barcode", "name").boost(2.0f)
.fields("name_prefix")
.matching(query)
.fuzzy()
)
.loading( o -> o.graph( graph, GraphSemantic.FETCH ) )
.fetchHits(10);
}
但是,根据您的模型和实体图,这可能会导致执行多个查询(由于 Hibernate ORM 中的实现限制)。在这种情况下,您可以探索这些将影响任何加载的替代解决方案,而不仅仅是在 Hibernate Search 中加载:
- 在您的
flavors
协会中使用 @Fetch(FetchMode.JOIN)
。
- 利用 Hibernate ORM 的批量提取只触发两个 SQL 查询(一个用于烟草实例,一个用于它们的口味):参见
batch_fetch_size
configuration property and the @BatchSize
annotation.
我是 Hibernate Search 的新手,我正在使用 Hibernate Search 6.1.4.Final
以及 Spring Boot 2.6.7
、Spring Data JPA 2.6.7
和 PostgreSQL 14
。我有以下架构:
CREATE TABLE manufacturer
(
id UUID,
PRIMARY KEY (id)
);
CREATE TABLE flavor
(
id UUID,
PRIMARY KEY (id)
);
CREATE TABLE tobacco
(
id UUID,
name VARCHAR NOT NULL,
manufacturer_id UUID NOT NULL,
PRIMARY KEY (id)
);
ALTER TABLE tobacco
ADD CONSTRAINT fk_manufacturer FOREIGN KEY (manufacturer_id) REFERENCES manufacturer (id);
CREATE TABLE tobacco_flavor
(
tobacco_id UUID REFERENCES tobacco (id) ON UPDATE CASCADE ON DELETE CASCADE,
flavor_id UUID REFERENCES flavor (id) ON UPDATE CASCADE,
CONSTRAINT tobacco_flavor_pk PRIMARY KEY (tobacco_id, flavor_id)
);
我在 Tobacco 和 Manufacturer 之间有一个单向的 ManyToOne 关系,在 Tobacco 和 Flavor 之间有另一个单向的 ManyToMany 关系。索引实体是:
@Data
@Entity
@Indexed
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@EqualsAndHashCode(of = "id")
@EntityListeners(AuditingEntityListener.class)
public class Tobacco {
@Id
@GeneratedValue
private UUID id;
@NotBlank
@FullTextField(analyzer = "name")
@FullTextField(name = "name_prefix", analyzer = "name_prefix", searchAnalyzer = "name")
private String name;
@ManyToOne(fetch = FetchType.EAGER) // this is EAGER because hibernate cannot serialize the proxy wrapper when using LAZY
@JoinColumn(name = "manufacturer_id", nullable = false)
private Manufacturer manufacturer;
@ManyToMany(cascade = {
CascadeType.MERGE
})
@JoinTable(name = "tobacco_flavor",
joinColumns = @JoinColumn(name = "tobacco_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "flavor_id", referencedColumnName = "id")
)
private Set<Flavor> flavors;
}
我正在使用以下 lucene 配置器:
@Component("luceneTobaccoAnalysisConfigurer")
public class LuceneTobaccoAnalysisConfigurer implements LuceneAnalysisConfigurer {
@Override
public void configure(LuceneAnalysisConfigurationContext context) {
context.analyzer("name").custom()
.tokenizer("standard")
.tokenFilter("lowercase")
.tokenFilter("asciiFolding");
context.analyzer("name_prefix").custom()
.tokenizer("standard")
.tokenFilter("lowercase")
.tokenFilter("asciiFolding")
.tokenFilter("edgeNGram")
.param("minGramSize", "2")
.param("maxGramSize", "7");
}
}
我正在执行以下查询:
public List<Tobacco> find(String query) {
return Search.session(entityManager)
.search(Tobacco.class)
.where(f -> f.match()
.fields("barcode", "name").boost(2.0f)
.fields("name_prefix")
.matching(query)
.fuzzy()
)
.fetchHits(10);
}
当我执行查询时,Hibernate 使用连接执行 5 个 SELECT 个查询,而不是 1 个。我想始终按烟草名称查询并检索与其关联的所有实体(口味和制造商),是否有任何方法可以指示 Hibernate Search 通过连接有效地执行查询?
要控制 Hibernate Search 加载的内容,您可以在构建搜索查询时通过调用 .loading( o -> o.graph( someGraph, GraphSemantic.FETCH ) )
来利用 JPA entity graphs in Hibernate Search。
是这样的吗?
public List<Tobacco> find(String query) {
EntityGraph<Tobacco> graph = entityManager.createEntityGraph( Tobacco.class );
graph.addAttributeNodes( "manufacturer" );
graph.addAttributeNodes( "flavors" );
return Search.session(entityManager)
.search(Tobacco.class)
.where(f -> f.match()
.fields("barcode", "name").boost(2.0f)
.fields("name_prefix")
.matching(query)
.fuzzy()
)
.loading( o -> o.graph( graph, GraphSemantic.FETCH ) )
.fetchHits(10);
}
但是,根据您的模型和实体图,这可能会导致执行多个查询(由于 Hibernate ORM 中的实现限制)。在这种情况下,您可以探索这些将影响任何加载的替代解决方案,而不仅仅是在 Hibernate Search 中加载:
- 在您的
flavors
协会中使用@Fetch(FetchMode.JOIN)
。 - 利用 Hibernate ORM 的批量提取只触发两个 SQL 查询(一个用于烟草实例,一个用于它们的口味):参见
batch_fetch_size
configuration property and the@BatchSize
annotation.