目录
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语句会重用执行计划
1 packagecn.xiangxu.entity;2
3 importjava.io.Serializable;4
5 public class User implementsSerializable {6
7 private static final long serialVersionUID = -5109978284633713580L;8
9 privateInteger id;10 privateString name;11 privateString pwd;12 publicUser() {13 super();14 //TODO Auto-generated constructor stub
15 }16 publicUser(Integer id, String name, String pwd) {17 super();18 this.id =id;19 this.name =name;20 this.pwd =pwd;21 }22 @Override23 public inthashCode() {24 final int prime = 31;25 int result = 1;26 result = prime * result + ((id == null) ? 0: id.hashCode());27 returnresult;28 }29 @Override30 public booleanequals(Object obj) {31 if (this ==obj)32 return true;33 if (obj == null)34 return false;35 if (getClass() !=obj.getClass())36 return false;37 User other =(User) obj;38 if (id == null) {39 if (other.id != null)40 return false;41 } else if (!id.equals(other.id))42 return false;43 return true;44 }45 publicInteger getId() {46 returnid;47 }48 public voidsetId(Integer id) {49 this.id =id;50 }51 publicString getName() {52 returnname;53 }54 public voidsetName(String name) {55 this.name =name;56 }57 publicString getPwd() {58 returnpwd;59 }60 public voidsetPwd(String pwd) {61 this.pwd =pwd;62 }63 @Override64 publicString toString() {65 return "User [id=" + id + ", name=" + name + ", pwd=" + pwd + "]";66 }67
68
69
70 }
user表对应的实体类
1 packagetestJDBC;2
3 importjava.sql.Connection;4 importjava.sql.DriverManager;5 importjava.sql.PreparedStatement;6 importjava.sql.ResultSet;7 importjava.sql.SQLException;8 importjava.util.ArrayList;9 importjava.util.List;10
11 importorg.junit.Test;12
13 importcn.xiangxu.entity.User;14
15 public classTestCase {16 @Test17 public voidtest01() {18 Connection conn = null;19 PreparedStatement ps = null;20 ResultSet rs = null;21 try{22 Class.forName("com.mysql.jdbc.Driver"); //加载数据库驱动
23
24 conn = DriverManager.getConnection( //初始化连接对象
25 "jdbc:mysql://localhost:3306/test", "root", "182838");26
27
28 String sql = "SELECT * FROM user WHERE pwd = ? "; //拼接SQL语句,位置参数用?代替
29
30 ps = conn.prepareStatement(sql); //初始化预编译执行对象
31
32 ps.setString(1, "182838"); //设置SQL语句中的位置位置参数(注意:是从1开始数不是从0开始数)
33
34 rs = ps.executeQuery(); //执行SQL语句
35
36 List users = new ArrayList(); //创建一个集合来存放记录对象
37 while(rs.next()) { //遍历结果集38 //System.out.println("====================");39 //System.out.println(rs.getInt("id"));40 //System.out.println(rs.getString("name"));41 //System.out.println(rs.getString("pwd"));
42 User user = newUser();43 user.setId(rs.getInt("id"));44 user.setName(rs.getString("name"));45 user.setPwd(rs.getString("pwd"));46 users.add(user); //向集合中添加元素
47 }48
49 System.out.println(users); //打印输出集合
50 for(User user : users) {51 System.out.println(user);52 }53
54 //释放资源
55 rs.close();56 ps.close();57 conn.close();58
59 } catch(Exception e) {60 //TODO Auto-generated catch block
61 e.printStackTrace();62 } finally{63 if(rs != null) {64 try{65 rs.close();66 } catch(SQLException e) {67 //TODO Auto-generated catch block
68 e.printStackTrace();69 }70 }71 if(ps != null) {72 try{73 ps.close();74 } catch(SQLException e) {75 //TODO Auto-generated catch block
76 e.printStackTrace();77 }78 }79 if(conn != null) {80 try{81 conn.close();82 } catch(SQLException e) {83 //TODO Auto-generated catch block
84 e.printStackTrace();85 }86 }87 }88
89 }90
91 }
使用预编译Statement的实例
4 利用Properties对象读取properties配置文件中的信息
4.1 Properties继承了Hashtable类,Properties对象也是使用键值对的方式来保存数据,但是Properties对象的键和值都是字符串类型
class Properties extends Hashtable
4.2 Properties 类中的主要方法
4.2.1 public synchronized voidload(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 StringgetProperty(String key)
获取属性值,参数是属性的键
4.2.4 public synchronized Object setProperty(String key, String value)
修改属性值,参数1是属性的键,参数2是属性的新值
4.3 案例
要求:读取properties配置文件总的属性值,将读取到的属性值进行修改后保存到另外一个properties配置文件中
1 packagecn.xiangxu.entity;2
3 importjava.io.FileInputStream;4 importjava.io.FileOutputStream;5 importjava.io.InputStream;6 importjava.util.Iterator;7 importjava.util.Properties;8
9 public classTest {10 public static voidmain(String[] args) {11 try{12 Properties prop = new Properties(); //创建Properties对象13
14 //prop.load(new FileInputStream("config.properties"));//使用这种方式时,配置文件必须放在项目的根目录下
15 InputStream is = Test.class.getClassLoader().getResourceAsStream("config/config.properties"); //读取属性文件
16
17 prop.load(is); //加载属性列表
18
19 Iterator it=prop.stringPropertyNames().iterator(); //将配置文件中的所有key放到一个可迭代对象中
20 while(it.hasNext()){ //利用迭代器模式进行迭代
21 String key=it.next(); //读取下一个迭代对象的下一个元素
22 System.out.println(key+":"+prop.getProperty(key)); //根据key值获取value值(获取属性信息)
23 }24
25 is.close(); //关闭输入流,释放资源
26
27 FileOutputStream oFile = new FileOutputStream("b.properties", true);//创建一个输出流文件,true表示追加打开
28 prop.setProperty("maxactive", "33"); //修改属性信息
29 prop.store(oFile, "zhe shi yi ge xin de shu xing pei zhi wen jian."); //将Properties对象中的内容放到刚刚创建的文件中去
30 oFile.close(); //关闭输出流,释放资源
31
32 } catch(Exception e) {33 //TODO Auto-generated catch block
34 e.printStackTrace();35 }36 }37 }
读取属性配置文件信息
等待读取的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 利用数据库连接池操作数据库
项目结构图
1 # zhe shi zhu shi , yi ban bu yong zhong wen2 # deng hao liang bian mei you kong ge, mo wei mei you fen hao3 # hou mian bu neng you kong ge4 driverClassName=com.mysql.jdbc.Driver5 url=jdbc:mysql://localhost:3306/test6 username=root7 password=1828388 maxActive=1009 maxWait=3000
properties配置文件
1
2 4.0.0
3 cn.xiangxu
4 testJDBC
5 0.0.1-SNAPSHOT
6
7
8 mysql
9 mysql-connector-java
10 5.1.37
11
12
13 junit
14 junit
15 4.12
16
17
18 commons-dbcp
19 commons-dbcp
20 1.4
21
22
23
maven依赖文件
1 packagecn.xiangxu.tools;2
3 importjava.io.IOException;4 importjava.io.InputStream;5 importjava.sql.Connection;6 importjava.sql.SQLException;7 importjava.util.Properties;8
9 importorg.apache.commons.dbcp.BasicDataSource;10
11 public classDBUtil {12 /*
13 * ThreadLocal用于线程跨方法共享数据使用14 * ThreadLocal内部有一个Map, key为需要共享数据的线程本身,value就是其需要共享的数据15 */
16 private static ThreadLocal tl; //声明一个类似于仓库的东西
17 private static BasicDataSource dataSource; //声明一个数据库连接池对象18
19 //静态代码块,在类加载的时候执行,而且只执行一次
20 static{21 tl = new ThreadLocal(); //实例化仓库对象
22 dataSource = new BasicDataSource(); //实例数据库连接池对象
23
24 Properties prop = new Properties(); //创建一个Properties对象用(该对象可以用来加载配置文件中的属性列表)
25 InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("config/mysql.properties"); //读取配置文件信息
26 try{27 prop.load(is); //加载配置文件中的属性列表
28
29 String driverClassName = prop.getProperty("driverClassName"); //获取属性信息
30 String url = prop.getProperty("url");31 String username = prop.getProperty("username");32 String password = prop.getProperty("password");33 Integer maxActive = Integer.parseInt(prop.getProperty("maxActive"));34 Integer maxWait = Integer.parseInt(prop.getProperty("maxWait"));35
36 dataSource.setDriverClassName(driverClassName); //初始化数据库连接池(即:配置数据库连接池的先关参数)
37 dataSource.setUrl(url);38 dataSource.setUsername(username);39 dataSource.setPassword(password);40 dataSource.setMaxActive(maxActive);41 dataSource.setMaxWait(maxWait);42
43 is.close(); //关闭输入流,释放资源
44 } catch(IOException e) {45 //TODO Auto-generated catch block
46 e.printStackTrace();47 }48
49 }50
51 /**
52 * 创建连接对象(注意:静态方法可以直接通过类名来调用)53 *@return连接对象54 *@throwsException55 */
56 public static Connection getConnection() throwsException {57 try{58 Connection conn = dataSource.getConnection(); //创建连接对象(利用数据库连接池进行创建)
59 tl.set(conn); //将连接对象放到仓库中
60 returnconn;61 } catch(Exception e) {62 //TODO Auto-generated catch block
63 e.printStackTrace();64 throwe;65 }66 }67
68 /**
69 * 关闭连接对象(注意:静态方法可以通过类名直接调用)70 *@throwsException71 */
72 public static void closeConnection() throwsException {73 Connection conn = tl.get(); //从仓库中取出连接对象
74 tl.remove(); //清空仓库
75 if(conn != null) { //判断连接对象是否释放资源
76 try{77 conn.close();78 } catch(Exception e) {79 //TODO Auto-generated catch block
80 e.printStackTrace();81 throwe;82 }83 }84 }85
86 }
数据库连接池类
1 packagetestJDBC;2
3 importjava.sql.Connection;4 importjava.sql.PreparedStatement;5 importjava.sql.ResultSet;6
7 importorg.junit.Test;8
9 importcn.xiangxu.tools.DBUtil;10
11 public classTestDBUtil {12 @Test13 public voidtest01() {14 try{15 Connection conn = DBUtil.getConnection(); //创建连接对象
16 String sql = "SELECT * FROM user "; //拼接SQL语句
17 PreparedStatement ps = conn.prepareStatement(sql); //创建执行对象
18 ResultSet rs = ps.executeQuery(sql); //执行SQL语句
19 while(rs.next()) { //遍历结果集
20 System.out.println(rs.getString("name"));21 }22 } catch(Exception e) {23 e.printStackTrace();24 } finally { //关闭连接,释放资源
25 try{26 DBUtil.closeConnection();27 } catch(Exception e) {28 e.printStackTrace();29 }30 }31 }32 }
数据库连接池的应用