按照前一篇的内容写完JDBC之后,会发现在JDBC的类中内容实在过多,而且许多代码可以重复使用。那么是否可以将这些代码整合到一个类里面呢?当然是可以的。
一、JDBC类代码工具类的实现
首先我们先想一下,在JDBC 的类中,哪些代码是可以重复使用的。。。
1. 每个JDBC中都需要 注册驱动,创建连接和释放资源,所以将这些内容整合到一个工具类中,可以减少代码的复杂度。
2. 在这个JDBCUtils.java 的工具类中,其中driverClass,url,username,password 这些字符串可能都会随着环境的不同而发生改变,那么如果每次环境一变,我们都选择去改变源代码,这肯定是不可取得,这时候我们就可以使用属性文件(xxxx.properties),
这样就可以减少未来我们要修改配置的麻烦。
注:这是我在属性文件中编写的一些小问题
在.properties的文件中 格式是 key = value
无论在key值上还是在value值上,都是不需要 " "的,不然会出错,还有在每句语句的后面不需要写 ; 不然也会出错。
package com.zhouxin.jdbc.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCUtils {
private static final String driverClass;
private static final String url;
private static final String username;
private static final String password;
//properties文件读取过程.
static {
//加载属性文件并解析:
Properties prop = new Properties();
//通常情况下使用类的加载器的方式进行获取属性文件的输入流:
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
prop.load(is);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
driverClass = prop.getProperty("driverClass");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
}
/**
* 注册数据库驱动
* @throws ClassNotFoundException
*/
public static void loadDriver() throws ClassNotFoundException {
Class.forName(driverClass);
}
/**
* 创建连接
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
loadDriver();
Connection conn = DriverManager.getConnection(url,username,password);
return conn;
}
/**
*
*/
public static void release(Statement stmt ,Connection conn) {
if(stmt!= null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
stmt = null;
}
if(conn!= null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs,Statement stmt,Connection conn) {
if(rs!= null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rs = null;
}
if(stmt!= null) {
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
stmt = null;
}
if(conn!= null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
conn = null;
}
}
}
在写完这些后,直接在JDBC类中调用就可以了,这样能提高代码可读和降低复杂度。
二、JDBC的SQL注入漏洞
1. 引起的原因:
在SQL语句的动态拼装过程的。传入的字符串(参数)和原SQL语句组合,形成一句完整的SQL语句时,但由于传入的值的不同而发生了不同的语义。
例如: 假设 正确的用户名密码为 username : aaa password : 123
String sql = "select * from user where username = ' "+username + " ' and password = ' " + password + " ' ";
此时传入username = aaa ' -- password :asdfasdf 因为 -- 在数据库中是注释的意思,当传入这个uername 之后就会到导致 在后面的sql 全部都失效了,所以这个语句照样能从数据库中读取出数据,那么此时我们就可以登录成功了。
这样的漏洞在我们的项目中是万万不能出现的。
2. 解决方法
用PreparedStatement 对象替换 Statement 对象,避免SQL注入的问题出现
Statement会频繁的编译SQL,可能会导致数据库缓冲区的溢出,而PreparedStatement可以对sql进行预编译,提高数据库执行的效率。在PreparedStatement中的SQL参数,可以使用占位符( ? )的形式进行替换,简化sql语句的编写。
String sql = "select * from user where username = ? and password = ? " ;
PreparedStatement pstmt = connection.preparedStatement(sql);
// 第一个参数,代表的是第几个占位符 ,第二个参数,则是这个位置的填入的值
pstmt.setString(1,username);
pstmt.setString(2,password);
ResultSet rs = pstmt.executeQuery();