Statement 和 PreparedStatement 都是JDBC中用于执行静态SQL语句并返回其生成的结果的对象,他们之间的关系是什么?在使用上有哪些差别?
JDBC访问数据库的过程
在利用JDBC访问数据库的过程中,本质上我们还是执行sql语句来对数据库表数据进行操作,JDBC是连接java程序和数据库之间的一座桥梁,承担着连接数据库,执行sql,并返回执行结果的任务。
JDBC访问数据库的过程如下:
在获取到与数据库之间的连接之后,我们需要创建Statement对象,来执行我们编写的SQL语句,并返回相应结果,对于查询语句来说,还需要一个ResultSet对象来接收查询结果集;对于增删改来说,本质上都是对数据表的更新操作,是没有返回结果集的。
Statement操作数据表的弊端
(1)sql存在字符串拼接操作,比较繁琐
(2)存在SQL注入问题(安全漏洞)
案例演示:
假设需要进行用户登录验证,将控制台输入的账号密码和数据库表中存在的用户信息进行比对,用户存在且密码正确,则显示登陆成功。
还是如下用户登录功能,目的是检测用户输入的账号密码是否存在,存在则返回一个user对象,并打印登录信息。
数据库用户表数据如下:
当输入如下内容时,数据库明显不存在对应用户,但是却返回了user对象
原因,控制台输入后拼接的sql为
可以看到以上sql在拼接之后,'1'='1'
条件始终为真,那么就会绕过身份验证,返回所有用户的数据,因为在识别sql的过程中错误的解析了用户输入。
SQL注入问题的解决办法
对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了。
PreparedStatement
PreparedStatement 是继承自 Statement的一个子接口,是对Statement的一个拓展。
PreparedStatement 提供了一种更安全和更有效的方式来执行参数化查询。
PreparedStatement 使用占位符(?)来表示参数,然后将参数值与查询分离开来,通过占位符的标记,程序在执行的过程中可以明确的识别占位符的位置就是一个参数,避免了sql拼接带来的错误识别问题,这样可以防止 SQL 注入攻击。
执行效率对比
在传入sql创建PreparedStatement 对象时,会进行预编译,DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
而statement对象中,即使是相同操作,但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义,数据库不会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次,效率较低。
除此之外,在操作BLOB类型的数据时,必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。
BLOB类型可以用来存储各种类型的二进制数据,例如图像、音频、视频、文档等。
小结
Statement 适用于静态查询和简单查询,没有动态设置参数的需求。
PreparedStatement 适用于需要设置参数的查询和重复执行的查询,且拥有更好的安全性,性能也更好。
在实际使用过程中,推荐使用PreparedStatement来对数据库进行操作,避免sql注入带来的数据安全问题,同时,在执行多次动态操作时,PreparedStatement的效率更高。