JDBC-自定義數據庫工具類(DBService)


 寫在前面的話:
     (1)使用JDBC,必須要使用對應的jar包,該筆記中使用jar包: mysql-connector-java-5.1 .6-bin.jar
     (2)使用連接池,一定要導入對象的數據庫連接池包,該筆記中使用jar包: c3p0-0.9.1.2.jar
               常用連接池:dbcp連接池、c3p0連接池、druid連接池
 
 
第一版:不用數據庫連接池:
 
  1 package cn;
  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.ResultSetMetaData;
  8 import java.sql.SQLException;
  9 import java.util.ArrayList;
 10 import java.util.HashMap;
 11 import java.util.List;
 12 import java.util.Map;
 13  
 14 public class SqlService {
 15  
 16        // 四大金剛
 17       String driver = "com.mysql.jdbc.Driver" ;// 驅動名稱
 18       String url = "jdbc:mysql:///mao" ;// 連接字符串
 19       String username = "root" ;// 用戶名
 20       String password = "123456" ;// 密碼
 21  
 22        // 三劍客
 23       Connection con = null ;// 連接對象
 24       PreparedStatement pstmt = null ;// 語句對象
 25       ResultSet rs = null ;// 結果集對象
 26  
 27        /**
 28        * 獲得連接對象
 29        *
 30        * @return 連接對象
 31        * @throws ClassNotFoundException
 32        * @throws SQLException
 33        */
 34        public Connection getConnection() throws ClassNotFoundException,
 35                   SQLException {
 36             Class. forName( driver);
 37              con = DriverManager.getConnection( url , username , password );
 38              return con ;
 39       }
 40  
 41        /**
 42        * 關閉三劍客
 43        *
 44        * @throws SQLException
 45        */
 46        public void close(ResultSet rs, PreparedStatement pstmt, Connection con) {
 47  
 48              try {
 49                    if (rs != null)
 50                         rs.close();
 51                    if (pstmt != null)
 52                         pstmt.close();
 53                    if (con != null)
 54                         con.close();
 55             } catch (SQLException e) {
 56                    // TODO: handle exception
 57                   e.printStackTrace();
 58             }
 59       }
 60  
 61        /**
 62        * 執行更新
 63        *
 64        * @param sql
 65        *            傳入的預設的 sql語句
 66        * @param params
 67        *            問號參數列表
 68        * @return 影響行數
 69        */
 70        public int execUpdate(String sql, Object[] params) {
 71              try {
 72                    this .getConnection();// 獲得連接對象
 73                    this .pstmt = this. con .prepareStatement(sql);// 獲得預設語句對象
 74  
 75                    if (params != null) {
 76                          // 設置參數列表
 77                          for (int i = 0; i < params. length; i++) {
 78                                // 因為問號參數的索引是從1開始,所以是i+1,將所有值都轉為字符串形式,好讓setObject成功運行
 79                                this .pstmt .setObject(i + 1, params[i] + "" );
 80                         }
 81                   }
 82  
 83                    return this .pstmt .executeUpdate(); // 執行更新,並返回影響行數
 84  
 85             } catch (ClassNotFoundException | SQLException e) {
 86                    // TODO Auto-generated catch block
 87                   e.printStackTrace();
 88             } finally {
 89                    this .close( this. rs, this. pstmt , this .con );
 90             }
 91              return 0;
 92       }
 93  
 94        /**
 95        * 執行查詢
 96        *
 97        * @param sql
 98        *            傳入的預設的 sql語句
 99        * @param params
100        *            問號參數列表
101        * @return 查詢后的結果
102        */
103        public List<Map<String, Object>> execQuery(String sql, Object[] params) {
104  
105              try {
106                    this .getConnection();// 獲得連接對象
107                    this .pstmt = this. con .prepareStatement(sql);// 獲得預設語句對象
108  
109                    if (params != null) {
110                          // 設置參數列表
111                          for (int i = 0; i < params. length; i++) {
112                                // 因為問號參數的索引是從1開始,所以是i+1,將所有值都轉為字符串形式,好讓setObject成功運行
113                                this .pstmt .setObject(i + 1, params[i] + "" );
114                         }
115                   }
116  
117                    // 執行查詢
118                   ResultSet rs = pstmt .executeQuery();
119  
120                   List<Map<String, Object>> al = new ArrayList<Map<String, Object>>();
121  
122                    // 獲得結果集元數據(元數據就是描述數據的數據,比如把表的列類型列名等作為數據)
123                   ResultSetMetaData rsmd = rs.getMetaData();
124  
125                    // 獲得列的總數
126                    int columnCount = rsmd.getColumnCount();
127  
128                    // 遍歷結果集
129                    while (rs.next()) {
130                         Map<String, Object> hm = new HashMap<String, Object>();
131                          for (int i = 0; i < columnCount; i++) {
132                                // 根據列索引取得每一列的列名,索引從1開始
133                               String columnName = rsmd.getColumnName(i + 1);
134                                // 根據列名獲得列值
135                               Object columnValue = rs.getObject(columnName);
136                                // 將列名作為key,列值作為值,放入 hm中,每個 hm相當於一條記錄
137                               hm.put(columnName, columnValue);
138                         }
139                          // 將每個 hm添加到al中, al相當於是整個表,每個 hm是里面的一條記錄
140                         al.add(hm);
141                   }
142  
143                    return al;
144  
145             } catch (ClassNotFoundException | SQLException e) {
146                    // TODO Auto-generated catch block
147                   e.printStackTrace();
148             } finally {
149                    this .close( this. rs, this. pstmt , this .con );
150             }
151              return null ;
152       }
153 }

 

