天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。

MyBatis 中为什么不建议使用 where 1=1?

itzoo 789次浏览 0个评论

点击“终码一生”,关注,置顶公众号

每日技术干货,第一时间送达!



最近接手了一个老项目,“愉悦的心情”自然无以言表,做开发的朋友都懂,这里就不多说了,都是泪图片…


接手老项目,自然是要先熟悉一下业务代码,然而在翻阅 mapper 文件时,发现了一个比较诡异的事情。这里给出简化后的业务代码:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <select id="list" resultType="com.example.demo.model.User">
        select * from user
        where 1=1
        <if test="name!=null">
            and name=#{name}
        </if>
        <if test="password!=null">
            and password=#{password}
        </if>
    </select>
</mapper>


机智的小伙伴可能已经看出了问题,在众多 mapper 中发现了一个相同的想象,几乎所有的 mapper 中都包含了一个无用的拼接 SQL:where 1=1。作为一个几乎有代码洁癖症的人,自然是忍不住动手改造一番了。


 

1

错误的改造方式


既然是去掉 where 1=1,那最简单的方式就是将它直接从代码中删除了,如下代码所示:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <select id="list" resultType="com.example.demo.model.User">
        select * from user
            where
            <if test="name!=null">
                name=#{name}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
    </select>
</mapper>


以上代码删除了 1=1,并且把第一个 name 查询中的 and 去掉了,以防 SQL 查询报错。


但这样就没问题了吗?我们直接来看结果,当包含参数 name 查询时,结果如下:



一切顺理成章,完美的一塌糊涂。 


然而,当省略 name 参数时(因为 name 为非必要参数,所以可以省略),竟然引发了以下异常:



又或者只有 password 查询时,结果也是一样:



都是报错信息,那肿么办呢?难不成把 1=1 恢复回去?


另外,关注公号“终码一生”,回复关键词“资料”,获取视频教程和最新的面试资料!



2

正确的改进方式


其实不用,在 MyBatis 中早已经想到了这个问题,我们可以将 SQL 中的 where 关键字换成 MyBatis 中的标签,并且给每个标签内都加上 and 拼接符,这样问题就解决了,如下代码所示:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <select id="list" resultType="com.example.demo.model.User">
        select * from user
        <where>
            <if test="name!=null">
               and name=#{name}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
        </where>
    </select>
</mapper>


代码改造完成之后,接下来我们来测试一下所有的请求场景。














MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。[1] 

背景介绍编辑 语音

MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。

每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。

用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpath resource),但你可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类—-Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。

特点编辑 语音

简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。

解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

提供映射标签,支持对象与数据库的orm字段关系映射

提供对象关系映射标签,支持对象关系组建维护

提供xml标签,支持编写动态sql。[2] 

总体流程编辑 语音

(1)加载配置并初始化

触发条件:加载配置文件

处理过程:将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。

(2)接收调用请求

触发条件:调用Mybatis提供的API

传入参数:为SQL的ID和传入参数对象

处理过程:将请求传递给下层的请求处理层进行处理。

(3)处理操作请求

触发条件:API接口层传递请求过来

传入参数:为SQL的ID和传入参数对象

处理过程:

(A)根据SQL的ID查找对应的MappedStatement对象。

(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。

(C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。

(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。

(E)释放连接资源。

(4)返回处理结果将最终的处理结果返回。

功能架构编辑 语音

MyBatis架构

MyBatis架构

我们把Mybatis的功能架构分为三层:

(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

(2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。

(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。



不传任何参数的请求


此时我们可以不传递任何参数(查询所有数据),如下图所示:



生成的 SQL 语句如下:



传递 1 个参数的请求


也可以传递 1 个参数,根据 name 进行查询,如下图所示:



生成的 SQL 如下图所示:



也可以只根据 password 进行查询,如下图所示:



生成的 SQL 如下图所示:



传递 2 个参数的请求


也可以根据 name 加 password 的方式进行联合查询,如下图所示:



生成的 SQL 如下图所示:



用法解析


我们惊喜的发现,在使用了标签之后,无论是任何查询场景,传一个或者传多个参数,或者直接不传递任何参数,都可以轻松搞定。


首先,标签会判断,如果没有任何参数,则不会在 SQL 语句中拼接 where 查询,反之才会拼接 where 查询;其次在查询的标签中,每个标签都可以加 and 关键字,MyBatis 会自动将第一个条件前面的 and 关键字删除掉,从而不会导致 SQL 语法错误,这一点官方文档中也有说明,如下图所示:




3

总结


在 MyBatis 中,建议尽量避免使用无意义的 SQL 拼接  where 1=1,我们可以使用标签来替代 where 1=1,这样的写既简洁又优雅,何乐而不为呢?


PS:防止找不到本篇文章,可以收藏点赞,方便翻阅查找哦。


往期推荐



SpringBoot整合ActiveMQ实现Queue和Topic两种模式

Windows 性能超越 Linux?

从 JDK 8 到 JDK 17,GC 性能大幅提升!!

每日开源 | 推荐一款java class文件安全加密工具,你也许用得到

面试官:对象不再使用时,为什么要赋值为 null ?

SQL语句中 left join 后用 on 还是 where,区别大了!



ITZOO版权所有丨如未注明 , 均为原创丨转载请注明来自IT乐园 ->MyBatis 中为什么不建议使用 where 1=1?
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址