JDBCUtils工具类
JDBC简介:
JDBC(Java Database Connectivity),Java数据库连接,是一组使用Java语言操作数据库的规范(访问不同DBMS的底层方法是不一样的,JDBC把访问数据库的方法进行了统一,访问MYSQL、Oracle、DB2等不同数据库的用法几乎一模一样),具体表现在java.sql包下的一组API
主流数据库厂商都提供了操作自己数据库的JDBC实现,具体表现在数据库驱动jar包,比如MySQL数据库提供的mysql-connector-java.jar
对于开发人员来说,需要把驱动包添加到项目的构建路径,然后使用java.sql包下的接口编程
接口提供统一的操作方式,驱动提供具体的实现
JDBC核心API:
- DriverManager 管理驱动、创建数据库连接
- Connection 数据库连接,用来操作数据库
- Statement 表示SQL语句,用来执行SQL语句
- PreparedStatement 表示预编译的带占位符的SQL语句
- ResultSet 查询结果集
加载JDBC驱动:
Java源码中只提供了JDBC的接口API,但是具体的实现是靠不同的数据库厂商.
即,在使用JDBC之前要先加载不同厂商的驱动(具体的实现)
将MYSQL的JDBC驱动mysql-connector-java-*-bin.jar添加到项目的库中。
加载mysql 的 jdbc驱动: Class.forName("com.mysql.jdbc.Driver")
获得和DBMS的连接:Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost/demo1?seUnicode=true&characterEncoding=UTF8", "root", "root")
注意:不要引用错了,mysql驱动jar包中也有一个同名的Connection 。
第一个参数叫“连接字符串”,用于设置连接到哪台服务器(localhost),哪个数据库(demo1),以及其他配置信息(和数据库的编码一致)。连接字符串的格式由不同DBMS的驱动来规定,具体查看驱动的文档;
第2、3个参数为用户名、密码。
JDBCUtils
package com.jdbc.test;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils
{
private static final String drivername;
private static final String dburl;
private static final String dbusername;
private static final String dbpassword;
// 静态代码块,在类第一次使用的时候执行完成static变量赋值
static
{
InputStream inStream = null;
try
{
inStream = JdbcUtils.class.getResourceAsStream("config.properties");
Properties prop = new Properties();
prop.load(inStream);
// 读取配置文件
drivername = prop.getProperty("drivername");
dburl = prop.getProperty("dburl");
dbusername = prop.getProperty("dbusername");
dbpassword = prop.getProperty("dbpassword");
} catch (IOException e)// 把IOException转化为RuntimeException
{
// static中不能抛出checkedException(非RuntimeException)
throw new RuntimeException("加载config.properties出错", e);
} finally
{
if (inStream != null)
{
try
{
inStream.close();
} catch (IOException e)
{
//do nothing
}
}
}
try
{
// 加载jdbc驱动.只用加载一次
// 所以不放到creatConnection中
Class.forName(drivername);
} catch (ClassNotFoundException e)
{
throw new RuntimeException("加载Mysql JDBC驱动失败", e);
}
}
// 对于不知道怎么处理的异常,就抛出,不要"藏着掖着"
public static Connection createConnection() throws SQLException
{
return DriverManager.getConnection(dburl, dbusername, dbpassword);
}
public static int executeUpdate(String sql, Object... parameters) throws SQLException
{
Connection conn=null;
try
{
conn=createConnection();
return executeUpdate(conn,sql,parameters);
}
finally
{
closeQuietly(conn);
}
}
public static int executeUpdate(Connection conn, String sql, Object... parameters) throws SQLException
{
PreparedStatement ps = null;
try
{
ps = conn.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++)
{
// set...都是从1开始的 依次填充sql中的?
ps.setObject(i + 1, parameters[i]);
}
return ps.executeUpdate();
}
/*
* catch(SQLException e) { System.out.println("执行出错"); //如果只是把异常打印出来不算处理
* //这叫"吃异常".应该合理的告诉调用者错了(抛出) }
*/
finally
{
closeQuietly(ps);
// conn用完了不要关,也许人家还有用
}
}
public static ResultSet executeQuery(String sql,Object...parameters) throws SQLException
{
Connection conn= createConnection();
return executeQuery(conn,sql,parameters);
}
public static ResultSet executeQuery(Connection conn,String sql,
Object... parameters)throws SQLException
{
//[注意] 这里不要关闭ps,或者会导致返回出去的ResultSet失效
//外部程序调用时候可以关闭ps,关闭方法:通过rs.getStatement() 先拿到rs关联的Statement
PreparedStatement ps=conn.prepareStatement(sql);
for(int i=0;i<parameters.length;i++)
{
ps.setObject(i+1, parameters[i]);
}
return ps.executeQuery();
}
//PreparedStatement继承自Statement
public static void closeQuietly(Statement stmt)
//public static void closeQuietly(PreparedStatement stmt)
{
if (stmt != null)
{
try
{
stmt.close();
} catch (SQLException e)
{
// do nothing
}
}
}
/**
* 关闭ResultSet以及和其关联的Statement
* @param ResultSet rs
*/
public static void closeResultSetAndStatement(ResultSet rs)
{
Statement stmt=null;
try
{
stmt = rs.getStatement();
}catch(SQLException E)
{
//do nothing
}
finally
{
closeQuietly(rs);
closeQuietly(stmt);
}
}
/**
* 把Result和其对应的Statement以及Connection都关掉
* @param rs
*/
public static void closeAll(ResultSet rs)
{
Statement stmt=null;
Connection conn=null;
try
{
stmt=rs.getStatement();
conn=stmt.getConnection();
}
catch(SQLException e)
{
//do nothing
}
finally
{
//(*) CachedRowSet:是一个离线结果集,也就是把数据保存到客户端,Connection可以关闭 (简化关闭,但是耗内存)
closeQuietly(rs);
closeQuietly(stmt);
closeQuietly(conn);
}
}
public static void closeQuietly(Connection conn)
{
if (conn != null)
{
try
{
conn.close();
} catch (SQLException e)
{
// do nothing
}
}
}
public static void closeQuietly(ResultSet resultSet)
{
if (resultSet != null)
{
try
{
resultSet.close();
} catch (SQLException e)
{
// do nothing
}
}
}
public static void rollback(Connection conn)
{
try
{
conn.rollback();
} catch (SQLException e)
{
// do nothing
}
}
}
SQL注入漏洞
采用sql拼接的方式
1. 容易产生sql注入漏洞(a’ or ‘a’=’a)
2. 执行效率低,每次的拼接结果都将产生一条新的sql语句,需要对其进行词法分析,语法分析等
解决方案:
采用参数化查询安全;效率高(SQL预编译)