从今天开始 让我们了解下关于
MyBatis
的使用细节,以及底层原理。
同时 我们也会从JDBC的发展史来说。
JDBC(Java DataBase Connectivity, Java数据库连接) ,是一种用于执行SQL语句的Java API,为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成
简单来说就是Java 实现了一套关于JDBC
相关接口,可以理解为是JDBC规范。数据库厂商为了能在Java中使用其语言,必须实现这套规范。大致过程如下图:
其实我们平常使用的jar包mysql-connector-java
就是这种实现。
在JDBC中大体分为以下几个类以及接口
DriverManager
类 作用:管理各种不同的JDBC驱动Connection
接口Statement
接口和PreparedStatement
接口ResultSet
接口我们一般在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替代了
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语句
通过上述的代码描述,我们可以得到以下几个结论:
相同点
不同点
通过createStatment
执行的话,内部就是使用了拼接字符串的方式来执行sql。如果被有心人利用的话,会产生SQL注入的问题
通过prepareStatement
执行的话。
select * from account where username = ? and password = ?
setXXX
设置参数的时候,参数会被转义,意思是如果参数中带有引号等,会被转义所以使用prepareStatement
是可以防止SQL注入攻击
的
我们需要手动开启预编译模式:
值得注意的是,我们的Connector/J 5.0.5及之后useServerPrepStmts默认false,就是默认没有开启预编译,之前默认为true, cachePrepStmts 一直默认为false,需要我们手动设置才可以启用预编译,在开启预编译的同时要同时开启预编译缓存才能带来些许的性能提升
"jdbc:mysql://localhost:3306/mydb?*****&useServerPrepStmts=true&cachePrepStmts=true";
其实
${} 以及#{} 不同
相当于Statement 以及PrepareStatement
不同。
相同点
不同点
${}
底层是基于字符串拼接来实现的,所以很容易发生SQL注入攻击#{}
是动态SQL 的一种手段。是基于PrepareStatement
来实现的
其实上面说了很多废话,都是为了引出
第4点
。 也算是一种知识的铺垫吧。好了废话不多说了,今天的分享就到这里了。