【问题标题】:Join a many to many relation with QueryDSL and JPASQLQuery使用 QueryDSL 和 JPASQLQuery 加入多对多关系
【发布时间】:2021-07-18 08:34:27
【问题描述】:

我有以下实体:

@AllArgsConstructor
@EqualsAndHashCode(of = {"name"})
@Data
@NoArgsConstructor
@Entity
@Table(schema = "eat")
public class Pizza {

   @Id
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="pizza_id_seq")
   private Integer id;

   @NotNull       
   private String name;

   @NotNull
   @Positive
   private Double cost;

   @ManyToMany
   @JoinTable(schema = "eat",
              name = "pizza_ingredient",
              inverseJoinColumns = { @JoinColumn(name = "ingredient_id") })
   private Set<Ingredient> ingredients;

}


@AllArgsConstructor
@EqualsAndHashCode(of = {"name"})
@Data
@NoArgsConstructor
@Entity
@Table(schema = "eat")
public class Ingredient {

   @Id
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="ingredient_id_seq")
   private Integer id;

   @NotNull
   @Size(min=1, max=64)
   private String name;

}

我正在使用 QueryDSL (4.2.2) 提供的 JPASQLQuery 对象在 PostgreSQL 中创建一些本机查询:

public JPASQLQuery<T> getJPASQLQuery() {
   return new JPASQLQuery<>(
      entityManager,
      PostgreSQLTemplates.builder().printSchema().build()
   );
}

问题在于尝试使用join函数,例如:

QIngredient ingredient = QIngredient.ingredient;
QPizza pizza = QPizza.pizza;

StringPath ingredientPath = Expressions.stringPath("ingredient");
StringPath pizzaPath = Expressions.stringPath("pizza");
NumberPath<Double> costPath = Expressions.numberPath(Double.class, "cost");
Expression rowNumber = SQLExpressions.rowNumber().over().partitionBy(ingredientPath).orderBy(costPath.desc()).as("rnk");

JPASQLQuery subQuery = getJPASQLQuery()
   .select(ingredient.name.as(ingredientPath), pizza.name.as(pizzaPath), pizza.cost.as(costPath), rowNumber)
   .from(pizza)
   // The error is in next innerJoin
   .innerJoin((SubQueryExpression<?>) pizza.ingredients, ingredient)
   .where(ingredient.name.in(ingredientNames));

如果我保留当前的innerJoin((SubQueryExpression&lt;?&gt;) pizza.ingredients, ingredient) 我会收到:

class com.querydsl.core.types.dsl.SetPath cannot be cast to class com.querydsl.core.types.SubQueryExpression

我无法删除当前的(SubQueryExpression&lt;?&gt;),因为innerJoin 不接受SetPath作为参数。

另一方面,如下:

.from(pizza)               
.innerJoin(ingredient)

由于pizza_ingredient 未包含在生成的查询中,因此不起作用。

如何在JPASQLQuery 中使用innerJoin 与上述多对多关系?

【问题讨论】:

    标签: java jpa querydsl


    【解决方案1】:

    基本上,有两种主要的方法试图解决它:


    包含所需的原生函数

    正如一位 QueryDSL 开发人员 here 所建议的那样,将 JPASQLQuery 替换为 JPA 替代品。


    为多对多表创建所需的Path

    首先将name 属性添加到每个@Table 注释中很重要,因为内部是QueryDSL NativeSQLSerializer 类用来生成fromjoin 子句的属性。

    所以,例如:

    @Table(schema = "eat")
    public class Pizza ...
    

    应替换为:

    @Table(name = "pizza", schema = "eat")
    public class Pizza ...
    

    接下来,为多对多表创建自定义Path

    RelationalPathBase<Object> pizzaIngredient = new RelationalPathBase<>(Object.class, "pi", "eat", "pizza_ingredient");
    NumberPath<Integer> pizzaIngredient_PizzaId = Expressions.numberPath(Integer.class, pizzaIngredient, "pizza_id");
    NumberPath<Integer> pizzaIngredient_IngredientId = Expressions.numberPath(Integer.class, pizzaIngredient, "ingredient_id");
    

    所以完整的代码是:

    QIngredient ingredient = QIngredient.ingredient;
    QPizza pizza = QPizza.pizza;
    
    RelationalPathBase<Object> pizzaIngredient = new RelationalPathBase<>(Object.class, "pi", "eat", "pizza_ingredient");
    NumberPath<Integer> pizzaIngredient_PizzaId = Expressions.numberPath(Integer.class, pizzaIngredient, "pizza_id");
    NumberPath<Integer> pizzaIngredient_IngredientId = Expressions.numberPath(Integer.class, pizzaIngredient, "ingredient_id");
    
    StringPath ingredientPath = Expressions.stringPath("ingredient");
    StringPath pizzaPath = Expressions.stringPath( "pizza");
    NumberPath<Double> costPath = Expressions.numberPath(Double.class, "cost");
    
    Expression rowNumber = SQLExpressions.rowNumber().over().partitionBy(ingredientPath).orderBy(costPath.desc()).as("rnk");
    NumberPath<Long> rnk = Expressions.numberPath(Long.class, "rnk");
    
    SubQueryExpression subQuery = getJPASQLQuery()
       .select(ingredient.name.as(ingredientPath), pizza.name.as(pizzaPath), pizza.cost.as(costPath), rowNumber)
       .from(pizza)
       .innerJoin(pizzaIngredient).on(pizzaIngredient_PizzaId.eq(pizza.id))
       .innerJoin(ingredient).on(ingredient.id.eq(pizzaIngredient_IngredientId))
       .where(ingredient.name.in(ingredientNames));
    
    return getJPASQLQuery()
              .select(ingredientPath, pizzaPath, costPath)
              .from(
                  subQuery,
                  Expressions.stringPath("temp")
              )
              .where(rnk.eq(1l))
              .fetch();
    

    【讨论】:

      猜你喜欢
      • 2021-10-18
      • 1970-01-01
      • 2016-08-03
      • 2013-06-17
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-18
      相关资源
      最近更新 更多