目录
1/2/3 Statement 和 Preparedstatement 的区别
4 读取properties配置文件
5 数据库连接池
6 利用数据库连接池连接数据库
1 使用Statement执行含有动态信息的SQL语句时有几个不足:
1.1 由于需要将动态数据拼接到SQL语句中,这导致程序复杂度高,容易出错
1.2 拼接的数据若含有SQL语法内容就会导致拼接后的SQL语法含义改变而出现SQL注入攻击
1.3 当大批量执行语义相同,但是含有动态数据的SQL时效率很差
2 使用Statement执行SQL语句不好的原因
2.1 当执行一条SQL语句发送到数据库时,数据库先将该SQL解析并生成一个执行计划(这个过程会消耗资源和性能),如果多次执行一样的SQL语句,数据库会重用执行计划,但是若多次执行语义相同但是含有动态数据的SQL时,数据库会生成不同的执行计划,严重影响数据库的开销
2.2 例如
执行 SELECT * FROM userifo_fury 生成一个执行计划再次执行SELECT * FROM userifo_fury 就会重用上面的执行计划(因为这是静态的SQL语句
但是,执行INSERT INTO userifo VALUES(1, 'JACK','122314','141234@QQ.COM','FURY',15600) ) 生成一个执行计划,再执行执行INSERT INTO userifo VALUES(2, 'rose','122314','141234@QQ.COM','FURY',15600)由于内容不同,会再次生成另外一个执行计划,若执行1000次上述情况的INSERT,数据库会产生1000个执行计划,这样就严重影响了数据库的效率
因此,Statement只适合执行静态的SQL语句,不适合执行动态的SQL语句
3 利用PreparedStatement代替Statement
编写简单
没有SQL注入问题
批量执行语义相同的SQL语句会重用执行计划
package cn.xiangxu.entity; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = -5109978284633713580L; private Integer id;
private String name;
private String pwd;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(Integer id, String name, String pwd) {
super();
this.id = id;
this.name = name;
this.pwd = pwd;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", pwd=" + pwd + "]";
} }
user表对应的实体类
package testJDBC; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List; import org.junit.Test; import cn.xiangxu.entity.User; public class TestCase {
@Test
public void test01() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver"); // 加载数据库驱动 conn = DriverManager.getConnection( // 初始化连接对象
"jdbc:mysql://localhost:3306/test", "root", "182838"); String sql = "SELECT * FROM user WHERE pwd = ? "; // 拼接SQL语句,位置参数用?代替 ps = conn.prepareStatement(sql); // 初始化预编译执行对象 ps.setString(1, "182838"); // 设置SQL语句中的位置位置参数(注意:是从1开始数不是从0开始数) rs = ps.executeQuery(); // 执行SQL语句 List<User> users = new ArrayList<User>(); // 创建一个集合来存放记录对象
while(rs.next()) { // 遍历结果集
// System.out.println("====================");
// System.out.println(rs.getInt("id"));
// System.out.println(rs.getString("name"));
// System.out.println(rs.getString("pwd"));
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPwd(rs.getString("pwd"));
users.add(user); // 向集合中添加元素
} System.out.println(users); // 打印输出集合
for(User user : users) {
System.out.println(user);
} // 释放资源
rs.close();
ps.close();
conn.close(); } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ps != null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} } }
使用预编译Statement的实例
4 利用Properties对象读取properties配置文件中的信息
4.1 Properties继承了Hashtable类,Properties对象也是使用键值对的方式来保存数据,但是Properties对象的键和值都是字符串类型
class Properties extends Hashtable<Object,Object>
4.2 Properties 类中的主要方法
4.2.1 public synchronized void load(InputStream inStream) throws IOException
将properties属性文件的文件输入流加载到Properties对象
4.2.2 public void store(OutputStream out, String comments) throws IOException
将Properties对象中的属性列表保存到输出流文件中
注意:第二个参数表示注释信息(注意:properties文件中不能用中文),在注释信息后面会自动添加一个时间信息
注意:新创建的文件在项目的根目录下面(问题:为什么在eclipse中没有,但是到文件夹中却能找到???)
4.2.3 public String getProperty(String key)
获取属性值,参数是属性的键
4.2.4 public synchronized Object setProperty(String key, String value)
修改属性值,参数1是属性的键,参数2是属性的新值
4.3 案例
要求:读取properties配置文件总的属性值,将读取到的属性值进行修改后保存到另外一个properties配置文件中
package cn.xiangxu.entity; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Properties; public class Test {
public static void main(String[] args) {
try {
Properties prop = new Properties(); // 创建Properties对象 // prop.load(new FileInputStream("config.properties")); // 使用这种方式时,配置文件必须放在项目的根目录下
InputStream is = Test.class.getClassLoader().getResourceAsStream("config/config.properties"); // 读取属性文件 prop.load(is); // 加载属性列表 Iterator<String> it=prop.stringPropertyNames().iterator(); // 将配置文件中的所有key放到一个可迭代对象中
while(it.hasNext()){ // 利用迭代器模式进行迭代
String key=it.next(); // 读取下一个迭代对象的下一个元素
System.out.println(key+":"+prop.getProperty(key)); // 根据key值获取value值(获取属性信息)
} is.close(); // 关闭输入流,释放资源 FileOutputStream oFile = new FileOutputStream("b.properties", true);//创建一个输出流文件,true表示追加打开
prop.setProperty("maxactive", "33"); // 修改属性信息
prop.store(oFile, "zhe shi yi ge xin de shu xing pei zhi wen jian."); // 将Properties对象中的内容放到刚刚创建的文件中去
oFile.close(); // 关闭输出流,释放资源 } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
读取属性配置文件信息
等待读取的properties配置文件的位置如下图所示
5 数据库连接池
5.1 什么是数据库连接池
程序启动时就创建足够多的数据库连接,并将这些连接组成一个连接池,由程序自动地对池中的连接进行申请、使用、释放
5.2 数据库连接池的运行机制
》程序初始化时创建连接池
》需要操作数据库时向数据库连接池申请一个可用的数据库连接
》使用完毕后就将数据库连接还给数据库连接池(注意:不是关闭连接,而是交给连接池)
》整个程序退出时,断开所有连接,释放资源(即:管理数据库连接池的那个线程被杀死后才关闭所有的连接)
5.3 数据库连接池的编程步骤
5.3.1 导包
5.3.2 声明ThreadLocal、BasicDataSource成员变量(注意:这两个成员变量是静态的)
5.3.3 在静态代码块中实例化那两个成员变量,并通过Properties对象读取配置文件信息,利用这些配置文件信息给BasicDataSource对象进行初始化处理
5.3.4 编写创建连接静态方法
利用BasicDataSource对象实例化一个连接对象
将这个连接对象放到ThreadLocal对象中
5.3.5 编写释放连接静态方法
从ThreadLocal对象中获取连接对象
清空ThreadLocal对象
判断连接对象是否释放
6 利用数据库连接池操作数据库
项目结构图
# zhe shi zhu shi , yi ban bu yong zhong wen
# deng hao liang bian mei you kong ge, mo wei mei you fen hao
# hou mian bu neng you kong ge
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=182838
maxActive=100
maxWait=3000
properties配置文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.xiangxu</groupId>
<artifactId>testJDBC</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
</project>
maven依赖文件
package cn.xiangxu.tools; import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties; import org.apache.commons.dbcp.BasicDataSource; public class DBUtil {
/*
* ThreadLocal用于线程跨方法共享数据使用
* ThreadLocal内部有一个Map, key为需要共享数据的线程本身,value就是其需要共享的数据
*/
private static ThreadLocal<Connection> tl; // 声明一个类似于仓库的东西
private static BasicDataSource dataSource; // 声明一个数据库连接池对象 // 静态代码块,在类加载的时候执行,而且只执行一次
static {
tl = new ThreadLocal<Connection>(); // 实例化仓库对象
dataSource = new BasicDataSource(); // 实例数据库连接池对象 Properties prop = new Properties(); // 创建一个Properties对象用(该对象可以用来加载配置文件中的属性列表)
InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("config/mysql.properties"); // 读取配置文件信息
try {
prop.load(is); // 加载配置文件中的属性列表 String driverClassName = prop.getProperty("driverClassName"); // 获取属性信息
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
Integer maxActive = Integer.parseInt(prop.getProperty("maxActive"));
Integer maxWait = Integer.parseInt(prop.getProperty("maxWait")); dataSource.setDriverClassName(driverClassName); // 初始化数据库连接池(即:配置数据库连接池的先关参数)
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setMaxActive(maxActive);
dataSource.setMaxWait(maxWait); is.close(); // 关闭输入流,释放资源
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } /**
* 创建连接对象(注意:静态方法可以直接通过类名来调用)
* @return 连接对象
* @throws Exception
*/
public static Connection getConnection() throws Exception {
try {
Connection conn = dataSource.getConnection(); // 创建连接对象(利用数据库连接池进行创建)
tl.set(conn); // 将连接对象放到仓库中
return conn;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw e;
}
} /**
* 关闭连接对象(注意:静态方法可以通过类名直接调用)
* @throws Exception
*/
public static void closeConnection() throws Exception {
Connection conn = tl.get(); // 从仓库中取出连接对象
tl.remove(); // 清空仓库
if(conn != null) { // 判断连接对象是否释放资源
try {
conn.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw e;
}
}
} }
数据库连接池类
package testJDBC; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import org.junit.Test; import cn.xiangxu.tools.DBUtil; public class TestDBUtil {
@Test
public void test01() {
try {
Connection conn = DBUtil.getConnection(); // 创建连接对象
String sql = "SELECT * FROM user "; // 拼接SQL语句
PreparedStatement ps = conn.prepareStatement(sql); // 创建执行对象
ResultSet rs = ps.executeQuery(sql); // 执行SQL语句
while(rs.next()) { // 遍历结果集
System.out.println(rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally { // 关闭连接,释放资源
try {
DBUtil.closeConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
数据库连接池的应用