mybatis中#和$区别

150 阅读1分钟

前言

今天又去水了一波,结果有个问题居然忘记了,myBatis中的#和$有什么区别?

实验

1、使用# mybatis Mapper.xml配置如下:

<select id="selectByFuzzy" resultMap="BaseResultMap">
  select * from test_case where case_name like #{testCaseName} or busi_code like #{testCaseName} or case_name_zh like #{testCaseName}
</select>

查询语句以及结果如下:

==>  Preparing: select * from test_case where case_name like ? or busi_code like ? or case_name_zh like ?
==> Parameters: do_queryUsableBalance(String), do_queryUsableBalance(String), do_queryUsableBalance(String)
<==    Columns: id, case_name, user_id, request, expected_response, ignore_fields, create_date, last_update_date, busi_code, result_response, result, result_type, url, ext3, ext2, ext1, case_name_zh, test_result_file_name
<==        Row: 1, do_queryUsableBalance, 1001, <<BLOB>>, <<BLOB>>, errorMsg, 2023-03-01 15:01:17, 2021-12-08 17:33:45, 7064, <<BLOB>>, <<BLOB>>, 0, null, null, null, null, 查询可用余额, 49456.html
<==      Total: 1

可以看出#{}占位符最终被替换为了jdbc中常用的?占位符。

2、使用$ mybatis Mapper.xml配置如下:

<select id="selectByFuzzy" resultMap="BaseResultMap">
  select * from test_case where case_name like ${testCaseName} or busi_code like ${testCaseName} or case_name_zh like ${testCaseName}
</select>

执行结果如下:

==>  Preparing: select * from test_case where case_name like do_queryUsableBalance or busi_code like do_queryUsableBalance or case_name_zh like do_queryUsableBalance
==> Parameters:

其实最终的运行结果是报错了。但是光看这句sql其实就可以发现这是一个错误的语句。

原理

使用#号从sql语句中我们可以发现这种操作类似于原生jdbc的prepareStatement方式,jdbc写法如下:

String sql = "insert into ZD.Group_cust_mapper(cust_id, acct_id) values(?, ?)";
stmtPreparedMysql = connMysql.prepareStatement(sql);
		stmtPreparedMysql.setString(1, custId);
		stmtPreparedMysql.setString(2, acctId);
		stmtPreparedMysql.executeUpdate();
		stmtPreparedMysql.close();

使用$符原理就是使用Statement的方式操作sql,需要将sql提前拼接好。jdbc写法如下:

@Test
public void test2(){
 Connection conn = null;
 Statement stmt = null;

 try{
   Class.forName("com.mysql.jdbc.Driver").newInstance();
   conn = DriverManager.getConnection(
     "jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai",
     "root",
     "123456");
   stmt = conn.createStatement();
   List<String> whitelistAll = new LinkedList<String>();
   String sql="select * from test_case where case_name like 'do_queryUsableBalance' or busi_code like 'do_queryUsableBalance' or case_name_zh like 'do_queryUsableBalance'";
   //int deleted=stmt.executeUpdate(sql);
   ResultSet result = stmt.executeQuery(sql);
   result.setFetchSize(1);  // 游标一个一个读
   int sum = 0;

   while(result.next()) {
     sum ++;
   }
   System.out.println(sum);
 } catch (Exception e) {
   System.out.print(e.getMessage());
   e.printStackTrace();
 } finally {
   try {
     stmt.close();
     conn.close();
   } catch (SQLException e) {
     System.out.print("Can't close stmt/conn because of " + e.getMessage());
   }
 }
}

Statement

  • 每次通过Statement执行SQL语句时,都要将SQL语句发送给数据库服务器进行编译,因此执行速度比较慢。
  • Statement不能防止SQL注入攻击。

PreparedStatement:

  • 在创建PreparedStatement时,就已经预编译了SQL语句,可以多次执行,因此执行速度快。
  • PreparedStatement会在执行时将输入参数进行参数化处理,可以防止SQL注入攻击。

什么情况下必须要使用$占位符

比如目标sql是这样的:select name, age, school from student where score > 60 order by age desc;
那么如果使用mybatis时,mapper.xml就需要这样配置:

<select id="selectStudent" resultMap="BaseResultMap">
  select name, age, school from student where score > #{score} order by ${age} desc;
</select>