DRP之旅第四站--问题驱动学习

来源:互联网 发布:sql 创建触发器 编辑:程序博客网 时间:2024/05/18 00:08

          这一切还要从一个错误说起...

         在进行drp的过程中,需要进行相关的jdbc操作,而在学习的过程中遇到了以下的错误.


          ---------------------------------------------------错误再现-------------------------------------------------------


无效的列索引

非所有变量都已经绑定

java.sql.SQLException: ORA-01008:并非所有变量都已绑定

atoracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)

以上的错误很明显是关于数据库的错误,通过调试和查找发现问题根源时SQL中的变量没有正确的赋值.如果是使用plSql的话通常会应为以下两个原因报错:

1sql语句中的变量前多了:

2、变量传入类型不对或者是乱码

而我出现的错误时因为第二个.PreparedStatement提交更新命令的时候我添加了一个sql参数,如下:

pstmt.executeUpdate(sql);

出现以上问题的根本原因是自己对PreparedStatement的用法并不是很熟悉.


--------------------------------------------------认识PreparedStatement---------------------------------------------


       问题已经出现,并且已经解决,但是脚步不能在此停止,而是继续往前走,解开一层一层的面纱.进一步来认识PreparedStatement.

       javaPreparedStatement接口继承了Statement.通过以前巨人们的经验总结:在进行JDBC应用编程的时候尽量用PreparedStatement来代替Statement.因为两者相比,PreparedStatementStatement有更多的优点.

 

优点1:提高代码可读性和可维护性.

 

     我们具体编程的时候会发现,但从代码量上,PreparedStatement会比Statement多出个那么几行的代码,但也就是这多出的几行代码便提高了代码的可理解性和维护性.

//Statement示例Statementstmt=null;stmt.executeUpdate("insert into t_user(user_id, user_name,password, contact_tel) values('"+user.getUserId()+"','"+user.getUserName()+"',"+user.getPassword()+",'"+user.getContactTel()+"')"); //PreparedStatement示例        //获取连接Connection conn = null;//推荐使用PreparedStatement,有利于性能提成PreparedStatement pstmt = null;pstmt=conn.prepareStatement("insert into t_user (user_id,user_name, password, contact_tel) values(?, ?, ?, ?, ?)");//添加参数pstmt.setString(1, user.getUserId());pstmt.setString(2, user.getUserName());pstmt.setString(3, user.getPassword());pstmt.setString(4, user.getContactTel());//提交更新信息pstmt.executeUpdate();


通过以上两个示例可以看出,Statement虽然从总的代码量上来看"简洁"不少,但是由于大量的拼接字符串,无论可读,理解还是修改,都要比PreparedStatement下中的?占位符和随后的变量赋值要优秀很多.

 

优点2:提高性能.

 

       PreparedStatement实例包含已编译的 SQL语句。而数据库会最大的对编译语句通过重复调用提供性能优化,当一个sql语句被数据库的编译器编译后的执行代码被缓存下来,如果下次再调用的是相同的编译语言的话,数据库就不会再进行编译,只需要做的是将参数传入到编译过的语句执行代码中.相对比Statement,因为每次操作的数据相同的可能性非常小.即使是同一类型的语句但不同的数据使得整个语句匹配度很小,可以通过下面两个例子来进行解释.

insert into t_user (user_id, user_name) values ('01','name');

insert into t_user (user_id, user_name) values ('02','name');

insert into t_user (user_id, user_name) values ('03','name');

 

    上例中由于user_id的具体值进行变化由'01''02'再到'03',其它值没有进行变化,从而导致整条语句的内容不一致.以上只是两个例子,如果加入循环,循环一万次,那么数据库要对这一万个语句进行编译,所带来的性能后果可想而知,PreparedStatement便可以解决上述问题.具体的值作为参数传递,相同的语句只需要编译一次.如果在大批量插入数据的情况下可以大大的提高性能.

 

优点3:提高安全性.

 

    安全性这一点也是非常重要的一点.在说数据库安全之前需要了解一下什么是SQL注入:是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.

看下面的例子:

String sql = "select * from t_user where user_name= '"+userName+"' and password='"+passWord+"'";

     上面是一条通过用户名和密码来进入系统的一条Sql语句,如果我们把密码参数passWord改成[' or 'true' ='true]传进来,而不用管用户名是什么,会造成什么结果?

String sql = "select * from t_user where user_name= '"+'任何用户名称'+"' and password=' ' or 'true' ='true'";

      而其中的'true'='true'是成立的,所以可以通过验证进入系统.这样便通过一个简单的Sql注入进入系统.更严重的话加入一些个破坏性的Sql语句,会对安全造成不可估量的损失,例如将passWord参数改为[';droptable t_user;]那么sql语句就变成:

String sql = "select * from t_user where user_name= '"+'任何用户名称'+"' and password=' ';drop table t_user;

        人家把你的用户表都删了,到时候哭都没地儿哭.

刚才我们提到,PreparedStatement是使用的预编译语句,那么传来的内容不会和原有的SQL语句产生关系,这样就避免了Sql注入的问题.

 

----------------------------------------------sql注入现实生活示例--------------------------------------------------------------


在网络上看到了下面很有创意的照片

 

 

 

这是一个有技术含量的号牌遮挡。我们先不说其是不是能奏效,不过,这个创意相当的N啊。当你驾车通过某些路口时,被摄像头捕捉到你的车牌,通过OCR变成文本,然后插入数据库,于是,上图的这个车牌就成了SQL注入。(不要以为车牌的OCR技术还不行,这项目技术已经非常成熟了,无论是国内还是国外)。

 

 

        以上提到了PreparedStatement的三个主要优点,但并不是否认Statement,Statement也有其适应的场景:例如当执行语句中没有参数的情况.但总体上通过对比PreparedStatement还是有非常大的优势.

        以上的过程是通过一个数据库的错误引出了Statement相关的知识,通过PreparedStatementStatement对比分析做了进一步了解.当然,还可以再对其进行进一步深究.


总结:通过问题驱动而主动去获得知识的过程所产生的收获要远大于被动的去吸收一些知识.

 

计算机生成了可选文字: 查找问题分析对比总结继续学习收获思考
原创粉丝点击