[英]Dynamic query with @Query in Spring Data JPA?
I am using Specifications in my Spring Boot app and can filter result by different filter options.我在我的 Spring 引导应用程序中使用规范,可以通过不同的过滤器选项过滤结果。 However, I need to use special filter with @Query
in my repository method and as far as I see, I cannot build a dynamic WHERE clause in this query.但是,我需要在我的存储库方法中使用带有@Query
的特殊过滤器,据我所知,我无法在此查询中构建动态 WHERE 子句。
There are also QueryDSL and CriteriaAPI options, but I cannot find an example for using them in @Query
.还有 QueryDSL 和 CriteriaAPI 选项,但我找不到在@Query
中使用它们的示例。
So, is it possible to dynamically build WHERE clause or create filter for the query in @Query
?那么,是否可以动态构建 WHERE 子句或为@Query
中的查询创建过滤器? Here is my method:这是我的方法:
// there are more filters that omitted for brevity
@Query("SELECT r FROM Recipe r WHERE r.title LIKE %:text%")
Page<Recipe> findByFields(@Param("text") String text);
I tried to use my specification in this method, but it is not possible to use them with @Query
:((我尝试在此方法中使用我的规范,但无法将它们与@Query
一起使用 :((
Update:更新:
@Entity
public class Recipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@Enumerated(value = EnumType.STRING)
private HealthLabel healthLabel;
// code omitted for brevity
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RecipeIngredient> recipeIngredients = new ArrayList<>();
}
@Entity
public class RecipeIngredient {
@EmbeddedId
private RecipeIngredientId recipeIngredientId = new RecipeIngredientId();
@Column(nullable = false)
private BigDecimal amount;
@ManyToOne(optional = true, fetch = FetchType.LAZY)
@MapsId("recipeId")
@JoinColumn(name = "recipe_id", referencedColumnName = "id")
private Recipe recipe;
@ManyToOne(optional = true, fetch = FetchType.LAZY)
@MapsId("ingredientId")
@JoinColumn(name = "ingredient_id", referencedColumnName = "id")
private Ingredient ingredient;
}
@Entity
public class Ingredient {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false, length = 50)
private String name;
@OneToMany(mappedBy = "ingredient", cascade = CascadeType.ALL)
private Set<RecipeIngredient> recipeIngredients = new HashSet<>();
}
Here is also my enum that I cannot filter by:这也是我无法过滤的枚举:
@Getter
@AllArgsConstructor
public enum HealthLabel {
DEFAULT("Default"),
EGG_FREE("Egg-free"),
VEGETARIAN("Vegetarian"),
WHEAT_FREE("Wheat-free");
private String label;
}
@Query
can only do static queries. @Query
只能进行 static 次查询。
If you want something more dynamic you have to use another feature, for example Specifications
or even fall back to custom method implementations .如果你想要更动态的东西,你必须使用另一个特性,例如Specifications
或者甚至回退到自定义方法实现。
You can try like this:你可以这样尝试:
@Query("SELECT r FROM Recipe r WHERE r.title LIKE %?1%")
Page<Recipe> findByFields(String text);
There is nothing preventing you from using a Specification
(which is basically the Criteria
API but easier to use).没有什么可以阻止您使用Specification
(基本上是Criteria
API 但更易于使用)。 You basically want the dynamic version of the following JPQL.您基本上需要以下 JPQL 的动态版本。
SELECT r FROM Recipe r
JOIN r.recipeIngredients ri
JOIN ri.ingredient i
WHERE i.name LIKE :name
Now if you can write it in JPQL you can also use the Criteria
API to write it (generally speaking).现在,如果你可以用 JPQL 编写它,你也可以使用Criteria
API 来编写它(一般来说)。
public static Specification<Recipe> findByIngredientName(String name) {
return (root, query, criteriaBuilder) -> {
Join<Recipe, RecipeIngredient> ingredients = root.join("ingredients");
Join< RecipeIngredient, Ingredient> ingredient = ingredients.join("ingredient");
return criteriaBuilder.like(ingredient.get("name"), "%" + name + "%");
};
}
That is the specification to retrieve a Recipe
containing an Ingredient
with a name
like
something.这是用于检索包含name
like
某物的Ingredient
的Recipe
的规范。 You need 2 joins no query just a join.您需要 2 个连接,无需查询,只需一个连接。
You can now even combine multiple predicates by using Predicate.and
.您现在甚至可以使用Predicate.and
组合多个谓词。 So if you create another Predicate
for the Recipe.name
you can just chain them together, JPA will handle the rest.因此,如果您为Recipe.name
创建另一个Predicate
,您可以将它们链接在一起,JPA 将处理 rest。
public static Specification<Recipe> findName(String name) {
return (root, query, criteriaBuilder) -> {
return criteriaBuilder.like(root.get("title"), "%" + name + "%");
};
}
Specification<Recipe> specs = findByName("recipe").and(findByIngredientName("ingredient"));
recipeRepository.findAll(specs);
That is all you need.这就是你所需要的。 What and how you receive those parameters that is up to you how you build the request/response objects and is highly subjective.您接收什么以及如何接收这些参数取决于您如何构建请求/响应对象并且是高度主观的。 If you want a sort order use the findAll
which takes a Specification
and a Sort
如果您想要排序顺序,请使用findAll
,它需要一个Specification
和一个Sort
Specification<Recipe> specs = findByName("recipe").and(findByIngredientName("ingredient"));
recipeRepository.findAll(specs, Sort.by("title"));
The above will generate a SQL in the form of以上将生成一个 SQL 的形式
SELECT recipesapp0_.id as id1_1_, recipesapp0_.title as title2_1_ FROM recipes_application$recipe recipesapp0_
INNER JOIN recipes_application$recipe_ingredient recipeingr1_ ON recipesapp0_.id=recipeingr1_.recipe_id
INNER JOIN recipes_application$ingredient recipesapp2_ ON recipeingr1_.ingredient_id=recipesapp2_.id
WHERE (recipesapp0_.title like ?) AND (recipesapp2_.name like ?) ORDER BYrecipesapp0_.title ASC
NOTE: Next time when someone asks for clarification of your use-case please give it instead of being unfriendly to them!注意:下次当有人要求澄清你的用例时,请给出而不是对他们不友好!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.