深入理解PreparedStatement和Statement

本文详细介绍了PreparedStatement的预编译功能,包括预编译的好处、如何在MySQL中开启预编译、cachePrepStmts参数的作用以及PreparedStatement与Statement的区别。通过预编译可以提高SQL执行效率并增强安全性,但在某些持久层框架中并未充分利用预编译。
摘要由CSDN通过智能技术生成

预编译

  • MySQL数据库服务器的预编译功能在4.1之后才支持预编译功能的,4.1之前的版本是不支持预编译的。如果数据库服务器不支持预编译功能时,并且使用PreparedStatement开启预编译功能是会抛出异常的。
  • MySQL驱动5.0.5以后的版本默认PreparedStatement是关闭预编译功能的,所以需要我们手动开启。而之前的JDBCMySQL驱动版本默认是开启预编译功能的。

预编译的好处:

大家平时都使用过JDBC中的PreparedStatement接口,它有预编译功能。什么是预编译功能呢?它有什么好处呢?
当客户发送一条SQL语句给服务器后,服务器总是需要校验SQL语句的语法格式是否正确,然后把SQL语句编译成可执行的函数,最后才是执行SQL语句。其中校验语法,和编译所花的时间可能比执行SQL语句花的时间还要多。
注意:可执行函数存储在MySQL服务器中,并且当前连接断开后,MySQL服务器会清除已经存储的可执行函数。
如果我们需要执行多次insert语句,但只是每次插入的值不同,MySQL服务器也是需要每次都去校验SQL语句的语法格式,以及编译,这就浪费了太多的时间。如果使用预编译功能,那么只对SQL语句进行一次语法校验和编译,所以效率要高。

在PreparedStatement中开启预编译功能

我们一般用的PreparedStatement并没有用到预编译功能的,只是用到了防止sql注入攻击的功能。防止sql注入攻击的实现是在PreparedStatement中实现的,和服务器无关。笔者在源码中看到,PreparedStatement对敏感字符已经转义过了。

在我们以前写项目的时候,貌似都没有注意是否开启PreparedStatement的预编译功能,以为它一直都是在使用的。下面在PreparedStatement中开启预编译功能:

  • 设置MySQL连接URL参数:useServerPrepStmts=true,如下所示。
    jdbc:mysql://localhost:3306/mybatis?&useServerPrepStmts=true
  • 这样才能保证mysql驱动会先把SQL语句发送给服务器进行预编译,然后在执行executeQuery()时只是把参数发送给服务器。

注意:
我们设置的是MySQL连接参数,目的是告诉MySQL JDBC的PreparedStatement使用预编译功能(5.0.5之后的JDBC驱动版本需要手动开启,而之前的默认是开启的),不管我们是否使用预编译功能,MySQL Server4.1版本以后都是支持预编译功能的。

cachePrepStmts参数

当使用不同的PreparedStatement对象来执行相同的SQL语句时,还是会出现编译两次的现象,这是因为驱动没有缓存编译后的函数key,导致二次编译。如果希望缓存编译后函数的key,那么就要设置cachePrepStmts参数为true。例如:jdbc:mysql://localhost:3306/mybatis?useServerPrepStmts=true&cachePrepStmts=true
**注意:**每次使用PreparedStatement对象后都要关闭该PreparedStatement对象流,否则预编译后的函数key是不会缓存的。

MySQL执行预编译

MySQL执行预编译分为如三步:
1.执行预编译语句,例如:prepare showUsersByLikeName from ‘select * from user where username like ?’;
2.设置变量,例如:set @username=’%小明%’;
3.执行语句,例如:execute showUsersByLikeName using @username;

如果需要再次执行myfun,那么就不再需要第一步,即不需要再编译语句了:
1.设置变量,例如:set @username=’%小宋%’;
2.执行语句,例如:execute showUsersByLikeName using @username;

总结:

  • 所以到了这里我的疑惑都解开了,PreparedStatement的预编译是数据库进行的,编译后的函数key是缓存在PreparedStatement中的,编译后的函数是缓存在数据库服务器中的。预编译前有检查sql语句语法是否正确的操作。只有数据库服务器支持预编译功能时,JDBC驱动才能够使用数据库的预编译功能,否则会报错。预编译在比较新的JDBC驱动版本中默认是关闭的,需要配置连接参数才能够打开。在已经配置好了数据库连接参数的情况下,Statement对于MySQL数据库是不会对编译后的函数进行缓存的,数据库不会缓存函数,Statement也不会缓存函数的key,所以多次执行相同的一条sql语句的时候,还是会先检查sql语句语法是否正确,然后编译sql语句成函数,最后执行函数。
  • 对于PreparedStatement在设置参数的时候会对参数进行转义处理。
  • 因为PreparedStatement已经对sql模板进行了编译,并且存储了函数,所以PreparedStatement做的就是把参数进行转义后直接传入参数到数据库,然后让函数执行。这就是为什么PreparedStatement能够防止sql注入攻击的原因了。
  • PreparedStatement的预编译还有注意的问题,在数据库端存储的函数和在PreparedStatement中存储的key值,都是建立在数据库连接的基础上的,如果当前数据库连接断开了,数据库端的函数会清空,建立在连接上的PreparedStatement里面的函数key也会被清空,各个连接之间的预编译都是互相独立的。

在持久层框架中存在的问题

很多主流持久层框架(MyBatis,Hibernate)其实都没有真正的用上预编译,预编译是要我们自己在参数列表上面配置的,如果我们不手动开启,JDBC驱动程序5.0.5以后版本 默认预编译都是关闭的。

所以我们要在参数列表中配置,例如:jdbc:mysql://localhost:3306/mybatis?&useServerPrepStmts=true&cachePrepStmts=true

注意:
在MySQL中,既要开启预编译也要开启缓存。因为如果只是开启预编译的话效率还没有不开启预编译效率高,大家可以做一下性能测试,其中性能测试结果在这篇博客中有写到,探究mysql预编译,而在MySQL中开启预编译和开启缓存,其中的查询效率和不开启预编译和不开启缓存的效率是持平的。这里用的测试类是PreparedStatement。

使用PreparedStatement的Batch功能

Update大量的数据时, 先Prepare一个INSERT语句再多次的执行, 会导致很多次的网络连接. 要减少JDBC的调用次数改善性能, 你可以使用PreparedStatement的AddBatch()方法一次性发送多个查询给数据库. 例如, 让我们来比较一下下面的例子.

为了区分 “Statement、PreparedStatement、PreparedStatement + 批处理” 这三者之间的效率,下面的示例执行过程都是在数据库表t1中插入1万条记录,并记录出所需的时间(此时间与电脑硬件有关)。实验结果如下:
1.使用Statement对象 用时312.预编译PreparedStatement 用时143.使用PreparedStatement + 批处理   用时485毫秒***
-------------------------------------------------------
1.使用Statement对象
使用范围:当执行相似SQL(结构相同,具体值不同)语句的次数比较少
优点:语法简单
缺点:采用硬编码效率低&#x
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值