纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

mybatis ${}和#{}取值区别 mybatis中${}和#{}取值的区别分析

星空赶路人   2021-09-13 我要评论
想了解mybatis中${}和#{}取值的区别分析的相关内容吗星空赶路人在本文为您仔细讲解mybatis ${}和#{}取值区别的相关知识和一些Code实例欢迎阅读和指正我们先划重点:mybatis,${}和#{},mybatis,$和#区别下面大家一起来学习吧

mybatis作为一个轻量级的ORM框架应用广泛其上手使用也比较简单;一个成熟的框架必然有精巧的设计值得学习

在使用mybatis框架时在sql语句中获取传入的参数有如下两种方式:

  • ${paramName}
  • #{paramName}

那如何理解这两种传参方式呢?如下带你走近背后的奥义

先来回顾下原生Jdbc查询:

public static void main(String[] args) throws Exception {

  // sql语句
  String sql = "select id,name from customer limit 2";
  // 1.加载驱动, 此处使用的mysql驱动包是8.0版本, 若为5.0+版本, 请修改以下类路径
  Class.forName("com.mysql.cj.jdbc.Driver");
  // 2.获取数据库连接
  String url = "jdbc:mysql://localhost:3306/work?useSSL=false&useUnicode=true" +
    "&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true" +
    "&useLegacyDatetimeCode=false&serverTimezone=UTC";
  Connection conn = DriverManager.getConnection(url,"root", "123456");
  // 3、获得可以执行sql语句的对象
  Statement st = conn.createStatement();
  // 4、使用对象去执行SQL语句
  ResultSet rs = st.executeQuery(sql);
  // 5、处理sql语句返回的结果集
  while(rs.next()){
    // 获得一行数据
    Integer id = rs.getInt("id");
    String name = rs.getString("name");
    System.out.println("sql查询: id = " + id + " , name = " + name);
  }
  // 6、释放资源
  rs.close();
  st.close();
  conn.close();
}

控制台打印:

sql查询: id = 1 , name = 李白
sql查询: id = 2 , name = 杜甫

了解Jdbc的人会知道其中第3、4步两条语句也可以换成如下两条:

// 3.创建 PreparedStatement 对象去执行sql
PreparedStatement preparedStatement = conn.prepareStatement(sql);
// 4.执行sql语句
ResultSet rs = preparedStatement.executeQuery();

我们来比较下区别:

  • 创建 PreparedStatement 对象时就把sql语句传入在执行语句时就不用传入sql了;而 Statement 则刚好相反

这就引出了预编译的概念:

  • 如果使用PreparedStatement 对象那么在执行第3步时你既然已经传入了sql则相当于这条sql会被数据库编译(数据库对sql语句的编译也是相当复杂的)所以在第4步执行的时候就不用再传入sql了因为数据库已经知道你要执行的sql了你只需要传入参数即可;
  • 如果使用Statement对象那容易理解数据库就没有提前去解析你的sql因为你创建对象时都没有传入;当执行sql时数据库再编译与执行

看到这里可能也仅仅只记住了一个预先编译sql了一个没有预先编译并没有了解到对于实际开发中的区别以下将会举例说明

那是否PreparedStatement对象这种方式就一定比Statement对象方式好?

没有那么绝对的事大家要理解:

​ PreparedStatement对象的好处是sql已经提前编译好剩下的工作就是传入参数即可编译好的sql可以复用传入不同的参数则数据库就将相应的参数填入编译好的sql而Statement对象就是每次都要传入sql丢给数据库去编译再执行;但是创建PreparedStatement对象的开销是比Statement对象大的

​ 回归到日常开发中以上的区别我们压根也不用在意事实上百分之九十的场景我们使用的是PreparedStatement对象的方式可能平时没有感知到因为这是框架已经封装了再者当系统出现性能问题时也绝对不会是因为这两个对象的原因

​以上简单回顾了下Jdbc中PreparedStatement与Statement对象;

可以预料mybatis中${} 与 #{} 这两种取值方式就是相当于对应着PreparedStatement和Statement对象的区别了

  • #{} 传参代表sql已经预编译好了你传入的参数真的就仅仅是参数!
  • ${} 传参随便你传传完了之后我再统一编译

那具体在使用中有什么不同呢?理解如下两种场景:

1.看如下service和sql语句

@Override
public List<Map<String, Object>> listUser() {
  String param = " and name = '李白'";
  return indexMapper.listUser(param);
}
<select id="listUser" resultType="map">
  select * from customer
  where 1 = 1 #{param}
</select>

以上代码能正常查询吗?

### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: You have
  an error in your SQL syntax; check the manual that corresponds to your MySQL
    server version for the right syntax to use near '' and name = \'李白\''

不能!会报sql语句规则错误之前说了#{} 取值代表sql已经编译好了你传入的仅仅是参数

对应上面示例:

​ sql是指:select * from customer where 1 = 1

​ 参数是指:and name = '李白'

此时sql可以正确运行但是带上传入的参数就不行了要理解你传入的真的仅仅是参数不要和前面的sql混了

但是这明显不对因为想表达的其实是这样:

​ sql是指:select * from customer where 1 = 1 and name = ?

​ 参数是指:'李白'

此时把参数替换进占位符是可以正常运行整个语句的

所以此时应该用 ${param}因为用${}就没有提前编译好哪些是属于sql

此示例表明:当你传入的参数不仅仅是参数其实是一小段sql想和原sql拼接在一起时那就得用${}传参相当于拼接好了之后丢给

数据库去解析整个语句;

sql中的问号代表参数占位符这也是PreparedStatement对象特点之一会将你传入的参数一一替换进占位符

反之如下:

@Override
public List<Map<String, Object>> listUser() {
  String param = "李白";
  return indexMapper.listUser(param);
}
<select id="listUser" resultType="map">
  select * from customer
  where 1 = 1 and name = #{param}
</select>

这种情况使用 #{} 就是对的了因为传入的参数仅仅就是参数替换进sql语句中即可

2.对参数类型的影响

@Override
public List<Map<String, Object>> listUser() {
  String param = "李白";
  return indexMapper.listUser(param);
}
<select id="listUser" resultType="map">
  select * from customer
  where 1 = 1 and name = ${param}
</select>

以上代码能执行成功吗?

按理说传入的仅仅是参数不管是否预编译都应该能执行但是实际还是会报错

这是执行时打印出的sql语句:

select * from customer where 1 = 1 and name = 李白

显然问题就在于参数没有加单引号name字段是字符串类型传入的也是字符串偏偏mybatis转换之后没有加单引号

所以当传入字符串类型参数时应该用 #{} 取值此时会自动加上单引号

再看下面这种语句:

@Override
public List<Map<String, Object>> listUser() {
  String param = "name";
  return indexMapper.listUser(param);
}
<select id="listUser" resultType="map">
  select * from customer
  where 1 = 1
  order by ${param} desc
</select>

此时传入的参数是要排序的字段名称之前说了如果采用#{} 取值则实际是会自动加上单引号的但是order by后面的排序字段需要单引号吗?

不需要所以这种情况只能使用 ${} 取值

你可能会发现此处用 #{} 取值也不会报错那是因为mysql支持这种写法但是查询的结果并不对

​ 日常开发中只要能理解上述两种情形那么就能正确使用 ${} 和 #{}由于这两种方式取值原理的区别也容易明白 #{} 这种方式是可以防止sql注入的

总结


相关文章

猜您喜欢

网友评论

Copyright 2020 www.sopisoft.net 【绿软下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式