JDBC02 利用JDBC连接数据库【使用数据库连接池】

JAVA学习网 2017-07-08 13:33:05

目录

  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 package cn.xiangxu.entity;
 2 
 3 import java.io.Serializable;
 4 
 5 public class User implements Serializable {
 6 
 7     private static final long serialVersionUID = -5109978284633713580L;
 8     
 9     private Integer id;
10     private String name;
11     private String pwd;
12     public User() {
13         super();
14         // TODO Auto-generated constructor stub
15     }
16     public User(Integer id, String name, String pwd) {
17         super();
18         this.id = id;
19         this.name = name;
20         this.pwd = pwd;
21     }
22     @Override
23     public int hashCode() {
24         final int prime = 31;
25         int result = 1;
26         result = prime * result + ((id == null) ? 0 : id.hashCode());
27         return result;
28     }
29     @Override
30     public boolean equals(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     public Integer getId() {
46         return id;
47     }
48     public void setId(Integer id) {
49         this.id = id;
50     }
51     public String getName() {
52         return name;
53     }
54     public void setName(String name) {
55         this.name = name;
56     }
57     public String getPwd() {
58         return pwd;
59     }
60     public void setPwd(String pwd) {
61         this.pwd = pwd;
62     }
63     @Override
64     public String toString() {
65         return "User [id=" + id + ", name=" + name + ", pwd=" + pwd + "]";
66     }
67     
68     
69 
70 }
user表对应的实体类
 1 package testJDBC;
 2 
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 import java.sql.PreparedStatement;
 6 import java.sql.ResultSet;
 7 import java.sql.SQLException;
 8 import java.util.ArrayList;
 9 import java.util.List;
10 
11 import org.junit.Test;
12 
13 import cn.xiangxu.entity.User;
14 
15 public class TestCase {
16     @Test
17     public void test01() {
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<User> users = new ArrayList<User>(); // 创建一个集合来存放记录对象
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 = new User();
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<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配置文件中

 1 package cn.xiangxu.entity;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.FileOutputStream;
 5 import java.io.InputStream;
 6 import java.util.Iterator;
 7 import java.util.Properties;
 8 
 9 public class Test {
10     public static void main(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<String> 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 wen 
2 # deng hao liang bian mei you kong ge, mo wei mei you fen hao
3 # hou mian bu neng you kong ge
4 driverClassName=com.mysql.jdbc.Driver
5 url=jdbc:mysql://localhost:3306/test
6 username=root
7 password=182838
8 maxActive=100
9 maxWait=3000
properties配置文件
 1 <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">
 2   <modelVersion>4.0.0</modelVersion>
 3   <groupId>cn.xiangxu</groupId>
 4   <artifactId>testJDBC</artifactId>
 5   <version>0.0.1-SNAPSHOT</version>
 6   <dependencies>
 7       <dependency>
 8           <groupId>mysql</groupId>
 9           <artifactId>mysql-connector-java</artifactId>
10           <version>5.1.37</version>
11       </dependency>
12       <dependency>
13           <groupId>junit</groupId>
14           <artifactId>junit</artifactId>
15           <version>4.12</version>
16       </dependency>
17       <dependency>
18           <groupId>commons-dbcp</groupId>
19           <artifactId>commons-dbcp</artifactId>
20           <version>1.4</version>
21       </dependency>
22   </dependencies>
23 </project>
maven依赖文件
 1 package cn.xiangxu.tools;
 2 
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5 import java.sql.Connection;
 6 import java.sql.SQLException;
 7 import java.util.Properties;
 8 
 9 import org.apache.commons.dbcp.BasicDataSource;
10 
11 public class DBUtil {
12     /*
13      * ThreadLocal用于线程跨方法共享数据使用
14      * ThreadLocal内部有一个Map,  key为需要共享数据的线程本身,value就是其需要共享的数据
15      */
16     private static ThreadLocal<Connection> tl; // 声明一个类似于仓库的东西
17     private static BasicDataSource dataSource; // 声明一个数据库连接池对象
18     
19     // 静态代码块,在类加载的时候执行,而且只执行一次
20     static {
21         tl = new ThreadLocal<Connection>(); // 实例化仓库对象
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      * @throws Exception
55      */
56     public static Connection getConnection() throws Exception { 
57         try {
58             Connection conn = dataSource.getConnection(); // 创建连接对象(利用数据库连接池进行创建)
59             tl.set(conn); // 将连接对象放到仓库中
60             return conn; 
61         } catch (Exception e) {
62             // TODO Auto-generated catch block
63             e.printStackTrace();
64             throw e;
65         }
66     }
67     
68     /**
69      * 关闭连接对象(注意:静态方法可以通过类名直接调用)
70      * @throws Exception
71      */
72     public static void closeConnection() throws Exception {
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                 throw e;
82             }
83         }
84     }
85 
86 }
数据库连接池类
 1 package testJDBC;
 2 
 3 import java.sql.Connection;
 4 import java.sql.PreparedStatement;
 5 import java.sql.ResultSet;
 6 
 7 import org.junit.Test;
 8 
 9 import cn.xiangxu.tools.DBUtil;
10 
11 public class TestDBUtil {
12     @Test
13     public void test01() {
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 }
数据库连接池的应用

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

阅读(807) 评论(0)