SpringdataJPA使用@query注解完成条件分页查询原生SQL遇到的问题

1.先说一下前端到后端的流程

基本功能就是页面展示table数据,通过分页的方式

image-20220811141435794

页面写的展示表字段

image-20220811141758420

默认的查询数据,有页数,每页数据量,以及客户id,默认情况就是点击这个页面进来后响应展示数据.

image-20220811141605676

vue页面

<template>
  <div class="content">
    <div class="row">
      <div class="col-md-12">
        <div class="box">
          <el-form :inline="true" :model="listQuery" class="demo-form-inline" label-width="75px">
            <el-form-item label="客户号" prop="customerid">
              <el-input v-model.trim="listQuery.customerid" placeholder="" clearable style="width: 200px;"></el-input>
            </el-form-item>
            <el-form-item>
              <button class="btn btn-default" type="button" @click="listSearch">查询</button>
              <el-upload
                    class="upload-demo"
                    action=""
                    accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
                    :http-request="uploadFile"
                    :show-file-list="false"
                    multiple>
                    <button class="btn btn-import bg-green" type="button">导入</button>
              </el-upload>
            </el-form-item>
          </el-form>


<!--  这个是表信息-->
          <div class="box-body">
            <div class="table-responsive">
              <el-table
                :key="tableKey"
                v-loading="listLoading"
                :data="list"
                border
                fit
                stripe
                highlight-current-row
                style="width: 100%;"
              >
                <el-table-column
                  label="客户号"
                  prop="customerid"
                  align="left"
                  min-width="200">
                </el-table-column>
                <el-table-column
                  label="券码号"
                  prop="couponid"
                  align="left"
                  min-width="240">
                </el-table-column>
<!--                <el-table-column-->
<!--                  label="服务使用状态"-->
<!--                  prop="servicestatus"-->
<!--                  align="left"-->
<!--                  min-width="240">-->
<!--                </el-table-column>-->
<!--                <el-table-column-->
<!--                  label="使用通道"-->
<!--                  prop="serviceChannel"-->
<!--                  align="left"-->
<!--                  min-width="240">-->
<!--                </el-table-column>-->
<!--                <el-table-column-->
<!--                  label="项目供应商"-->
<!--                  prop="supplierName"-->
<!--                  align="left"-->
<!--                  min-width="100">-->
<!--                </el-table-column>-->
<!--                <el-table-column-->
<!--                  label="状态"-->
<!--                  align="left"-->
<!--                  min-width="80">-->
<!--                  <template slot-scope="scope">-->
<!--                    <span>{{scope.row.serviceStatus == 0 ? '正常':'暂停'}}</span>-->
<!--                  </template>-->
<!--                </el-table-column>-->
<!--                <el-table-column label="操作" align="left" class-name="small-padding fixed-width" width="110">-->
<!--                  <template slot-scope="scope">-->
<!--                    <el-button class="btn1" @click="handleEditRow(scope.row)" type="text">修改</el-button>-->
<!--                    <el-button class="btn1" @click="handleDeleteRow(scope.row)" type="text">删除</el-button>-->
<!--                  </template>-->
<!--                </el-table-column>-->

              </el-table>
            </div>
          </div>


          <div class="box-footer clearfix">
            <!-- 控制分页模块 -->
            <pagination align="right" v-show="total>0" :total="total" :page.sync="listQuery.pageNum" :limit.sync="listQuery.pageSize" @pagination="getList" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { getPageService } from '@/api/couponid/couponid'
// import { getSupplier } from '@/api/supplierManagement/supplier'
import { upLoadcouponidDef } from '@/api/common'
import { parseTime } from '@/utils/ruoyi'
import Pagination from '@/components/Pagination'

