首页 > 其他分享 >Mybatis 不要乱用,这个坑真不小!

Mybatis 不要乱用,这个坑真不小!

时间:2023-10-08 09:55:57浏览次数:25  
标签:语句 name 传入 坑真 参数 param sql Mybatis 乱用

来源:www.cnblogs.com/tjstep/p/15256463.html

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

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

  • ${paramName}
  • #{paramName}

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

推荐一个开源免费的 Spring Boot 实战项目:

https://github.com/javastacks/spring-boot-best-practice

先来回顾下原生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中PreparedStatementStatement对象;

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

  • #{} 传参,代表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 = ?

参数是指:'李白'

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

所以此时应该用 ,因为用{}就没有提前编译好哪些是属于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注入的。

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

标签:语句,name,传入,坑真,参数,param,sql,Mybatis,乱用
From: https://www.cnblogs.com/javastack/p/17748188.html

相关文章

  • 若依系统将Mybatis升级为Mybatis-Plus详解
    1.找到ruoyi-framework/...../config/MybatisConfig,将其sqlSessionFactory(DataSourcedataSource)注释2.找到整个项目的pom文件,添加Mybatis-plus版本号3.找到ruoyi-common的pom文件,添加依赖4.找到ruoyi-admin/../resources/application.yml文件,添加plus的配置文件简简单......
  • 零基础尝试mybatis-plus读写分离
     看了好几篇博友写的文章,关于spring-boot整合mybatis-plus实现读写分离,不过都是缺这少那的,跑不起来,所以自己实操了一次,做个记录 实现方式为使用Aop切面1、增加数据库枚举类/***数据库类型*/publicenumDBTypeEnum{/***主节点*/MASTER,......
  • Spring+MyBatis配置文件
    spring-config.xml<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="h......
  • 人事管理系统 SpringBoot2+MyBatis+MySQL5.7
    人事管理系统一、系统介绍本系统为人事管理系统,系统分为七大模块:绩效考核,招聘管理,档案管理,工资管理,考勤管理,培训管理,系统管理。可满足小企业日常办公。本系统最大特色是有强大和灵活的权限控制功能,所有菜单,按钮功能均可由管理通过配置来控制。系统默认有四个角色:管理员,财务专......
  • 就业管理系统 SpringBoot2+MyBatis+MySQL5.7
    就业管理系统一、系统介绍本系统为就业管理系统,主要围绕高校毕业生的毕业情况进行跟踪和分析,为学校领导对专业设置优化,为高校毕业生就业方向提供参考。系统分为六大模块:就业管理,招聘咨询,通告管理,学院管理,师生管理,系统管理。系统默认有三个角色:管理员,老师,学生用户管理员(admin......
  • MyBatis注解开发
    MyBatis注解开发MyBatis还支持非常便捷的基于注解的配置方式。在该方式中,我们不再编写映射文件,只需要在接口文件中利用注解的方式编写SQL语句并实现结果映射。而且可在同一项目中混合使用XML方式和注解方式快速提高开发效率;两者之间的移植也非常简单。常用注解概述@Select@Sel......
  • MyBatis
    1、对MyBatis的理解1、Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。2、MyBatis可以使用XML或注解来配置......
  • SpringBoot整合mybatisplus总结
      最近学习了一些springboot的知识后,开始了对于mybatis的学习,在运用这个框架方面,我选择跟随网上的资料进行整合,遇到了一些问题,并通过自己的查阅资料以及探索,解决了问题。下面概括一下mybatisplus框架的作用,以及应用,并对我学习时遇到的一些问题进行说明。  MyBatis-Plus(简......
  • Spring与MyBatis集成中遇到的问题
    1、依赖版本问题描述在进行Spring框架于MyBatis框架集成时需要使用xml文件装配sqlSessionFactory为bean,从而自动获取sqlSession。遇到了sqlSessionFactory装配失败的问题报错信息Causedby:org.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwi......
  • MyBatis动态SQL
    MyBatis动态SQL在项目的开发中,编码人员经常需要根据不同的条件拼接SQL语句。在组拼SQL语句的过程中除了实现核心功能以外还需要处处小心,时时警惕确保不遗漏必要的标点符号、空格以及关键字。动态SQL常用标签如下:<if/><where/><choose/>、<when/>、<otherwise/><set/><trim/......