当前位置: 首页 > 知识库问答 >
问题:

Spring中的动态查询和JpaSpecificationExecutor

宋明亮
2023-03-14

我正在尝试创建一个简单的Spring项目,餐厅可以在共享数据库中添加菜单项,用户可以使用html表单根据一系列标准搜索菜肴,尤其是饮食要求

表格示例:

Restaurant Name: Chez Hans
Gluten Free: (X)
Egg Free: (X)
Vegan: ()

示例SQL命令

Select all FROM "dishes" Dish WHERE restaurant_name = "Chez Hans" AND egg_free = TRUE AND 
gluten_Free = TRUE;

然后,符合他们标准的菜肴列表将返回给用户。

表单中的任何字段都可以留空,不勾选框(例如,“素食主义者”并不意味着条件应设置为“假”,而是不包括在查询中)。因此,处理该问题的最佳方法似乎是使用JpaSpecificationExecutor创建动态SQL查询(类似于下面问题答案中的实现)

使用spring数据jpa和spring mvc筛选数据库行

基于我的研究和先前的知识,我创建了一个解决方案。然而,当我实现我的解决方案时,不会返回任何菜肴——即使数据库中有符合搜索条件的菜肴。没有产生任何错误,只是一张空白表,所以我不确定哪里出了问题。

我已经研究了无数关于JpaSpeficationExecators/动态查询的文章/视频,但是这个理论的某些部分我仍然不确定。这就是我收集的关于JpaSpeficationExecators/动态查询的信息(但是我可能错了)