export default {
  name: 'Projects',
  components: { Pagination },
  data() {
    return {
      tableKey: 0,
      title: '添加项目',
      list: null,
      total: 0,
      listLoading: false,
      listQuery: {
        pageNum: 1,
        pageSize: 10,
        customerid: null


      },
      listRow: {
        customerid:null,
        couponid:null

      }
    }
  },
  created() {
    this.getList()
    // this.list = this.pageInfo.list
    // this.total = this.pageInfo.total
  },
  methods: {
    getList() {
      this.listLoading = true

      getPageService(this.listQuery).then(response => {
        this.listLoading = false
        this.list = response.content
        this.total = response.totalElements
      })
      .catch(() => {
        this.listLoading = false
      })
    },
    reset() {
      this.listQuery = {
        pageNum: 1,
        pageSize: 10,
        customerid: null

      }
    },
    //查询
    listSearch() {
      this.listQuery.pageNum = 1
      this.getList()
    },
    // 导入
    uploadFile(item) {
      const form = new FormData();
      form.append('file', item.file);
      upLoadcouponidDef(form).then(res => {
        if (res.success == 'SUCCESS') {
          this.msgSuccess('导入成功!')
           this.reset()
           this.getList(); //导入成功刷新列表
        }else if (res.success == 'FALSE'){
          this.msgError(res.message)
        }
      }).catch(err => {
        this.msgError(err)
      })
    },

    // 关闭
    closeWriteoOffVisible() {
      this.projectVisible = false
    },



    // 保存
    // saveService(formName){
    //   this.$refs[formName].validate((valid) => {
    //     if (valid) {
    //       if (this.listRow.serviceID) {
    //         updateService(this.listRow).then(res=>{
    //           this.msgSuccess('修改成功')
    //           this.projectVisible = false
    //           this.reset()
    //           this.getList()
    //         })
    //       }else{
    //         saveService(this.listRow).then(res=>{
    //           this.msgSuccess('添加成功')
    //           this.projectVisible = false
    //           this.reset()
    //           this.getList()
    //         })
    //       }
    //     } else {
    //       console.log('error submit!!');
    //       return false;
    //     }
    //   });
    // },

  }
}
</script>
<style lang="scss" scoped>
::v-deep{
  .el-table thead{
    color: #888888;
    font-size: 13px;
  }
  .el-table tr{
    color: #888888;
    font-size: 13px;
  }
  .el-table--medium th{
    padding: 8px 0;
  }
  .el-table--medium td{
    padding: 8px 0;
  }
  .btn1{
    color: #37A8AF;
    font-size: 13px;
  }
  .el-table--striped .el-table__body tr.el-table__row--striped td{
    background: #f3f4f5;
  }
  .el-pagination{
    padding: 0;
    margin-right: -4px;
  }
  .pagination-container{
    padding: 0;
    margin: 0;
  }
  .el-dialog__header{
    display: none;
  }
  .el-dialog__body{
    padding: 25px 30px 60px;
    font-size: 13px;
    color: #888888;
  }
  .modal-body{
    padding: 20px;
  }
  .el-dialog{
    width: 95%;
    margin-top: 10vh !important;
  }
  .el-form-item{
    margin-bottom: 15px;
  }
  .el-input__inner{
    color: #555555;
    border: 1px solid #cccccc;
  }
  .el-select-dropdown {
    position: absolute !important;
    top: 30px !important;
    left: 0 !important;
  }
}
/deep/.el-form-item__error{
  padding-top: 1px;
}
.demo-form-inline{
  padding: 40px 0 0 35px;
}
.box{
  margin-bottom: 0;
}
.box-body{
  padding-top: 0;
}
.btn-default{
  margin-right: 10px;
}
.btn-import{
  margin-right: 10px;
}
.close-btn{
  margin-right: 0px;
  margin-left: 10px;
}
.upload-demo{
  display: inline-block;
}
</style>

就是点击页面后会执行getList()方法,点击查询按钮也会执行这个方法,重置也会执行这个方法,

这个getList方法image-20220811142235686

会去找这个路径,

image-20220811142300207

到js页面,也就是'@/api/couponid/couponid' couponid是js文件

// 获取服务分页列表
export function getPageService(query) {
  return request({

    url: '/couponid/getPagecouponid',
    method: 'get',
    params: query
  })
}

根据上面的路径就会去找对应后台控制器层上面路径进行匹配,后台控制器层,前端参数传过来的,CouponidModel是前端查询条件信息,一般也跟表字段信息保持一致.分页查询默认情况是查询第一页,默认条数是10.

@RequestMapping("/getPagecouponid")
    public Page<Cuscouponinfo> getPageService(CouponidModel couponidmodel, int pageNum, int pageSize){
        System.out.println("走这里");
        return couponidservice.findAll(couponidmodel,pageNum,pageSize);
    }

执行service层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
//上面是使用的包

@Transactional
    public Page<Cuscouponinfo> findAll(CouponidModel couponidmodel, int pageNum, int pageSize) {
        //如果客户好是空的,设置客户号为空
        System.out.println("couponidmodel.getCustomerID():"+couponidmodel.getCustomerid());
		//这里是判断页面查询条件是不是空的,就是这个输入框是空的,用户没有选择
        if("".equals(couponidmodel.getCustomerid())){
            couponidmodel.setCustomerid(null);
        }
        if(StringUtils.isNoneEmpty(couponidmodel.getCustomerid())) {
            couponidmodel.setCustomerid("%"+couponidmodel.getCustomerid().trim()+"%");
        }
        System.out.println("?????????jintian hesuan ");
        String custoemrid = couponidmodel.getCustomerid();
        
        Pageable pageRequest = new PageRequest(pageNum-1,pageSize, Sort.Direction.DESC,"customerid");
        Page<Cuscouponinfo> pageList= couponidrepository.getinfo(custoemrid,pageRequest);
        System.out.println(pageList);
        return pageList;
        
        
    }

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface CouponidRepository extends JpaRepository<Cuscouponinfo,Long> {

    @Query(value = "SELECT c.id,case c.customerid when '1' then '' else c.customerid end customerid,c.couponid,c.serviceid,c.businesscode  from cus_coupon_info c  where 1=1 " +
            "and ( :customerid is null  or customerid like :customerid )  ",
            countQuery ="SELECT count(*) from cus_coupon_info  where 1=1 " +
                   "and ( :customerid is null  or customerid like :customerid ) ",
                    nativeQuery = true)
    Page<Cuscouponinfo> getinfo(@Param("customerid") String customerid, Pageable pageRequest);

}