代碼解釋:
一:為什么直接使用 “Class. forName( driver); ”注冊驅動
          ①原始注冊驅動寫法:
   
1   // 注冊驅動:告訴 java 去連接哪種數據庫
2      //DriverManager :驅動管理類,負責管理驅動,創建連接對象
3      DriverManager. registerDriver( new com.mysql.jdbc.Driver());
static void
registerDriver ( Driver  driver)
           DriverManager 注冊給定驅動程序。
參數:Driver=====來哦子欲你要連接的數據庫(如果連接orcale,來自於oracle,如果連接mysql,來自於mysql)
備注:查詢  Drive源碼
 
 1 public class Driver extends NonRegisteringDriver implements java.sql.Driver {
 2      // ~ Static fields/initializers
 3      // ---------------------------------------------
 4  
 5      //
 6      // Register ourselves with the DriverManager
 7      //
 8      static {
 9         try {
10            java.sql.DriverManager. registerDriver( new Driver());
11        } catch (SQLException E) {
12             throw new RuntimeException( "Can't register driver!" );
13        }
14     }

 

可以看到:  java.sql.DriverManager. registerDriver( new Driver());是寫在靜態代碼塊中,只要調用Driver這個方法,就會自動運行這段代碼,這樣會造成“注冊兩次驅動”
因此,我們可以改成如下版本:
  
          ②改進版
   
1   // 注冊驅動:告訴 java去連接哪種數據庫
2      //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
3      new com.mysql.jdbc.Driver();

 

    但是這樣寫:
      1、靜態初始化已經new了一個Driver對象,注冊到DriverManager中去,在此再創建一個Driver對象則是完全沒有必要的,浪費空間;
      2、不夠靈活,如果需要換數據庫的話,需要改動代碼,此時最好把要變得內容改成字符串的形式,可以由變量讀取外部配置文件進行傳入(由此可以解釋,為什么源代碼中要直接寫成靜態代碼塊)     
        
           ③最終版   
     
1  // 注冊驅動:告訴 java 去連接哪種數據庫
2       //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
3       //new com.mysql.jdbc.Driver();
4       Class. forName( "com.mysql.jdbc.Driver");

 

     最終版的好處:
     1、Class.forName()的作用是要求JVM查找並加載指定的類,也就是說JVM會執行該類的靜態代碼段。
     2、既然在靜態初始化器中已經進行了注冊,所以我們在使用JDBC時只需要Class.forName(XXX.XXX)就可以了
 
二:獲得連接對象
 
con = DriverManager.getConnection( url , username , password );
url:連接到某一個具體的數據庫
user:數據庫的用戶名
password:數據庫用戶名對應的密碼
 
url = "jdbc:mysql://localhost:3306/mao"

 

jdbc:     jdbc協議
mysql:     jdbc子協議(表示mysql協議)
 
備注:
     url其他寫法一:
url = jdbc:mysql://localhost:3306/mao?userUnicode=true&characterEncoding=utf8
//?userUnicode=true&characterEncoding=utf8
//加上這個可以解決數據庫的亂碼問題

 

/*
*1、存數據時:
*數據庫在存放項目數據的時候會先用UTF-8格式將數據解碼成字節碼,然后再將解碼后的字節碼重新使用GBK編碼存*放到數據庫中。
*
*2.取數據時:
*     在從數據庫中取數據的時候,數據庫會先將數據庫中的數據按GBK格式解碼成字節碼,然后再將解碼后的字節*碼重新按UTF-8格式編碼數據,最后再將數據返回給客戶端。
*
*/
     url其他寫法二:
//如果連接的mysql是在本機,並且mysql的端口是3306,比如:
url:"jdbc:mysql://localhost:3306/mao"
//那么url可以簡寫為:
url:"jdbc:mysql:///mao"

 

 
三、語句對象
 
   
1   // 創建語句對象Statement
2      Statement stmt = con.createStatement();
3  
4      //創建預設語句對象PreparedStatement
5      String sql = "update class set classDesc = ? where id = ?";
6      PreparedStatement pstmt = con.prepareStatement(sql);

 

     接下來是重點:
      1:Statement的缺陷:
               ①容易被sql注入
 
sql注入:在頁面表單中輸入sql的片段。達到串改程序中sql語句。
 
 
正常情況:
 
select * from user where username='zhangsan' and password = '123456';
 
 
當sql被注入之后的語句:
 
select * from user where username='zhangsan' and password = '' or '1'='1'
                ②效率不高
               Statement每執行一次,sql進行一次編譯。即使每次的sql語句格式都一樣
     
     2:PreparedStatement的優勢
               ①防止sql注入
                    PreparedStatement會對特殊字符進行轉譯,並通過參數形式設置到語句中,而不再是變量拼湊成sql語句
                 ②預編譯功能
                 相同格式的sql語句只被編譯一次,后一條格式相同的sql語句運行時,只需改變參數的值即可
 
 
第二版:使用數據庫連接池
代碼前備注:
     本工具類代碼使用c3p0連接池
c3p0-config.xml
 1 <? xml version ="1.0" encoding= "UTF-8" ?>
 2 < c3p0-config>
 3        <!-- 默認配置,c3p0框架默認加載這段默認配置 -->
 4        < default-config>
 5              <!-- 配置JDBC 四個基本屬性 -->
 6              < property name ="driverClass" > com.mysql.jdbc.Driver</ property >
 7              < property name ="jdbcUrl" > jdbc:mysql:///數據庫名</ property >
 8              < property name ="user" > 數據庫用戶名</ property >
 9              < property name ="password" > 數據庫密碼</ property >
10        </ default-config> <!-- This app is massive! -->
11 </ c3p0-config>

 

 
 
  1 package cn.service;
  2  
  3 import java.sql.Connection;
  4 import java.sql.PreparedStatement;
  5 import java.sql.ResultSet;
  6 import java.sql.ResultSetMetaData;
  7 import java.sql.SQLException;
  8 import java.util.ArrayList;
  9 import java.util.HashMap;
 10 import java.util.List;
 11 import java.util.Map;
 12  
 13 import org.junit.Test;
 14  
 15 import com.mchange.v2.c3p0.ComboPooledDataSource;
 16 import com.mchange.v2.c3p0.util.TestUtils;
 17  
 18 public class DBService {
 19  
 20     // 三劍客
 21     Connection con = null;// 連接對象
 22     PreparedStatement pstmt = null;// 語句對象
 23     ResultSet rs = null;// 結果集對象
 24  
 25     /**
 26      * 獲得連接對象
 27      *
 28      * @return 連接對象
 29      * @throws ClassNotFoundException
 30      * @throws SQLException
 31      */
 32     public Connection getConnection() throws ClassNotFoundException,
 33             SQLException {
 34  
 35         // 創建c3p0連接池
 36         ComboPooledDataSource ds = new ComboPooledDataSource("itcast");
 37         // 通過連接池對象創建連接
 38         con = ds.getConnection();
 39         return con;
 40     }
 41  
 42     /**
 43      * 關閉三劍客
 44      *
 45      * @throws SQLException
 46      */
 47     public void close(ResultSet rs, PreparedStatement pstmt, Connection con) {
 48  
 49         try {
 50             if (rs != null)
 51                 rs.close();
 52             if (pstmt != null)
 53                 pstmt.close();
 54             if (con != null)
 55                 con.close();
 56         } catch (SQLException e) {
 57             // TODO: handle exception
 58             e.printStackTrace();
 59         }
 60     }
 61  
 62     /**
 63      * 執行更新
 64      *
 65      * @param sql
 66      *            傳入的預設的sql語句
 67      * @param params
 68      *            問號參數列表
 69      * @return 影響行數
 70      */
 71     public int execUpdate(String sql, Object[] params) {
 72         try {
 73             this.getConnection();// 獲得連接對象
 74             this.pstmt = this.con.prepareStatement(sql);// 獲得預設語句對象
 75  
 76             if (params != null) {
 77                 // 設置參數列表
 78                 for (int i = 0; i < params.length; i++) {
 79                     // 因為問號參數的索引是從1開始,所以是i+1,將所有值都轉為字符串形式,好讓setObject成功運行
 80                     this.pstmt.setObject(i + 1, params[i] + "");
 81                 }
 82             }
 83  
 84             return this.pstmt.executeUpdate();// 執行更新,並返回影響行數
 85  
 86         } catch (ClassNotFoundException | SQLException e) {
 87             // TODO Auto-generated catch block
 88             e.printStackTrace();
 89         } finally {
 90             this.close(this.rs, this.pstmt, this.con);
 91         }
 92         return 0;
 93     }
 94  
 95     /**
 96      * 執行查詢
 97      *
 98      * @param sql
 99      *            傳入的預設的sql語句
100      * @param params
101      *            問號參數列表
102      * @return 查詢后的結果
103      */
104     public List<Map<String, Object>> execQuery(String sql, Object[] params) {
105  
106         try {
107             this.getConnection();// 獲得連接對象
108             this.pstmt = this.con.prepareStatement(sql);// 獲得預設語句對象
109  
110             if (params != null) {
111                 // 設置參數列表
112                 for (int i = 0; i < params.length; i++) {
113                     // 因為問號參數的索引是從1開始,所以是i+1,將所有值都轉為字符串形式,好讓setObject成功運行
114                     this.pstmt.setObject(i + 1, params[i] + "");
115                 }
116             }
117  
118             // 執行查詢
119             ResultSet rs = pstmt.executeQuery();
120  
121             List<Map<String, Object>> al = new ArrayList<Map<String, Object>>();
122  
123             // 獲得結果集元數據(元數據就是描述數據的數據,比如把表的列類型列名等作為數據)
124             ResultSetMetaData rsmd = rs.getMetaData();
125  
126             // 獲得列的總數
127             int columnCount = rsmd.getColumnCount();
128  
129             // 遍歷結果集
130             while (rs.next()) {
131                 Map<String, Object> hm = new HashMap<String, Object>();
132                 for (int i = 0; i < columnCount; i++) {
133                     // 根據列索引取得每一列的列名,索引從1開始
134                     String columnName = rsmd.getColumnName(i + 1);
135                     // 根據列名獲得列值
136                     Object columnValue = rs.getObject(columnName);
137                     // 將列名作為key,列值作為值,放入hm中,每個hm相當於一條記錄
138                     hm.put(columnName, columnValue);
139                 }
140                 // 將每個hm添加到al中,al相當於是整個表,每個hm是里面的一條記錄
141                 al.add(hm);
142             }
143  
144             return al;
145  
146         } catch (ClassNotFoundException | SQLException e) {
147             // TODO Auto-generated catch block
148             e.printStackTrace();
149         } finally {
150             this.close(this.rs, this.pstmt, this.con);
151         }
152  
153         return null;
154     }
155  
156 }
157  

 

 
 
 
     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM