• 首先是建一个实体类,打上注解,运行后自动在指定数据库中创建数据表。
  • 配置文件换用.yml,这种格式层次分明,不易冗余;连接上数据库。
  • 写一个dao接口继承一下JpaRepository,自定义方法可以自己添加。即可操作数据库。

    自定义@Query查询

  • 在dao中写自定义查询方法

    /*
    自定义方法实现模糊查询
    */
    @Query("select b from Book b where b.name like %?1%") //使用hql(操作对象),Book表示类型,?1传入第一个参数,前后都有%表示前后都可以是其他文字
    public List findByName(String name);
    
    /*
    原生sql
    */
    @Query(value = "select * from book b where b.name=?1", nativeQuery = true)
    List findByName(String name);
    
    /*
    自定义任意查询几条数据出来
    */
    @Query(value="select * from t_book order by RAND() limit ?1",nativeQuery = true) //默认是false,这里使用本地sql应开启
    public List randomList(Integer n);
    
    @Query(value = "select name,author,price from Book b where b.price>?1 and b.price findByPriceRange(long price1, long price2);
    
    @Query(value = "select name,author,price from Book b where b.name like %:name%")
    List findByNameMatch(@Param("name") String name);
    
    @Query(value = "select * from book b where b.name=?1", nativeQuery = true)
    List findByName(String name);
    
    /*
    使用@Param注解注入参数
    */
    @Query("select name,author,price from Book b where b.name=:name and b.author=:author and b.price=:price")
    List findByNamedParam(@Param("name")String name,@Param("author")String author,@Param("price")String price);
    
  • 对于like语句,参数为null时查不到数据;而动态查询则能获取全部数据

  • 建议使用sql语句

    定义Predicate动态查询、条件拼接

  • 以下是扩充findAll进行动态查询。主要是考虑搜索条件可能并不俱全,有时甚至是空搜,空搜的话会把所有记录都查出来

    public ModelAndView list2(Book book){ //自动封装
    ModelAndView mav=new ModelAndView();
    List bookList=bookDao.findAll(new Specification(){
    
        /*
        拼接条件
         */
        @Override
        public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cd) { //cb是条件构造器;query是一种高级查询;root可以获取数据库字段
            Predicate predicate=cd.conjunction(); //conjunction:结合
            //条件全为null的话查询全部数据
            if(book!=null){
                if(book.getName()!=null&&!"".equals(book.getName())){ //如果用户输入了此查询条件
                    predicate.getExpressions().add(cd.like(root.get("name"),"%"+book.getName()+"%")); //cb.like(root.get("name"):获取对应字段,按like规则查询,类似的还有equal等;后面为具体查询规则
                }
                if(book.getAuthor()!=null&&!"".equals(book.getAuthor())){
                    predicate.getExpressions().add(cd.like(root.get("author"),"%"+book.getAuthor()+"%"));
                }
            }
            return predicate; //返回拼接好的查询条件
        }
    
    });
    
    mav.addObject("bookList",bookList);
    mav.setViewName("bookList");
    return mav;
    

    }

  • 除了like查询语句还有其他:

    //大于等于,可用于时间
    predicate.getExpressions().add(cd.greaterThanOrEqualTo(root.get("createDate"), document.getFirstDate()));
    //小于等于
    predicate.getExpressions().add(cd.lessThanOrEqualTo(root.get("createDate"), document.getLastDate()));
    

    拼接+分页

  • 除了拼接条件,一般还会伴随着分页。我们可以定义一个方法同时实现条件拼接和分页

    //dao层
    public interface DocumentRepository extends JpaRepository{
    
        Page findAll(Specification specification, Pageable pageable);
    }
    
    //service层定义接口
    public interface DocumentService {
    
        public Page findAll(UsefulDocument document, int pageNum, int pageSize);
    
    }
    
    //service实现层
    @Override
    public Page findAll(UsefulDocument document, int pageNum, int pageSize) { //传入页码和每页容量
    
        List orders = new ArrayList(); //排序规则
        //搜索条件包含时间则按时间排
        if (document.getFirstDate() != null) { 
            orders.add(new Sort.Order(Sort.Direction.DESC, "createDate"));
        } else if (document.getLastDate() != null) {
            orders.add(new Sort.Order(Sort.Direction.DESC, "createDate"));
        } else { //否则按id排
            orders.add(new Sort.Order(Sort.Direction.ASC, "id"));
        }
        //封装排序规则
        Sort sort = new Sort(orders); 
        //创建分页对象,传入页码、容量和排序规则三个条件
        Pageable pageable=new PageRequest(pageNum,pageSize,sort);
    
        //调用findAll方法开始动态拼接(重写拼接方法并传入分页对象)
        Page documentList = documentRepository.findAll(new Specification() { @Override …… }, pageable);
    
        //这样,根据传入的pageNum不同即可返回不同数据
        return documentList;
    }
    
  • 其中的Sort与Order是这么用的:Sort是排序方法,Order则是定义排序规则。如果只有一种排序规则那可以这样写

    Sort sort=new Sort(Sort.Direction.ASC,"age"); //第二个参数是实体类的属性(记住不是数据库的字段)
    //底层是把字段全部转换为String进行比较
    
  • 但如果我想要先按age降序,再按grade升序,再按dnum降序怎么办?
  • 这个时候就要用到org.springframework.data.domain.Sort.Order这个包下面的Order类了
  • 根据排序规则我们分别建立三个Order如下:

    //设置规则
    Sort.Order order1=new Sort.Order(Sort.Direction.DESC,"age");
    Sort.Order order2=new Sort.Order(Sort.Direction.ASC,"grade");
    Sort.Order order3=new Sort.Order(Sort.Direction.DESC,"dnum");
    //放入集合
    List list=new ArrayList<>();
    list.add(order1);
    list.add(order2);
    list.add(order3);
    //放到Sort里
    Sort sort=new Sort(list);
    

    排序

    Iterable findAll(Sort sort);
    
  • 分页获得的对象集合其实就跟普通集合一样,可以用任何遍历方法来遍历
  • documentList.getTotalElements() //全部查询结果总行数,Long型,测试时应该这么判断:is(4L) //后面加个L
  • documentList.getTotalPages() //查询总页数
  • Document document = documentList.getContent() //获取结果对象
  • documents.getNumberOfElements() //本页结果条数
  • documents.getNumber() //同上
  • documents.getSize() //每页容量
  • documents.getSort() //排序方法:字段 升/降

    代码实例

  • D:/SpringBootJpa/BookDao、BookController、entity/Book(增删查改图书)
  • D:/civalWeb/LLCWEB/DocumentRepository、DocumentService、DocumentServiceImpl、DocumentRepositoryTest