java—使用querydsl和jpasqlquery连接多对多关系

idv4meu8  于 2021-07-12  发布在  Java
关注(0)|答案(1)|浏览(490)

我有以下实体:

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

}

我在用 JPASQLQuery querydsl(4.2.2)提供的用于在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<?>) pizza.ingredients, ingredient) 我收到:

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

我不能移除当前 (SubQueryExpression<?>) 因为 innerJoin 不接受 SetPath 作为参数。
另一方面,以下是:

.from(pizza)               
.innerJoin(ingredient)

由于以下原因无法工作 pizza_ingredient 不包括在生成的查询中。
我怎么用 innerJoinJPASQLQuery 像这样的多对多关系?

3pmvbmvn

3pmvbmvn1#

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

包括必需的本机函数

正如一位querydsl开发人员所建议的,替换 JPASQLQuery jpa的替代品。

为多对多表创建所需路径

首先要补充一点 name 每个 @Table 注解,因为内部是querydsl使用的注解 NativeSQLSerializer 要生成的类 from 以及 join 条款。
例如:

@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();

相关问题