EAV 模型 Spring 数据 jpa 上的复杂 select
Complex select on EAV model Spring data jpa
我有一个像下面这样的产品实体(这是简单的版本)
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@OneToMany(mappedBy = "product")
private List<ProductAtt> attributes;
}
每个产品可以有一个或多个属性。属性如下所示
@Entity
@Table(name = "attribute")
public class Attribute {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
}
所以我创建了一个如下所示的具有额外值的关系实体属性
@Entity
@Table(name = "product_att")
public class ProductAtt implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne
@JoinColumn
private Product product;
@ManyToOne
@JoinColumn
private Attribute attribute;
private int value;
}
现在我想查找所有具有自定义值属性的产品。例如,属性 1 的值为 3 且属性 2 的值为 40 和...的所有产品。
最简单、最有效的查询是什么?
由于在设计时不知道要查询的属性数量,因此必须使用 Spring Data JPA 支持的动态查询机制之一。当然可以使用 JPA 规范或标准 API 构建查询。
如果使用 QueryDSL 支持,可以使用带有 exists
的子查询。以下示例显示了如何完成此操作(假设 Java 8 和 QueryDSL 4)。
interface ProductRepository
extends CrudRepository<Product, Long>
, QueryDslPredicateExecutor<Product> {
default Iterable<Product> findAllByAttributes(final Map<String, String> attributes) {
final QProduct root = QProduct.product;
BooleanExpression query = root.isNotNull();
for (final String attribute : attributes.keySet()) {
final QProductAttribute branch = root.attributes.any();
final BooleanExpression subquery = branch.attribute.name.equalsIgnoreCase(attribute)
.and(branch.value.equalsIgnoreCase(attributes.get(attribute)));
query = query.and(JPAExpressions.selectFrom(QProductAttribute.productAttribute).where(subquery).exists());
}
return findAll(query);
}
}
应该注意的是,数据库设计必然会出现性能问题,因为相同的 table (ProductAttr
) 被包含的次数与要搜索的属性的次数一样多.这不是 QueryDSL、JPA、Hibernate、SQL 或数据库服务器的问题,而是数据模型本身(也称为 EAV 模型)的问题。
我有一个像下面这样的产品实体(这是简单的版本)
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@OneToMany(mappedBy = "product")
private List<ProductAtt> attributes;
}
每个产品可以有一个或多个属性。属性如下所示
@Entity
@Table(name = "attribute")
public class Attribute {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
}
所以我创建了一个如下所示的具有额外值的关系实体属性
@Entity
@Table(name = "product_att")
public class ProductAtt implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne
@JoinColumn
private Product product;
@ManyToOne
@JoinColumn
private Attribute attribute;
private int value;
}
现在我想查找所有具有自定义值属性的产品。例如,属性 1 的值为 3 且属性 2 的值为 40 和...的所有产品。
最简单、最有效的查询是什么?
由于在设计时不知道要查询的属性数量,因此必须使用 Spring Data JPA 支持的动态查询机制之一。当然可以使用 JPA 规范或标准 API 构建查询。
如果使用 QueryDSL 支持,可以使用带有 exists
的子查询。以下示例显示了如何完成此操作(假设 Java 8 和 QueryDSL 4)。
interface ProductRepository
extends CrudRepository<Product, Long>
, QueryDslPredicateExecutor<Product> {
default Iterable<Product> findAllByAttributes(final Map<String, String> attributes) {
final QProduct root = QProduct.product;
BooleanExpression query = root.isNotNull();
for (final String attribute : attributes.keySet()) {
final QProductAttribute branch = root.attributes.any();
final BooleanExpression subquery = branch.attribute.name.equalsIgnoreCase(attribute)
.and(branch.value.equalsIgnoreCase(attributes.get(attribute)));
query = query.and(JPAExpressions.selectFrom(QProductAttribute.productAttribute).where(subquery).exists());
}
return findAll(query);
}
}
应该注意的是,数据库设计必然会出现性能问题,因为相同的 table (ProductAttr
) 被包含的次数与要搜索的属性的次数一样多.这不是 QueryDSL、JPA、Hibernate、SQL 或数据库服务器的问题,而是数据模型本身(也称为 EAV 模型)的问题。