经过对前面代码的分析,会发现如下几个问题:java
Url、User、Password直接在代码中定义,若是数据库服务器稍做变更,怎么办?mysql
一个项目基本针对一个底层数据库,难道每次操做数据库,都要注册一次驱动程序嘛?是否能够只注册一次?sql
获取数据库链接时,每次都须要Url、User、Password,一旦改动其中一个数据,意味着要修改全部此处的代码。数据库
释放资源,每次数据库操做后,都须要释放资源,难道每次操做后都要写三次try close catch代码嘛?缓存
若是要解决上面的几个问题,那么就要对刚才的代码实现封装,而且把数据库的配置放到配置文件(Properties)中,具体作法以下:服务器
新建jdbc.properties,ide
内容以下:性能
[plain] view plain copyurl
#数据库链接配置 spa
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/lcma?characterEncoding=utf-8
user=root
password=iflytek
新建一个JdbcUtil类,实现代码以下:
[java] view plain copy
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 JdbcUtil {
private static String url;
private static String user;
private static String password;
static{
//使用properties加载属性文件
Properties prop = new Properties();
try {
InputStream is = JdbcUtil.class.getClassLoader().getResourceAsStream("com/iflytek/jdbc.properties");
prop.load(is);
//注册驱动(获取属性文件中的数据)
String driverClassName = prop.getProperty("driverClassName");
Class.forName(driverClassName);
//获取属性文件中的url,username,password
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
} catch (Exception e) {
e.printStackTrace();
}
}
//获取数据库链接
public static Connection getConnection(){
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//释放资源
public static void close(Connection conn, Statement stat, ResultSet rs){
if(conn != null){
try {conn.close();} catch (SQLException e) {e.printStackTrace();}
}
if(stat != null){
try {stat.close();} catch (SQLException e) {e.printStackTrace();}
}
if(rs != null){
try {rs.close();} catch (SQLException e) {e.printStackTrace();}
}
}
}
在加载配置文件的时候使用了静态代码块,代表类一加载,配置文件就会立马加载,属性被保存在静态变量中(url,user,password)。
获取数据库链接和释放资源都采用了静态方法,经过类直接调用改方法,实现了公用代码的封装,下降的代码的耦合性。
这时候咱们再来看JDBC获取数据库数据代码:
[java] view plain copy
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try{
//经过JdbcUtil获取数据库连接
conn = JdbcUtil.getConnection();
stat = conn.createStatement();
rs = stat.executeQuery("select * from student");
while(rs.next()){
System.out.println(rs.getString("name"));
}
}catch(Exception e){
e.printStackTrace();
}finally {
//经过JdbcUtil关闭资源
JdbcUtil.close(conn, stat, rs);
}
经过如今的代码能够看出,代码简介了不少,没有出现容易出错的配置,获取链接,加载配置文件,关闭资源咱们也不须要关心,大大下降了代码的耦合性,提升了代码重用性。
封装事后的代码仍是有些问题,就是采用了Statement对数据库操做,若是如今SQL语句须要传入变量,只有采用拼接的方式,这样作有不少缺点,例如拼接容易出错或容易产生SQL注入等。
经过PreparedStatement对数据库操做能够解决Statement带来的缺点,PreparedStatement和Statement区别以下:
Statement的缺点:
一样的SQL语句,每次都要发送,不能进行有效的缓存。
拼接SQL字符串很是容易出现错误。
不能防止恶意数据,易产生SQL注入。
升级后的新接口PreparedStatement(推荐):
预编译SQL语句,并进行有效的缓存,性能更好。
容许使用问号占位符参数,而且该参数必须得到值后才能够执行。
无需拼接SQL语句。
问号占位符参数:INSERTINTO User(id,name,age,birthday)VALUES(?,?,?,?);
来看一下经过PreparedStatement对数据库操做的代码:
[java] view plain copy
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
try{
//经过JdbcUtil获取数据库连接
conn = JdbcUtil.getConnection();
stat = conn.prepareStatement("select * from student where name like ? and age = ? ");
stat.setString(1, "%小%");
stat.setInt(2, 22);
rs = stat.executeQuery();
while(rs.next()){
System.out.println(rs.getString("name"));
}
}catch(Exception e){
e.printStackTrace();
}finally {
//经过JdbcUtil关闭资源
JdbcUtil.close(conn, stat, rs);
}
}
注意如下两点:
1.问号占位符不能加引号。
2.占位符参数设置值从下标从1开始。
转载url:http://blog.csdn.net/mlc1218559742/article/details/52216895