这是正确的写法,但是我对这个框架是不了解的,我之前是没有写countQuery="" 这个的,然后去搜索了一下@Query注解的作用,因为我们这里使用的是分页功能,所以countQuery=""里面的内容是必须要写的,原理我也不太了解.这里就先记录下,不写就会报错.

2.@Query中countQuery的介绍

@Query源码

/*
 * Copyright 2008-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.jpa.repository;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.data.annotation.QueryAnnotation;

/**
 * Annotation to declare finder queries directly on repository methods.
 *
 * @author Oliver Gierke
 * @author Thomas Darimont
 * @author Christoph Strobl
 *
 * @see Modifying
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@QueryAnnotation
@Documented
public @interface Query {

	/**
	 * Defines the JPA query to be executed when the annotated method is called.
	 */
	String value() default "";

	/**
	 * Defines a special count query that shall be used for pagination queries to lookup the total number of elements for
	 * a page. If none is configured we will derive the count query from the original query or {@link #countProjection()} query if any.
	 */
	String countQuery() default "";

	/**
	 * Defines the projection part of the count query that is generated for pagination. If neither {@link #countQuery()}
	 * nor {@link #countProjection()} is configured we will derive the count query from the original query.
	 *
	 * @return
	 * @since 1.6
	 */
	String countProjection() default "";

	/**
	 * Configures whether the given query is a native one. Defaults to {@literal false}.
	 */
	boolean nativeQuery() default false;

	/**
	 * The named query to be used. If not defined, a {@link javax.persistence.NamedQuery} with name of
	 * {@code $ domainClass}.${queryMethodName}} will be used.
	 */
	String name() default "";

	/**
	 * Returns the name of the {@link javax.persistence.NamedQuery} to be used to execute count queries when pagination is
	 * used. Will default to the named query name configured suffixed by {@code .count}.
	 *
	 * @see #name()
	 * @return
	 */
	String countName() default "";
}

注释@Query提供了某些计数属性,如countQuery,countQueryName。

定义一个特殊的count查询,用于分页查询时,查找页面元素的总个数。如果没有配置,将根据方法名派生一个count查询。

countQuery用于分页,如果要求分页结果返回正确的总计数,@Query 注释可以在 countQuery 属性中与计数查询一起提供。此查询在结果查询之后单独执行,其结果用于填充返回的 Page 的 totalCount 属性。

注意这里记录一下我使用的场景,当我没有写countQuery="" 时,value="" 里面我写的是select * ,因为我测试的数据是少于10条的,不满足分页条件,所以没有报错,当数据库中超过10条数据时,就会引发报错,这个我当时是没有测试出来的,所以总结就是countQuery是必须要写的,这样可以有保证.

3.@Query中nativeQuery = true解释

原生sql: 所谓原生sql,也就是说这段sql拷贝到数据库中,然后把参数值给一下就能运行了

  1. 有nativeQuery = true时,是可以执行原生sql语句
@Query(value = "select * from product_rel where audit_id=?1 and process_object=0 ",nativeQuery = true)
List<ProductRel> findAllByProductAuditId(Integer id);

这个时候,把select * from product_rel where audit_id=?1 and process_object=0拷贝到数据库中,并给audit_id赋一个值,那么这段sql就可以运行。其中数据库表在数据库中的表名就是product_rel,字段audit_id在数据库中也是真实存在的字段名。要求必须为数据库中的表名和字段名,

​ 2.没有nativeQuery = true时,就不是原生sql,@Query中value值的sql语句就只能按照规范来写。否则就会报错,规范就是:表名必须是所写的实体类名,字段名也必须是实体类中的自定义的变量名,

@Query("select ratio from MdmRatio  where enabledNum=1 ")
List<MdmUtilThreeProjection> findByMdmUtilThreeProjection();

image-20220811151222283

select ratio from MdmRatio中的MdmRatio为实体名,不是真正的数据库表名,真正的数据库表名是mdm_ratio,而查询条件中的enabledNum在数据库中真正的名字是enabled_num。

这两个的作用是一样的,只是写法不同。

参考:

https://blog.csdn.net/qq_34626094/article/details/122332931

https://blog.csdn.net/seanxwq/article/details/119803302

https://blog.csdn.net/listeningsea/article/details/122364597

https://blog.csdn.net/weixin_42043101/article/details/103600196

posted @ 2022-09-03 20:00  哩个啷个波  阅读(1652)  评论(0编辑  收藏  举报