JDBC02 利用JDBC連接數據庫【使用數據庫連接池】

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','[email protected]','FURY',15600) ) 生成一個執行計劃,再執行執行INSERT INTO userifo VALUES(2, 'rose','122314','[email protected]','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 stub15     }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     @Override23     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     @Override30     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     @Override64     public String toString() {65         return "User [id=" + id + ", name=" + name + ", pwd=" + pwd + "]";66     }67     68     69 70 }

複製代碼

複製代碼

 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     @Test17     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 block61             e.printStackTrace();62         } finally {63             if(rs != null) {64                 try {65                     rs.close();66                 } catch (SQLException e) {67                     // TODO Auto-generated catch block68                     e.printStackTrace();69                 }70             }71             if(ps != null) {72                 try {73                     ps.close();74                 } catch (SQLException e) {75                     // TODO Auto-generated catch block76                     e.printStackTrace();77                 }78             }79             if(conn != null) {80                 try {81                     conn.close();82                 } catch (SQLException e) {83                     // TODO Auto-generated catch block84                     e.printStackTrace();85                 }86             }87         }88         89     }90     91 }

複製代碼

 

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 block34             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 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

複製代碼

複製代碼

 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>

複製代碼

複製代碼

 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 block46             e.printStackTrace();47         } 
48         49     }50     51     /**52      * 創建連接對象(注意:靜態方法可以直接通過類名來調用)53      * @return 連接對象54      * @throws Exception55      */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 block63             e.printStackTrace();64             throw e;65         }66     }67     68     /**69      * 關閉連接對象(注意:靜態方法可以通過類名直接調用)70      * @throws Exception71      */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 block80                 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     @Test13     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 }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章