美文网首页
Mybatis中#{}和${}区别以及预编译理解

Mybatis中#{}和${}区别以及预编译理解

作者: 打不开的回忆 | 来源:发表于2018-06-04 22:21 被阅读0次

    开发中经常使用mybatis的orm框架对数据库进行相关操作,mybatis最大的好处就是可以使用动态sql,即在mybatis对sql进行预编译之前会对sql语句进行动态解析,处理之后再进行预编译从而到达DBMS执行sql语句。而我们开发中经常用到的动态传参#{param}和${param}在动态解析时会有可能产生不同的sql语句,下面我们就来讨论一下如何正确使用#和$。

    预编译

    先来熟悉一下预编译,所谓预编译理解下来即:数据库驱动在发送sql和参数到DBMS(数据库管理软件,暂时这么理解吧)之前,先对sql语句进行编译处理,之后DBMS则可以直接对sql进行处理,不需要再次编译,提高了性能。这一点mybatis 默认情况下,将对所有的 sql 进行预编译处理。

    使用预编译可以带来哪些好处?

    JDBC中使用PreparedStatement来抽象预编译语句,从而使用达到预编译效果。
    1:预编译阶段可以对sql语句进行优化。   
        预编译可以将多个操作步骤合并成一个步骤,一般而言,越复杂的sql,编译程度也会复杂,难度大,耗时,费性能,而预编译可以合并这些操作,预编译之后DBMS可以省去编译直接运行sql。
    2:预编译语句可以重复利用。
        把一个 sql 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个sql,可以直接使用这个缓存的 PreparedState 对象。有兴趣的可以查看mysql驱动包里面的ConnectionImpl类是如何操作预编译的,这里提一下缓存PreparedStatement,在执行预编译时会先去判断是否存在缓存,如果存在则对参数清空,绑定新的参数。如果不存在则调用数据库进行预编译处理生成一个PreparedStatement对象。

    mybatis动态解析

    关于动态解析的原理,可以参考这篇文章,写的非常详细mybatis动态解析原理
    理解下来当我们在xml文件中写入动态sql之后,调用相关封装类进行解析处理,最终生成一个BounSql,该类包括sql语句,参数,参数源数据等参数。

    #{}和${}在动态解析时的区别?

    mybatis动态解析时,对于变量的值替换发生在不同阶段。
    #{ } 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,变量替换完成在DBMS中。

    ${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换,DBMS拿到的即为执行的sql。

    这里需要说明的是并不是使用${}一定会产生注入,或是一定会被显示替换变量。

    例如当${}使用在字段的动态变量时,mybatis在动态解析时是会替换成一个string字符串的,不是显示的字段名字。select * from user where name = ${name};传入name为auth,则在动态解析时也会生成select * from user where name = 'auth';从而DBMS进行处理sql。这里${}和#{}可以达到相同的效果,区别就是#{}会生成一个预编译sql给DBMS,在DBMS完成变量的替换。
    然而如果${}的位置发生在from后面或是order by(开发中经常会用到动态根据字段排序),这时候会显式的使用变量;select * from user where name='auth' order by ${column};select * from ${tableName} where name=#{name};这类的${}会进行显式处理,即传入什么直接替换,例如${column}=id和${column}='id'则会产生不同的效果。而此类语句有可能会产生sql的注入,经典的例子:select * from ${tableName} where name = #{name};传入变量tableName= "user;delete user;-- " 动态解析后的sql:select *fromuser;deleteuser;-- where name = ?;会存在注入。如果select * from #{tableName} where name = #{name};传入tableName="user" 则产生无法编译的sql错误。

    结论:能使用#{}尽量使用,因为i#{}可以产生一个预编译对象,可以重复利用,不会产生sql注入,当语句必须使用表明或者字段名时,需要排除sql注入的可能,必须使用${}。

    相关文章

      网友评论

          本文标题:Mybatis中#{}和${}区别以及预编译理解

          本文链接:https://www.haomeiwen.com/subject/lefusftx.html