简体   繁体   English

在EAV模型上进行复杂选择Spring数据jpa

[英]Complex select on EAV model Spring data jpa

I have a Product Entity like below (It's simple version) 我有一个如下的产品实体(简单版本)

@Entity
@Table(name = "product")
public class Product {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private long id;

   @OneToMany(mappedBy = "product")
   private List<ProductAtt> attributes;
}

Each Product could have one or more Attribute. 每个产品可以具有一个或多个属性。 Attribute look likes below 属性如下所示

@Entity
@Table(name = "attribute")
public class Attribute {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private long id;

   private String name;
}

So I create a relation entity like below with extra value property 因此,我使用附加值属性创建了如下所示的关系实体

@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;
}

Now I want to find all products that have some attributes with custom values. 现在,我想查找所有具有某些具有自定义值属性的产品。 For example all products that have attribute 1 with value 3 and attribute 2 with value 40 and ... . 例如,所有属性1的值为3且属性2的值为40和...的所有产品。

What is the simplest and most efficient query to do that? 什么是最简单,最有效的查询?

Since the number of attributes to query is not known at design time, one of the dynamic query mechanisms supported by Spring Data JPA will have to be used. 由于要查询的属性数量在设计时未知,因此必须使用Spring Data JPA支持的动态查询机制之一。 The query can certainly be built using the JPA Specification or Criteria APIs. 当然,可以使用JPA规范或条件API构建查询。

If using QueryDSL support, subqueries with exists can be used. 如果使用QueryDSL支持,则可以使用exists子查询。 The following example shows how this can be done (assuming Java 8 and QueryDSL 4). 以下示例显示了如何完成此操作(假设使用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);
  }
}

It should be noted that the database design is such that performance problems are bound to happen, because the same table ( ProductAttr ) is included as many times as there are attributes to search by. 应该注意的是,数据库设计必然会发生性能问题,因为包含相同表( ProductAttr )的次数与要搜索的属性一样多。 This is not a problem of QueryDSL, JPA, Hibernate, SQL or the database server but the data model itself (also known as the EAV model). 这不是QueryDSL,JPA,Hibernate,SQL或数据库服务器的问题,而是数据模型本身(也称为EAV模型)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM