【MyBatis 深入学习】MyBatis中${} 以及#{} 不同

MyBatis中${} 以及#{} 不同

1. 概述

从今天开始 让我们了解下关于MyBatis的使用细节,以及底层原理。
同时 我们也会从JDBC的发展史来说。

2. JDBC 详解

2.1 概述

JDBC(Java DataBase Connectivity, Java数据库连接) ,是一种用于执行SQL语句的Java API,为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成

简单来说就是Java 实现了一套关于JDBC相关接口,可以理解为是JDBC规范。数据库厂商为了能在Java中使用其语言,必须实现这套规范。大致过程如下图:

【MyBatis 深入学习】MyBatis中${} 以及#{} 不同_第1张图片

其实我们平常使用的jar包mysql-connector-java 就是这种实现。

2.2 JDBC 分类

在JDBC中大体分为以下几个类以及接口

  • DriverManager类 作用:管理各种不同的JDBC驱动
  • Connection接口
  • Statement接口和PreparedStatement接口
  • ResultSet接口

【MyBatis 深入学习】MyBatis中${} 以及#{} 不同_第2张图片

2.3 MySQL8 小细节

我们一般在SpringBoot中连接MySQL 会出现如下代码:

spring:
  jackson:
  datasource:
    url: jdbc:mysql://localhost:3306/personal_saas?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

但是 如果我们将属性driver-class-name 删除后,项目也是可以正常运行的,这是为什么呢???

因为我们在加载类com.mysql.cj.jdbc.Driver 的时候,执行的是如下代码:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

当类字节被被加载到内存的时候,静态代码块也会一起被执行的。同时也是会执行这个代码DriverManager.registerDriver(new Driver());

但是类字节码被加载到内存的过程以及被MySQL8替代了

【MyBatis 深入学习】MyBatis中${} 以及#{} 不同_第3张图片

2.4 Statement 以及 PrepareStatement

此部分要重点关注了因为会牵扯到今天的主题${} 以及#{} 不同

我们先从代码的角度来看下,通过上述两种方式执行,在写法上有什么不同呢??

Statement

connection = DriverManager.getConnection(url, user,password);
statement = connection.createStatement();
String sql="select * from account where username ='"+username+"' and password ='"+pwd+"'";
resultSet = statement.executeQuery(sql);

PrepareStatement

connection = DriverManager.getConnection(url, user,password);
/*
* 1使用PreparedStatement语句对象防止注入攻击
* 2PreparedStatement 可以使用 ? 作为参数的占位符
* 3使用?作为占位符,即使是字符串和日期类型,也不使用单独再添加 ''
* 4connection.createStatement();获得的是普通语句对象 Statement
* 5connection.prepareStatement(sql);可以获得一个预编译语句对象PreparedStatement
* 6如果SQL语句中有?作为参数占位符号,那么要在执行CURD之前先设置参数
* 7通过set***(问号的编号,数据) 方法设置参数
* */
String sql="select * from account where username = ? and password = ?";
preparedStatement = connection.prepareStatement(sql);//这里已经传入SQL语句
//设置参数
preparedStatement.setString(1,username );
preparedStatement.setString(2,pwd );
resultSet = preparedStatement.executeQuery();// 这里不需要再传入SQL语句

通过上述的代码描述,我们可以得到以下几个结论:

相同点

  1. 都可以作为传递sql参数的一种手段。sql语句本身跟参数会交给MySQL,返回对应的结果。

不同点

  1. 通过createStatment 执行的话,内部就是使用了拼接字符串的方式来执行sql。如果被有心人利用的话,会产生SQL注入的问题

  2. 通过prepareStatement执行的话。

    • 参数的部分会被作为占位符来使用。例如:select * from account where username = ? and password = ?
    • 通过函数setXXX 设置参数的时候,参数会被转义,意思是如果参数中带有引号等,会被转义
    • 使用此API,有条件使用预编译的优化手段。
  3. 所以使用prepareStatement 是可以防止SQL注入攻击

3. MySQL 预编译

【MyBatis 深入学习】MyBatis中${} 以及#{} 不同_第4张图片

我们需要手动开启预编译模式:

值得注意的是,我们的Connector/J 5.0.5及之后useServerPrepStmts默认false,就是默认没有开启预编译,之前默认为true, cachePrepStmts 一直默认为false,需要我们手动设置才可以启用预编译,在开启预编译的同时要同时开启预编译缓存才能带来些许的性能提升

"jdbc:mysql://localhost:3306/mydb?*****&useServerPrepStmts=true&cachePrepStmts=true"; 

4. ${} 以及#{} 不同

其实${} 以及#{} 不同 相当于Statement 以及PrepareStatement不同。

相同点

  • 在MyBatis 中都是通过service层 到 mapper.xml 传递参数的一种手段

不同点

  • ${} 底层是基于字符串拼接来实现的,所以很容易发生SQL注入攻击
  • #{} 是动态SQL 的一种手段。是基于PrepareStatement 来实现的
    • 安全性高,可以避免SQL注入
    • 简单不繁琐,不用进行字符串拼接
    • 可以开启预编译手段,性能高,用在执行多个相同数据库DML操作时,可以减少sql语句的编译次数

5. 结论

其实上面说了很多废话,都是为了引出第4点。 也算是一种知识的铺垫吧。好了废话不多说了,今天的分享就到这里了。

你可能感兴趣的:(MyBatis,Spring,Java,mybatis,学习,java)