首页 > 其他分享 >Mybatis -过滤查询

Mybatis -过滤查询

时间:2024-07-09 16:57:56浏览次数:17  
标签:product productName 过滤 salePrice 货品 Mybatis 查询

一、过滤查询

过滤查询实则是根据用户传入的条件进行数据的筛选查询,最终返回结果给用户。

1、需求及效果图

例如:在商品列表中,用户可以根据自己的需要,按照商品的名称和价格范围对商品进行查询。

2、原理分析

以上条件设计,后台需执行的 SQL 会有如下几种:

 需求 1:查询所有货品信息。

SELECT * FROM product

 需求 2:查询货品名称包含某个字符串的货品信息,例如包含 M 的。

SELECT * FROM product WHERE productName LIKE '%M%'

 需求 3:查询货品零售价在某个价格以下的货品信息,例如销售价小于等于 500。

SELECT * FROM product WHERE salePrice <= 500

 需求 4:查询货品零售价在某个价格以下的货品信息,例如销售价大于等于 200。

SELECT * FROM product WHERE salePrice >= 20

SELECT * FROM product WHERE salePrice >= 200 AND salePrice <= 500

 需求 6:查询货品名称包含某个字符串且零售价在某个价格以上的货品信息,例如名称包含 M,且销售价大于等于 200。

SELECT * FROM product WHERE productName LIKE '%M%' AND salePrice >= 200

 需求 7:查询货品名称包含某个字符串且零售价在某个价格以下的货品信息,例如名称包含 M,且销售价小于等于 500。

SELECT * FROM product WHERE productName LIKE '%M%' AND salePrice <= 500

 需求 8:查询货品名称包含某个字符串且零售价在某个区间的的货品信息,例如名称包含 M,且销售价在 200 到 500 之间(包含)。

SELECT * FROM product WHERE productName LIKE '%M%' AND salePrice >= 200 AND salePrice <= 500

从以上 SQL 分析,不管如何查询,SELECT * FROM product 这部分 SQL 是固定不变,变的地方就只是 WHERE 子句部分,而这个子句的变化,取决于用户在页面传递的查询参数。因此,过滤查询说直白点就是根据用户查询传递的参数,拼接 WHERE 条件,这就是过滤查询的底层原理

3、需解决的问题

 如何根据用户传递的参数,在 Mapper XML 中拼接对应的 SQL?

解决方案:第一个问题定义一个类封装数据即可。第二个问题使用 MyBaits 的动态 SQL 来解决。

二、过滤条件数据封装

根据前面我们对多条件过滤查询的分析得知,用户过滤查询时,请求时会传递可能会传递多个过滤条件的参数来查询对应的数据,又因为这些参数需要在后台多个层次之间进行传递(servlet--->service--->dao),所以为了方便参数传递,我们选择将这些参数封装到指定的对象中,然后再在多层之间进行传递。

编写 ProductQueryObject.java

用来封装过滤查询的参数。但由于产品查询又想支持分页查询,又想支持过滤查询,所以使用 ProductQueryObject 来 继承 QueryObject,这样既可以封装分页查询的参数,又可以封装过滤查询的参数,且减少代码重复。

@Setter

@Getter

public class ProductQueryObject extends QueryObject {

private String productName; private BigDecimal minSalePrice; private BigDecimal maxSalePrice;

// 商品名

// 最小销售价格

// 最大销售价格

}

三、MyBatis 动态 SQL

为了解决在 Mapper XML 中拼接 SQL 的问题,此处需要来学习 MyBatis 的动态 SQL.

1if 标签

1.1、语法与作用

用于单条件判断,一般用于判断是否符合某一查询条件。语法如下:

<if test="boolean 表达式"></if>

boolean 表达式:

可以写类似这样(productName != null)booolean 表达式。

 表达式中可以使用逻辑运算符,使用小写 and,or,但不可用 &&,||。

1.2、需求应用

需求:查询货品名称包含某个字符且零售价在某个区间的所有货品信息,商品名、最小销售价格和最大销售价格可能传也可能没传。

<select id="queryForList" resultType="cn.domain.Product"> SELECT * FROM product

<if test="productName != null and productName != ''">

