PreparedStatement相对于Statement最重要的一个优点就是可以进行SQL预处理,以此防止SQL语句的注入问题。
所谓SQL注入,就是通过把SQL命令插入到用户输入的文本中,最终达到欺骗数据库服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
使用PreparedStatement就可以防止一些SQL注入的问题,保证了一定的安全性,而且PreparedStatement能够使用提供参数的方式来避免拼错SQL语句,在调用PreparedStatement里的setString、setInt等方法的时候,会自动把你提供的参数转换成数据库里的数据类型。
就算没有被恶意的被SQL注入攻击,也可能会出现用户输入的内容碰巧转换成了SQL语句,或者在程序运行过程中出现符号转换的问题,这些都会导致数据库误以为是SQL命令而去执行。
下面我们使用Statement写几句简单的代码模拟一下用户的登录操作:
运行结果:
接着做个简单的小实验,模拟利用简单的SQL注入原理来达到不输入用户和密码也能登录的情况:
代码示例:
运行结果:
从结果可以看到没有输入用户和密码也登录成功了,如果这种漏洞问题出现真正的程序,想想也知道会有多严重。而且这仅仅是一个很简单的例子,借助SQL注入能做到的事情可不止这一点。
现在换成PreparedStatement来进行同样的试验:
代码示例:
运行结果:
从结果可以看到不会因为用户输入的内容是SQL命令或符号,而出现SQL注入的问题了。
和Statement一样PreparedStatement也能够执行数据库的两大类语句,更新语句(DML)和查询语句(DQL),并且也支持批量SQL语句执行。
insert语句代码示例:
运行结果:
数据库:
update语句代码示例:
运行结果:
数据库:
delete语句代码示例:
运行结果:
数据库:
批量SQL语句执行代码示例:
运行结果:
数据库:
在PreparedStatement里设置值的时候可以调用setObject方法,此方法能够自动将你提供的参数转换成相对应的数据库数据类型,也就是说即便你懒得写具体的类型或者不知道提供的值是什么类型的话均可使用这个方法。
代码示例:
运行结果:
我们看一下这个方法的实现代码,就知道此方法是如何实现的和支持哪些类型了:
从数据库中进行文件上传/下载:
我们可以上传文件到数据库中,相对也能从数据库中下载文件,不过很少人会这么做,这种骚操作了解一下记得有这个操作就可以了,一般情况下用不上。
因为把文件存放在数据库要面临以下几个问题:
· 1.对数据库的读/写的速度永远都赶不上文件系统处理的速度
· 2.数据库备份变的巨大,越来越耗时间
· 3.对文件的访问需要穿越你的应用层和数据库层
这后两个是真正的杀手。把图片缩略图存到数据库里?很好,那你就不能使用nginx或其它类型的轻量级服务器来处理它们了。
So给自己行个方便吧,在数据库里只简单的存放一个磁盘上你的文件的相对路径,或者使用S3或CDN之类的服务。
虽然把文件存到数据库中不是什么好的操作,但是为了预防万一或应急情况还是需要了解一下这个操作的:
1.首先创建一张表格使用blob作为文件的数据类型,当然也可以使用其他可以存储文件的类型:
3.查看一下数据库能接收多大的数据,如果要存放的文件大小大于数据库能接收的大小就会报错,使用以下SQL命令查看,单位是字节:
SHOW VARIABLES LIKE '%max_allowed_packet%';
如果要改变mysql数据库的数据接收大小,需要去配置my.ini文件,写上这句代码:
max_allowed_packet = 20M
修改方法:
1) 方法1
可以编辑my.ini来修改(Linux下my.cnf),在[mysqld]段或者mysql的server配置段进行修改。
max_allowed_packet = 20M
在Linux系统中如果找不到my.ini可以通过
mysql --help | grep my.ini
命令去寻找my.ini文件。
2) 方法2
(很妥协,很纠结的办法)
进入mysql server
在mysql 命令行中运行
set global max_allowed_packet = 2*1024*1024*10
然后关闭掉这此mysql server链接,再进入。
show VARIABLES like '%max_allowed_packet%';
查看下max_allowed_packet是否编辑成功
编写Java代码通过JDBC将文件上传到数据库中:
运行结果:
数据库:
接下来我们从数据库中把这个文件下载下来,先使用查询语句先把文件查找出来,然后调用getBinaryStream方法得到一个InputStream 对象,这个方法需要传递一个列的序号,接着使用FileOutputStream把文件写入到磁盘里。
代码示例:
运行结果:
文件:
调用存储过程:
使用CallableStatement 接口可以调用数据库中的存储过程,需要先使用Connection 对象调用prepareCall方法并提供调用存储过程的SQL语句来获得CallableStatement 对象,不过语法和数据库中的调用存储过程的SQL语句有点不一样,需要加上大括号括起来。使用CallableStatement 对象调用registerOutParameter方法并且提供存储过程带出值的类型,就可以获得存储过程的带出值。
代码示例:
运行结果:
本文转自 ZeroOne01 51CTO博客,原文链接:http://blog.51cto.com/zero01/1977008,如需转载请自行联系原作者