>

  • 元模型不需要创建动态查询而是验证数据库查询语句的正确性

    要使用元模型类创建查询,需要包装类(在我的例子中-DishSearch)

    下面的几行是将元模型Singular属性类强制转换回原始类值。

    路径dname=root.get(Dish_. dname); Path vegan=root.get(Dish_. vegan);

    我对Spring还很陌生,仍然觉得它很难。任何帮助或建议都将不胜感激!

    请看下面我的课程:

    package com.bron.demoJPA.specification;
        
    public class DishSpecification implements Specification<Dish>  {
        
        private final DishSearch criteria;
        
        public DishSpecification(DishSearch ds) {
            criteria =ds;
        }
    
        @Override
        public Predicate toPredicate(Root<Dish> root, CriteriaQuery<?> query,
                CriteriaBuilder cb) {
      
            Path<String> dname = root.get(Dish_.dname);
            Path<Boolean> vegan= root.get(Dish_.vegan);
            Path<Boolean> eggFree= root.get(Dish_.eggFree);
            Path<Boolean> glutenFree= root.get(Dish_.glutenFree);
       
            final List<Predicate> predicates = new ArrayList<Predicate>();
            
            if(criteria.getDname()!=null) {
                predicates.add(cb.equal(dname, criteria.getDname()));
            }
            
            if(criteria.isVegan()!=false) {
                predicates.add(cb.equal(vegan, criteria.isVegan()));
            }
            
            if(criteria.isEggFree()!=false) {
                predicates.add(cb.equal(eggFree, criteria.isEggFree()));
            }
            
            if(criteria.isGlutenFree()!=false) {
                predicates.add(cb.equal(glutenFree, criteria.isGlutenFree()));
            }
        
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
    }
    }
    

    请看我的搜索课:

    package com.bron.demoJPA.specification;
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    public class DishSearch {
    
        private Long dishId;
        private String dname;
        private String description;
        private double price;
        private boolean vegan;
        private boolean glutenFree;
        private boolean eggFree;
        private AppUser app;
    
    }
    

    请查看我的SearchController类:

    @Controller
    public class SearchController {
        
        @Autowired
        DishRepository drep;
            
        @GetMapping("/showSearchForm")
        public String showNewDishForm(Model model) {
            // Create model attribute to bind form data
            DishSearch dishSearch = new DishSearch();
            model.addAttribute("dishSearch", dishSearch);
            return "search_Dish";
        }
    
        @PostMapping("/showDishList")
        public String saveUser(@ModelAttribute("dishSearch")DishSearch dishSearch) {
              Specification<Dish> spec = new DishSpecification(dishSearch);
                drep.findAll(spec); 
                return "show_dish_List";
                }
    }
    

    请看我的课程:

    @Repository
    public interface DishRepository extends JpaRepository<Dish, Long>, JpaSpecificationExecutor<Dish>{
          
        @Transactional
        @Modifying
        List<Dish> findAll(Specification<Dish> spec);
    
      
    }
    

    请看我的搜索盘。html模板:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="ISO-8859-1">
    <title>Dish Management System</title>
    
    <link rel="stylesheet"
        href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    
        <meta name="viewport" content="width=device-width, initial-scale=1">
        
    </head>
    <body>
    
    <br>
    
    <div class="col-sm-10 offset-sm-1 text-center">
        <div class="container"> 
    
            <h2> Manage Dishes </h2>
            <hr>
            </div>
        
            <form action="#" th:action="@{/showDishList}" th:object="${dishSearch}" method="POST">
            <div class="col-sm-10 offset-sm-1 text-center">
                  <input type="text" th:field="*{dname}"
                    placeholder="Dish Name" class="form-control mb-4 col-10">
    
            
                    </div>
                    
                    
                    
    
        <div class="form-check form-check-inline">
      
       <label class=" form-check-label" for="inlineCheckbox1 ">Vegan?</label>
       <input type="checkbox" th:field="*{vegan}" />
      
       <label class="form-check-label" for="inlineCheckbox1">Gluten Free?</label>
       <input type="checkbox" th:field="*{glutenFree}" />
       
       <label class="form-check-label" for="inlineCheckbox1">Egg Free?</label>
       <input type="checkbox" th:field="*{EggFree}" />
       </div>
       <br>
       <br>
       
       
                    <br>
                    <br>
                    <button type="submit" class="btn btn-info col-4"> Search Database</button>
                    
            </form>
            </div>
            <hr>
        
    
    </body>
    </html>
    

    请看我的节目单。html模板:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
    <head>
    
    <title>Search Results</title>
    <link rel="stylesheet"
        href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    
    </head>
    <body>
        
    <br>
    <div class="col-sm-10 offset-sm-1 text-center">
    <h1>Dish List</h1>
    </div>
    
    <table border="1" class="table table-striped table-responsive-md">
        <thead>
            <tr>
                <th>Dish Name</th>
                <th>Dish description</th>
                <th>Dish Price</th>
                <th>Restaurant</th>
            </tr>
    
    
        </thead>
        <tbody>
            <tr th:each="dishSearch : ${listDishSearch}">
                <td th:text="${dishSearch.dname}"></td>
                <td th:text="${dishSearch.description}"></td>
                <td th:text="${dishSearch.price}"></td>
            </tr>
                        
        </tbody>
        
    </table>
    <div class="col-sm-10 offset-sm-1 text-center">
     <a th:href="@{/showNewDishForm}"
        class="btn btn-primary btn-sm mb-3"> Search Again</a>
        </div>
    

    ----------------------------更新------------------------------

    除了下面提供的答案外,在碟子规格类别I中更改了

    if(criteria.getDname()!=null) {
                predicates.add(cb.equal(dname, criteria.getDname()));
            }
    

      if(criteria.getDname()!="") {
                predicates.add(cb.equal(dname, criteria.getDname()));
            }
        
    

    搜索工作现在很顺利!

  • 共有1个答案

    壤驷俊逸
    2023-03-14

    我认为问题是您没有在用于呈现页面的show_dish_L中添加结果,因此UI中没有填充任何内容。您的用户界面期望数据在listDishSearch中,并且该变量中没有任何内容。

    更新您的代码到:

    @PostMapping("/showDishList")
    public String saveUser(@ModelAttribute("dishSearch") DishSearch dishSearch, Model model) {
        Specification<Dish> spec = new DishSpecification(dishSearch);
    
        model.addAttribute("listDishSearch", drep.findAll(spec));
    
        return "show_dish_List";
    }
    

    一切都应该很好。

    dish存储库中删除方法findAll。它已经由接口JpaSpecificationExecutor提供。

     类似资料:
    • 问题内容: 我正在寻找一种使用Spring Data JPA动态构建查询的解决方案。我有一个GameController,它有一个RESTful服务终结点/ games,它带有4个可选参数:体裁,平台,年份,标题。可能不传递任何API,而是传递所有4种,以及之间的每种组合。如果未传递任何参数,则默认为null。我需要在存储库中使用一种方法来构建适当的查询,并且理想情况下还允许Spring Data

    • 我的webapp中有一个过滤器,允许按车辆类型、品牌、燃油、州和城市进行搜索,但所有这些过滤器都是可选的。 如何使用存储库实现这一点。 控制器类 服务类 我还没有实现任何东西,因为我不知道如何实现这个过滤器。 车辆等级 车型和品牌是另一张桌子上的...我是葡萄牙人我把密码翻译成了英文... 当它发生时,我需要做什么?

    • 问题内容: 我正在与后端的Mongodb一起开发nodejs / express应用程序。在我的一个API调用中,根据是否存在特定的querystring参数,我想使用$ gt或$ lt向Mongodb发出查询。 在某些情况下,我们想要的所有内容都比使用$ lt少,但在其他情况下,我们希望所有的内容都比使用$ lt大。我们如何做到这一点而不重复查询? 这是一个示例查询: 有没有一种方法可以动态创建

    • 我们希望按属性名对查询结果进行排序。此属性名称可能不同。根据这一需求,哪一个是用spring SDN来完成它的最佳解决方案? 我找到的唯一解决方案是使用OGM并动态创建查询。 有什么建议吗?也许是Spring SDN的增强?

    • Spring官方JPA规范文档 我对非常陌生,所以任何指导都很有价值。