WHERE productName LIKE concat('%', #{productName}, '%')

</if>

<if test="minSalePrice != null">

AND salePrice >= #{minSalePrice}

</if>

<if test="maxSalePrice != null">

AND salePrice <= #{maxSalePrice}

</if>

LIMIT #{start}, #{pageSize}

</select>

1.3、存在的问题

minSalePrice 和 maxSalePrice 条件是可选的,可能传也可能没传,此时造成拼接的 SQL 出现语法问题.

  < 报错,因为使用了 XML 的标签字符。

解决方案:第一个问题使用 where 标签解决,第二个问题使用转义符解决。

2where 标签

2.1、语法与作用

用于在第一个条件语句之前加入 WHERE 关键字或者去除多余的 AND 或 OR 关键字,语法如下:

<where>条件语句</where>

注意:

若查询条件语句没有 WHERE 关键字,则自动在查询条件语句之前插入 WHERE;

若查询条件语句以 "AND" 或 "OR" 开头,则把第一个 AND 或 OR 使用 WHERE 关键字替换。

2.2、需求应用

需求:查询货品名称包含某个字符且零售价在某个区间的所有货品信息,关键字、最小销售价格和最大销售价格可能传也可能没传。

<select id="queryForList" resultType="cn.domain.Product"> SELECT * FROM product

<where>

<if test="productName != null and productName != ''">

AND productName LIKE concat('%', #{productName}, '%')

</if>

<if test="minSalePrice != null">

AND salePrice >= #{minSalePrice}

</if>

<if test="maxSalePrice != null">

AND salePrice <= #{maxSalePrice}

</if>

</where>

LIMIT #{start}, #{pageSize}

</select>

3MyBatis 转义符号

< < 小于号

> > 大于号

& & 与符号

' ' 单引号

" " 双引号

<select id="queryForList" resultType="cn.domain.Product"> SELECT * FROM product

<where>

<if test="productName != null and productName != ''">

AND productName LIKE concat('%', #{productName}, '%')

</if>

<if test="minSalePrice != null">

AND salePrice >= #{minSalePrice}

</if>

<if test="maxSalePrice != null">

AND salePrice <= #{maxSalePrice}

</if>

</where>

LIMIT #{start}, #{pageSize}

</select>

四、过滤查询实现

需求:查询货品名称包含某个字符串且零售价在某个区间的所有货品信息。

1、编写 ProductQueryObject.java

继承 QueryObject,用于封装查询产品过滤查询的参数值。

@Setter

@Getter

public class ProductQueryObject extends QueryObject {

private String productName; private BigDecimal minSalePrice; private BigDecimal maxSalePrice;

// 商品名

// 最小销售价格

// 最大销售价格

}

注意:一般的为了避免不必要的麻烦,让字段名称和表单中的请求参数名称相同。

2、修改 ProductMapper.xml

拼接查询的 SQL 语句,查询结果总数和查询结果集都要拼接,且查询条件是一样的。

<select id="queryForCount" resultType="int"> SELECT COUNT(*) FROM product

<where>

<if test="productName != null and productName != ''">

AND productName LIKE concat('%', #{productName}, '%')

</if>

<if test="minSalePrice != null">

AND salePrice >= #{minSalePrice}

</if>

<if test="maxSalePrice != null">

AND salePrice <= #{maxSalePrice}

</if>

</where>

</select>

<select id="queryForList" resultType="cn.domain.Product"> SELECT * FROM product

<where>

<if test="productName != null and productName != ''">

AND productName LIKE concat('%', #{productName}, '%')

</if>

<if test="minSalePrice != null">

AND salePrice >= #{minSalePrice}

</if>

<if test="maxSalePrice != null">

AND salePrice <= #{maxSalePrice}

</if>

</where>

LIMIT #{start}, #{pageSize}

</select>

3、修改单元测试类

造一些差异化的数据,修改 ProductServiceTest.java 其中 testQuery 方,测试过滤查询,看是否有问题,最好每个条件都试一下。

@Test

public void testQuery(){

ProductQueryObject qo= new ProductQueryObject(); qo.setCurrentPage(1);

qo.setProductName("M");

qo.setMinSalePrice(new BigDecimal("200"));

qo.setMaxSalePrice(new BigDecimal("500"));

PageResult<Product> pageResult = productService.query(qo);

System.out.println("结果集数据:" + pageResult.getData());

System.out.println("当前页总记录数:" + pageResult.getTotalCount());

System.out.println("条数:" + pageResult.getData().size());

System.out.println("总页数:" + pageResult.getTotalPage());

System.out.println("上一页:" + pageResult.getPrevPage());

System.out.println("下一页:" + pageResult.getNextPage());

}

4、修改 list.jsp

在 form 中增加过滤查询条件 input 元素,包含可以输入商品名,最小销售价,最大销售价,注意:让该表单(form)包含列表的 table 元素。

<form action="/product" method="post">

货品名:<input type="text" name="productName" style="width: 80px;">

价格: <input type="number" name="minSalePrice" style="width: 80px"> - <input type="number" name="maxSalePrice" style="width: 80px;"> <input type="submit" value="查询">

<table border="1" cellspacing="0" cellpadding="0" width="80%"> <!-- 省略 -->

</table>

</form>

5、修改 ProductServlet.java

在调用业务对象的 query 方法之前,获取过滤查询参数,并封装到 ProductQueryObject 对象中。

protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

ProductQueryObject qo = new ProductQueryObject();

// 获取请求参数 productName

String productName = req.getParameter("productName"); qo.setProductName(productName);

// 获取请求参数 minSalePrice

String minSalePrice = req.getParameter("minSalePrice"); if(StringUtil.hasLength(minSalePrice)){

qo.setMinSalePrice(new BigDecimal(minSalePrice));

}

// 获取请求参数 maxSalePrice

String maxSalePrice = req.getParameter("maxSalePrice"); if(StringUtil.hasLength(maxSalePrice)){

qo.setMaxSalePrice(new BigDecimal(maxSalePrice));

}

// 获取请求参数 currentPage

String currentPage = req.getParameter("currentPage"); if(StringUtil.hasLength(currentPage)) {

qo.setCurrentPage(Integer.valueOf(currentPage));

}

// 获取请求参数 pageSize

String pageSize = req.getParameter("pageSize"); if(StringUtil.hasLength(pageSize)) {

qo.setPageSize(Integer.valueOf(pageSize));

}

  • 调用业务层方法来处理请求某一页数据的需求

PageResult<Product> pageResult = productService.query(qo);

// 把数据共享给 list.jsp

req.setAttribute("pageResult", pageResult);

// 控制跳转到 list.jsp 页面

req.getRequestDispatcher("/WEB-INF/views/product/list.jsp").forward(req,

resp);

}

6、查询条件回显

修改 ProductServlet.java,在跳转 list.jsp 前加入如下代码:

  • 把查询参数数据共享给 list.jsp req.setAttribute("qo", qo);

修改 list.jsp,使之支持回显出查询的信息:

<form action="/product" method="post">

货品名:<input type="text" name="productName" style="width: 80px;" value="${qo.productName}">

价格: <input type="number" name="minSalePrice" style="width: 80px" value="${qo.minSalePrice}"> -

<input type="number" name="maxSalePrice" style="width: 80px;" value="${qo.maxSalePrice}">

<input type="submit" value="查询">

<table border="1" cellspacing="0" cellpadding="0" width="80%"> <!-- 省略 -->

</table>

</form>

7、页面测试

每个条件都要测试到,保证在网页上测试通过。

五、细节说明

1concat 函数

MySQL 中 concat 函数是用来拼接字符串,项目模糊查询时会使用这个函数,与 % 进行拼接完成模糊查询的需求。

<!-- 其中 if 标签 和 #{} 中的 productName  ProductQueryObject 的属性名 --> <!-- AND 后面的 productName  product 表中列明 -->

<if test="productName != null and productName != ''">

AND productName LIKE concat('%', #{productName}, '%')

</if>

2productName 判断空的问题

productName 属性是字符串类型,用户查询的时候,可能没传此参数或者传的是空字符串,所以我们需要在拼接条件之前进行判断,若用户查询传的是非空且有内容的的字符串时,才按照此条件过滤查询商品信息。

而判断方式可以有以下几种:

2.1、在 Mapper XML 中判断

<if test="productName != null and productName != ''">

AND productName LIKE concat('%', #{productName}, '%')

</if>

2.2、在 Java 代码中判断

因为 MyBatis 通过调用查询对象中的 getProductName() 方法,获取到 productName 属性值,然后再做判断的。所以我们可以在把查询对象中的 getProductName() 方法重写,在 MyBatis 获取 productName 属性值之前加入判断。

@Setter

@Getter

public class ProductQueryObject extends QueryObject { private String productName;

private BigDecimal minSalePrice;

private BigDecimal maxSalePrice;

public String getProductName() {

return StringUtil.hasLength(this.productName) ? this.productName : null;

}

}

那么在 Mapper XML 中此时就不需要再做空字符串的判断了只须像下面这样写即可。

<if test="productName != null">

AND productName LIKE concat('%', #{productName}, '%')

</if>

六、解决翻页数据丢失问题

1、存在的问题

当通过高级查询查询出结果集的时候,如下图:查询条件货品名输入的是 M,执行查询返回如下结果:

但之后如果再点击下一页:应该是进入 2/5 页,可是结果却是如下这样的:

此时,我们会发现过滤查询条件已经丢失,分页条的数据也全部和预期不一样了。

2、问题原因

此时的翻页操作(点击首页,上一页,下一页,尾页等这些操作),是通过 a 标签的超链接 发送 GET 请求查询,该超链接只带有 currentPage 参数,没带有其他过滤查询条件参数。

<a href="/product?currentPage=1">首页</a>

<a href="/product?currentPage=${pageResult.prevPage}">上一页</a> <a href="/product?currentPage=${pageResult.nextPage}">下一页</a> <a href="/product?currentPage=${pageResult.totalPage}">尾页</a>

3、解决思路

在翻页的时候,不仅要发送 currentPage 到后台,同时还需要把过滤查询的参数发送到后台。解决方案:

 在超链接上拼接过滤查询的参数,若参数过多,实现比较麻烦

 使用 JS 来实现翻页操作,将超链接上的分页相关的参数放到表单中(推荐方式)。

 在高级查询表单中提供 currentPage 的文本框,我们把需要跳转的页码设置到该文本框

 再使用 JS 提交过滤查询表单,此时就可以把查询条件和 currentPage 一起提交到后台,并封

装到 ProductQueryObject中。

4、代码实现

修改 list.jsp

<!-- 加了 id 属性,目的为了方便通过 JS 获取标签对象重新设置 value 属性值 -->

跳转到<input type="number" id="currentPage" onchange="changePageSize()"

name="currentPage" value="${pageResult.currentPage}" style="width: 60px;">

<a href="#" onclick="goPage(1);">首页</a>

<a href="#" onclick="goPage(${pageResult.prevPage});">上一页</a> <a href="#" onclick="goPage(${pageResult.nextPage});">下一页</a> <a href="#" onclick="goPage(${pageResult.totalPage});">尾页</a>

function goPage(paegNum) {

  • pageNum 设置给表单中表示跳转到某一页的表单控件中

document.getElementById("currentPage").value = paegNum;

  • 提交表单数据

document.forms[0].submit();

}

七、分页过滤查询流程图

八、拓展

1Mapper XML 中代码优化

抽取重复 SQL 的片段(模板),供其他 SQL 包含。

<!-- 抽取一个 SQL 片段-->

<sql id="where_sql">

<where>

<if test="productName != null and productName != ''">

AND productName LIKE concat('%', #{productName}, '%')

</if>

<if test="minSalePrice != null">

AND salePrice >= #{minSalePrice}

</if>

<if test="maxSalePrice != null">

AND salePrice <= #{maxSalePrice}

</if>

</where>

</sql>

<select id="queryForCount" resultType="int"> SELECT COUNT(*) FROM product

<include refid="where_sql"/>

</select>

<select id="queryForList" resultType="cn.domain.Product"> SELECT * FROM product

<include refid="where_sql"/>

LIMIT #{start}, #{pageSize}

</select>

2、加入关键字查询

所谓关键字查询,根据输入的关键字,把表中多个列值包含这个关键字的查询出来。举个例子:有个需求根据产品的名字和品牌做关键字查询,其本质意思就是产品名称或者产品品牌只要包含输入的关键字就把其查询出来。

标签:product,productName,过滤,salePrice,货品,Mybatis,查询
From: https://blog.csdn.net/m0_63600788/article/details/140276344

相关文章

  • MyBatisPlus的Mapper.xml入参List执行in函数
    使用情景这个是开发过程中比较常见的情景,入参一个list,在Mapper.xml里面执行sql的in函数,今天来记录下这个问题,希望可以给大家一点帮助启发。Mapper文件解决方案xml文件<selectid="get"resultType="com.vo.tVo">SELECTnameFROMus......
  • 关联查询
     #关联查询   isouter=True表示使用外连接#1、查询2020年入职的员工姓名以及该员工的所在部门名称result=session.execute(select(Employee.name,Dept.name).join(Dept,isouter=True).where(extract('year',Employee.entry_date)==2018))#2、查询省份号码是:11111......
  • 6. DQL-多表查询
        本小节主要讲解数据库的多表查询功能,可实现从多个表中查询数据,多表查询内容包括内连接,外连接,笛卡尔积,等值连接,非等值连接,SQL99标准链接语法以及联合查询。6.1.1多表查询的介绍从多个表中获取数据思考如下问题?写一条查询语句,查询员工姓名、部门名称、工作地点? ......
  • 快速上手:前后端分离开发(Vue+Element+Spring Boot+MyBatis+MySQL)
    文章目录前言项目简介环境准备第一步:初始化前端项目登录页面任务管理页面第二步:初始化后端项目数据库配置数据库表结构实体类和Mapper服务层和控制器第三步:连接前后端总结......
  • 使用Mybatis框架操作数据库
    --------------idea中创建springboot项目引入Mybatis框架-----------------1、新建空项目2.创建模块3.选择springboot版本,添加mybatisframework框架和Mysqldriver驱动 4.删除多余文件 5.选择父工程中选择spring-boot版本6.选择依赖版本号(1)mybatis的起步依赖......
  • 【MyBatis-Plus】 代码生成器使用指南——快速上手最好用的代码生成器!
    MyBatis-Plus代码生成器使用指南1.简介2.环境准备3.项目结构4.引入依赖5.编写代码生成器配置类6.配置解释6.1全局配置6.2数据源配置6.3包配置6.4模板配置6.5策略配置7.运行代码生成器8.生成的代码结构9.总结1.简介MyBatis-Plus是一个MyBatis......
  • 【SVN】 设置过滤上传文件
    项目级过滤文件1.1在项目空白处,右键选择SVN-Properties1.2New->Other->新建一个“svn:global-ignores”属性1.3查看效果选中上传项目,右键-》SVN->Add如图所示,.vs、bin、obj文件都过滤了全局过滤文件配置2.1在项目工作根目录上,右键->TortosieSVN->Settin......
  • mybatis缓存
    MyBatis提供了两级缓存机制:一级缓存(本地缓存)和二级缓存(全局缓存)。这两级缓存可以显著提高数据查询的效率,减少数据库访问的次数。下面介绍MyBatis的一级缓存和二级缓存的原理、配置和使用方法。题外话:......
  • MybatisX插件使用
    什么是MybatisX插件:MybatisX是idea的一个插件,可以方便地生成MyBatis的映射文件和对应的Java代码。MybatisX的优点:提高开发效率,提高开发效率更方便进行数据库操作怎么使用MybatisX:创建一个springboot项目,在pom.xml文件中引入mybatis-plus依赖建立MySQL连接输入......
  • 数据库新开账号,并授予了相应表的查询权限。访问时,其他PC端远程被拒绝
    报错信息        DBMS:Casesensitivity:plain=mixed,delimited=exactDriver:(ver.,JDBC)Effectiveversion:MySQL(ver.0.0)[28000][1045]Accessdeniedforuser'woer_mysql_oa'@'172.16.41.94'(usingpassword:YES). 原因    上述报错可以......