通过对前面代码的分析,会发现以下几个问题:
Url、User、Password直接在代码中定义,如果数据库服务器稍作变动,怎么办?
一个项目基本针对一个底层数据库,难道每次操作数据库,都要注册一次驱动程序嘛?是否可以只注册一次?
获取数据库连接时,每次都需要Url、User、Password,一旦改动其中一个数据,意味着要修改所有此处的代码。
释放资源,每次数据库操作后,都需要释放资源,难道每次操作后都要写三次try close catch代码嘛?
如果要解决上面的几个问题,那么就要对刚才的代码实现封装,并且把数据库的配置放到配置文件(Properties)中,具体做法如下:
新建jdbc.properties,
内容如下:
[plain] view plain copy
#数据库连接配置
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