JDBC學習總結 -- JDBC 快速入門 教程

先感謝前人的勞動成果,html

本教程中含有多處指路,請注意篩選.java

詳細請閱讀官方文檔:https://docs.oracle.com/javase/tutorial/jdbc/basics/index.html (不建議直接上手)mysql

          https://www.journaldev.com/2681/jdbc-tutorialsql

前置知識:基本SQL 語句,JAVA SE基礎數據庫

 

JDBC 目錄:apache

1.JDBC概念編程

2.JDBC處理SQL語句的過程(建表,查表) api

  •  創建與數據庫的鏈接
  •  建立 statement
  •  執行 Query
  •  處理 結果集(ResultSets)

 3.專題介紹服務器

  • ResultSet 與 Statement 中的 Prestatement,CallableStatement 詳細介紹
  • 批處理的使用 + 刪除表格
  • DataSource 與鏈接池,事務 (DBCP,簡易鏈接池)

4.結尾併發

  • ORM
  • DAO封裝本身的JDBC

 

1. JDBC的概念  

JDBC(Java DataBase Connectivity)

是一種數據庫鏈接技術,
能實現Java程序對各類數據庫的訪問。由一組使用Java語言編寫的類和接口(JDBC API)
組成,他們位於java.sql以及javax.sql中。

推薦解釋指路 中的 (二.JDBC是什麼)

 

做用:

1. 與數據庫創建聯繫.

2. 將編寫好的SQL語句發送至數據庫執行

3. 對數據庫返回的結果進行操做

 

JDBC API

  • java.sql.*
  • javax.sql.*

  鏈接數據庫相關的:

    DriverManager

    用URL鏈接數據庫,

       在4.0版本以前要進行加載.  即用映射加載驅動 Class.forName("com.mysql.jdbc.Driver");

    DataSource  

   相對於DriverManager類,DataSource更適合用於獲取操做數據的Connection對象,

   它相對靈活,而且支持 JNDI ,能夠爲servlet容器提供鏈接池 

   (能夠去找DBCP的資料,這裏涉及到了 The Java EE Tutorial.)

 鏈接池解釋推薦指路,後續會解釋DataSource的使用.

    Connection接口

   負責鏈接數據庫並擔任傳送數據的任務。

  數據庫SQL相關的:

   Statement接口

  負責執行SQL語句。

  • PreparedStatement: 預編譯,可讓SQL擁有參數,防止SQL注入
  • CallableStatement:  能夠Statement基礎上執行存儲過程

   ResultSet接口

  負責保存Statement執行後所產生的查詢結果。

   RowSet接口:   

  RowSet 比 ResultSet 功能更多,更易使用,全部的RowSet對象都繼承於 ResultSet.

 

2.JDBC處理SQL語句的過程(建表,查表)

使用JDBC 的步驟

  •  創建 與數據庫的鏈接 (其中包括用
  •  建立 statement
  •  執行 Query
  •  處理 結果集(ResultSets)
  •  關閉 鏈接

這裏,咱們使用MySQL 進行演示,

若是你使用是JAVA DB,他自帶了JDBC

首先去MySQL官方下載 MYSQL 和 Connector/J.

 

(如下內容請根據本身環境進行配置)(官方教程使用的是 Apache ANT)

咱們要使用 Connector/J

這裏咱們使用Eclipse - 在項目窗口右擊Properties

 以後選擇 JAVA Build Path->中的Libraries ,而後點擊右邊的 Add External JARS 將Connector/J 中的JAR文件導入

 

這樣就能夠開始編寫了咱們的JDBC了.

 

1. 創建與數據庫的鏈接,這裏使用DriverManager

 咱們須要用到DBMS的 URL 進行鏈接

  • 通常mysql的URL : jdbc:mysql://localhost:3306/
  • JDBC URL: jdbc:derby:testdb;create=true
  •  

在用正確的用戶密碼,服務器等等參數鏈接數據庫URL後,

DriverManager 會返回一個Connection,

 

咱們編寫一個方法來獲取相關的Connection 

//根據本身的數據庫進行修改
public class JDBCTutorials {
    String userName = "root"; //用戶名
    String password = "password";//用戶密碼
    String dbms = "mysql";//數據庫管理系統名稱
    String serverName = "localhost";//服務器名稱
    String portNumber = "3306";//服務器接口
    String Atribute = "serverTimezone=UTC"
            + "&characterEncoding=utf-8"
            + "&useSSL=false";//時區與SLL鏈接方式禁用
    String dbName = "crashcourse";//須要訪問的數據庫名稱,能夠爲空,可是得本身用USE語句
    //jdbc:mysql://localhost:3306/
    //jdbc:derby:testdb;create=true
    public Connection getConnection() throws SQLException{
        Connection conn = null;
        Properties connectionPros = new Properties();//新建一個Propeties容器
        connectionPros.put("user", userName);
        connectionPros.put("password", password);
        if(this.dbms.equals("mysql")) {
            conn = DriverManager.getConnection("jdbc:" + this.dbms + "://" + this.serverName +
                                                    ":" + this.portNumber + "/" + this.dbName + "?" + this.Atribute,connectionPros);//獲取鏈接

            //conn.setCatalog(this.dbName);
        } else if (this.dbms.equals("derby")) {
            conn = DriverManager.getConnection("jdbc:" + this.dbms + ":"+ this.dbName + ":" 
                                                + ";create=true",connectionPros);
        }
        System.out.println("Connection DBMS Successfully");
        return conn;
    }
}

更改相關的參數後,若是出現

"Connection DBMS Successfully"

就說明鏈接數據庫管理系統成功了.返回的Connection就要用到了, 

接下來咱們須要將SQL語句運用起來.

 

2. 建立Statement , 和執行Query, ResultSet使用(建表 查表)

接下來咱們須要使用的表格以下:

創建這個表格的SQL語句:

 

//---------------------------------------創建表格-----------------------------------------
CREATE TABLE customers
(
  cust_id      int       NOT NULL AUTO_INCREMENT,
  cust_name    char(50)  NOT NULL ,
  cust_address char(50)  NULL ,
  cust_city    char(50)  NULL ,
  cust_state   char(5)   NULL ,
  cust_zip     char(10)  NULL ,
  cust_country char(50)  NULL ,
  cust_contact char(50)  NULL ,
  cust_email   char(255) NULL ,
  PRIMARY KEY (cust_id)
) ENGINE=InnoDB;

//---------------------------------------填充數據-----------------------------------------
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com');
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)
VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse');
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES(10003, 'Wascals', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'rabbit@wascally.com');
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES(10004, 'Yosemite Place', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Y Sam', 'sam@yosemite.com');
INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)
VALUES(10005, 'E Fudd', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'E Fudd');

 

那麼咱們怎麼使用上面的SQL語句?

首先,咱們要知道以前編寫的 getConnection 方法返回了一個connetion

咱們如今要再執行SQL語句的方法中使用它, SQL語句也須要先儲存到一個String中

 

    public void createTable(Connection con) throws SQLException{ String createSQL = "CREATE TABLE customers\r\n" + //存儲SQL語句
                "(\r\n" + 
                "  cust_id      int       NOT NULL AUTO_INCREMENT,\r\n" + 
                "  cust_name    char(50)  NOT NULL ,\r\n" + 
                "  cust_address char(50)  NULL ,\r\n" + 
                "  cust_city    char(50)  NULL ,\r\n" + 
                "  cust_state   char(5)   NULL ,\r\n" + 
                "  cust_zip     char(10)  NULL ,\r\n" + 
                "  cust_country char(50)  NULL ,\r\n" + 
                "  cust_contact char(50)  NULL ,\r\n" + 
                "  cust_email   char(255) NULL ,\r\n" + 
                "  PRIMARY KEY (cust_id)\r\n" + 
                ") ENGINE=InnoDB;"; try(Statement statement = con.createStatement()){//用connection 建立一個statement
            statement.executeUpdate(createSQL);//編譯SQL語句
        }catch (Exception e) { System.out.println(e); } }

這樣,建表的SQL語句就完成了.

 

爲何要把statement的創建寫到try中?

   由於JAVA7 提供了自動關閉資源的機制 

executeUpdate是什麼?

   與之對於的是execute,executeQuery

  • executeQuery 只執行查 (select語句)
  • executeUpdate 執行 增刪改  (insert或者update/delete(DML)語句,或者 什麼也不返回的DDL語句)
  • execute 執行 增刪改查

 

咱們創建了表,可是表是空的,咱們應該怎麼樣使用SQL的插入語句?

  原理相似

	public void populateTable(Connection con) throws SQLException{
		try(Statement statement = con.createStatement()){
			statement.executeUpdate("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
					"VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com');");
			statement.executeUpdate("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)\r\n" + 
					"VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse');");
			statement.executeUpdate("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
					"VALUES(10003, 'Wascals', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'rabbit@wascally.com');");
			statement.executeUpdate("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
					"VALUES(10004, 'Yosemite Place', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Y Sam', 'sam@yosemite.com');");
			statement.execute("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)\r\n" + 
					"VALUES(10005, 'E Fudd', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'E Fudd');");//這裏須要使用excute,由於返回多個ResultSet
		}catch (Exception e) {
			System.out.println(e);
		}
	}

 

接着,寫一個查詢表的方法

	public void viewTable(Connection con,String dbName) throws SQLException{//查詢表的功能
		String query =  
				"SELECT * FROM customers;";//SQL語句
		try(Statement statement = con.createStatement()){//建立Statement
			ResultSet resultSet = statement.executeQuery(query);//編譯SQL語句,用ResultSet存儲查詢結果
			while(resultSet.next()) {//換一行
				String custID = resultSet.getString("cust_id");//獲取該行列爲"cust_id"的的數據
				String custName = resultSet.getString("cust_name");
				String custAddress = resultSet.getString("cust_address");
				System.out.println(custID + ", " + custName + ", " + 
									custAddress);
			}
		}catch (SQLException e) {
			System.out.println(e);
		}
	}

注意:

  executeQuery 是隻能執行一條SQL 語句的,要執行多條須要用到批處理

 

3.使用

	public static void main(String[] args) {
		JDBCTutorials jdbcTutorials = new JDBCTutorials();
		try (Connection connection = jdbcTutorials.getConnection()){
			jdbcTutorials.createTable(connection);
			jdbcTutorials.populateTable(connection);
			jdbcTutorials.viewTable(connection,"crashcourse");
		}catch (SQLException e) {
			System.out.println(e);
		}
	}

執行結果:

 

4.關閉步驟

通常步驟是:

} finally {
    if (stmt != null) { stmt.close(); }
}

 不事後來JAVA 7出那個新特性,能夠用try來自動關閉了

try (Statement stmt = con.createStatement()) {
    // ...
}

 

============SQL 語句的處理 以及 鏈接數據庫 的入門結束============

 

3.專題講解

1.ResultSet 講解 推薦閱讀:

http://www.iteye.com/topic/560109  

http://www.javashuo.com/article/p-qgfcntgg-e.html 

https://www.tutorialspoint.com/jdbc/jdbc-result-sets.htm (英文)

JAVADOC : https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html

ResultSet 有不一樣功能特性,

1.1 三個特性

  1. 類型
  2. 併發性
  3. 光標保持性

 

光標是什麼?

   光標就是指向行的標記,告訴他指向了哪裏.

  如圖,左邊的紅箭頭指向的,這個就是個光標,他指着第一行 (cust_id = 10001 .....)

 

(1) ResultSet 有三個不一樣的類型,

默認類型:TYPE_FORWARD_ONLY.

TYPE_FORWARD_ONLY:

   光標只能向前滾動, (只能使用 next() )

  效率:高

TYPE_SCROLL_INSENSITIVE:

   先後鼓動的方法 next(),previous() ,回到首尾的方法 first(),last() , 到達指定位置的方法 absolute(int n ) // n表明行數,下標從1開始

   光標能夠向前向後滾動,對數據庫的更改不敏感,

   效率:中等

TYPE_SCROLL_SENSITIVE:

  光標能夠先後移動,對數據庫的更改敏感

  只對UPDATE操做有效,對INSERT,DELETE無效

  (敏感的意思就是你數據庫更改後,ResultSet的數據也會更改,好比數據庫中第一行第一列的

  "蘋果"變爲"李子",ResultSet也會跟着變化)

  效率:低

 

(2) 併發性

默認類型:CONCUR_READ_ONLY.
CONCUR_READ_ONLY: 

  只讀,在ResultSet中的數據沒法更改
CONCUR_UPDATABLE

  在ResultSet中的數據能夠更改

注意:有些JDBC drivers 和 數據 不支持併發.

 

(3)光標保持性

默認類型取決你的DBMS

HOLD_CURSORS_OVER_COMMIT:

  在事務commit 或rollback 後,ResultSet 仍然可用 (推薦在大部分ResultSet 都是 CONCUR_READ_ONLY 的狀況下使用)
CLOSE_CURSORS_AT_COMMIT:

  在事務commit 或rollback 後,ResultSet 不可用

注意:不是全部 數據庫支持

 

(4)檢測當前數據庫時候支持某種特性

boolean supportsResultSetType(int resultSetType); //類型
boolean supportsResultSetConcurrency(int type, int concurrency);//併發性
boolean supportsResultSetHoldability(int holdability);//保持性

public static void cursorHoldabilitySupport(Connection conn)
	    throws SQLException {

	    DatabaseMetaData dbMetaData = conn.getMetaData();

	    System.out.println("Default cursor holdability: " +
	        dbMetaData.getResultSetHoldability());//檢測是否支持併發性

	    System.out.println("Supports HOLD_CURSORS_OVER_COMMIT? " +
	        dbMetaData.supportsResultSetHoldability(
	            ResultSet.HOLD_CURSORS_OVER_COMMIT));//檢測時候支持併發性的某一種

	    System.out.println("Supports CLOSE_CURSORS_AT_COMMIT? " +
	        dbMetaData.supportsResultSetHoldability(
	            ResultSet.CLOSE_CURSORS_AT_COMMIT));
	}

 

1.2 功能

1.2.1 控制光標的函數介紹:

先後滾動的方法 next(),previous()

回到首尾的方法 first(),last()

到達指定位置的方法 absolute(int n) // n表明行數,下標從1開始

            relative(int n) 移動到相對於當前位置

移動到最後一行後面,此時沒有指向任意行 afterLast()

移動到第一行前面,此時沒有指向任意行 beforeFirst()

 

1.2.2 ResultSet支持用下標進行查詢,查詢更高效

從左到右,下標從1開始,(即1 表明cust_id 列)

 如: int custID = resultSet.getInt(1); // 獲取該行第一列的整數

示範代碼:

    public void viewTable(Connection con,String dbName) throws SQLException{ String query =  
                "SELECT * FROM customers;";//查詢 SQL語句
        try(Statement statement = con.createStatement()){//建立Statement
            ResultSet resultSet = statement.executeQuery(query);//編譯SQL語句
            while(resultSet.next()) { int custID = resultSet.getInt(1); // 獲取該行第一列的整數,即cust_id的信息 String custName = resultSet.getString(2); String custAddress = resultSet.getString(3); System.out.println(custID + ", " + custName + ", " + custAddress); } }catch (SQLException e) { System.out.println(e); } }

 

1.2.3 用ResultSet 更新行

用ResultSet.updateFloat(int columnInex ,  Array x) 去更新行,讓行進行改變,

只有在執行Result.updateRow() 以後,改變纔會生效.

    public void changeTable(Connection con,String dbName) throws SQLException{ String query =  
                "SELECT * FROM customers;";//SQL語句
        try(Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE)){//建立Statement
            ResultSet resultSet = statement.executeQuery(query);//編譯SQL語句
            resultSet.afterLast();//從前日後更新ID,由於他是 PRIMARY KEY不能重複
            while(resultSet.previous()) { int custID = resultSet.getInt(1); resultSet.updateInt(1, custID+1);//客服ID + 1
 resultSet.updateRow();//更改生效 } }catch (SQLException e) { System.out.println(e); } }

 

1.2.4 用ResultSet 插入行 

注意:有些JDBC driver不支持 這個功能,若是不支持,會拋出一個 SQLFeatureNotSupportedException  異常

先用 ResultSet.moveToInsertRow(); 將光標移動到要插入的地方

以後用 ResultSet.update() 修改要插入的數據

最後用 ResultSet.insertRow(); 完成插入

    public void insertTableReSet(Connection con) throws SQLException{ String query =  
                "SELECT * FROM customers;";//SQL語句
        try (Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE)){ ResultSet resultSet = statement.executeQuery(query); resultSet.moveToInsertRow(); resultSet.updateInt(1, 10006); resultSet.updateString(2, "New Man"); resultSet.updateString(3, "3123 street"); resultSet.insertRow(); }catch (Exception e) { System.out.println(e); } }

 

2.Statement 

2.1 Statement 方法

Statement和ResultSet是怎麼工做的?

  查詢到的的數據先存到數據庫的緩衝中,ResultSet就是對緩衝數據的應用.

setFetchsize 設置每次獲取的數據數(rs.next),當數據數量多,處理時間長,就須要用到

setMaxRows 限制返回的數據集數量,相似SQL 中的LIMIT

getGeneratedKeys() 獲取自動生成的主鍵值.

executeQuery 只執行查 (select語句)

executeUpdate 執行 增刪改  (insert或者update/delete(DML)語句,或者 什麼也不返回的DDL語句)

execute 執行 增刪改查

 

2.2  PreparedStatement

PreparedStatement 是一個預編譯SQL語句,能夠用setter傳入參數

也就是能夠用有參數的SQL語句了,參數代替用 ? 從左往右排序, 下標爲1開始.

動態參數化用來查詢的 演示代碼:

    public void viewTablePreparedStatement(Connection con,String dbName) throws SQLException{ String query =  
                "SELECT * FROM customers WHERE cust_id = ?;";// ? 能夠用一個參數代替
        try(PreparedStatement statement = con.prepareStatement(query)){//建立Statement
            ResultSet resultSet = statement.executeQuery(query);//編譯SQL語句
            int i = 0; while(resultSet.next()) { statement.setInt(1, 10001 + i++);//用 10001 + i 替代 第一個 ?
                int custID = resultSet.getInt(1); String custName = resultSet.getString(2); String custAddress = resultSet.getString(3); System.out.println(custID + ", " + custName + ", " + custAddress); } }catch (SQLException e) { System.out.println(e); } }

 

能夠用一個循環去設置參數

for(Map.Entry<Integer, String> e: customerCondition.entrySet()) { statement.setInt(1, e.getKey()); statement.setString(2, e.getValue());   //寫執行語句 }

演示代碼:

    public void viewTablePreparedStatement(Connection con,String dbName) throws SQLException{ String query =  
                "SELECT * FROM customers WHERE cust_id = ? AND cust_name = ?;";//? 能夠用一個參數代替
        HashMap<Integer, String> customerCondition = new HashMap<>(); customerCondition.put(10001, "Coyote Inc."); customerCondition.put(10002, "Mouse House"); customerCondition.put(10003, "Wascals"); customerCondition.put(10004, "Yosemite Place"); customerCondition.put(10005, "New Man"); try(PreparedStatement statement = con.prepareStatement(query)){//建立Statement
            ResultSet resultSet = statement.executeQuery(query);//編譯SQL語句
            int i = 0; for(Map.Entry<Integer, String> e: customerCondition.entrySet()) { statement.setInt(1, e.getKey()); statement.setString(2, e.getValue()); resultSet.next(); int custID = resultSet.getInt(1); String custName = resultSet.getString(2); String custAddress = resultSet.getString(3); System.out.println(custID + ", " + custName + ", " + custAddress); } }catch (SQLException e) { System.out.println(e); } }

 

優勢:

  性能比Statement 好,

  還能夠防止臭名昭著的 SQL注入 攻擊, 簡單原理

  應該儘可能使用PreparedStatement.

缺點:

  沒法使用 IN 語句,

解放方案

  1.多個單條SQL語句拼成與有 IN 語句同樣的查詢 -- 效率低

  2.使用存儲過程, (由於存儲過程是數據庫專有,

  推薦在只使用一種數據庫 和 不打算換數據庫服務器 的狀況下使用)

  3.動態生成PreparedStatement查詢 (可是沒法 使用 PreparedStatement caching)

  4.使用NULL在PreparedStatement查詢.(推薦)

第四種方法的演示代碼:

    public void viewTablePreaparedStatementNull(Connection con) {
         final String QUERY = "SELECT * FROM customers WHERE cust_id IN (?,?,?,?,?,?,?,?,?,?);";
         final int PARAM_SIZE = 10;
         int[] ids = {10001,10002,10003,10004,10005};
         if(ids.length > PARAM_SIZE) {
             System.out.println("MAXIMUM INPUT SIZE " + PARAM_SIZE);
             return;
         }
         int i = 1;
         try(PreparedStatement preparedStatement = con.prepareStatement(QUERY)){
             for(; i<= ids.length;i++) {//填充參數
                 preparedStatement.setInt(i, ids[i-1]);
             }
             for(; i<=PARAM_SIZE;i++) {//設置未填充的參數爲空
                 preparedStatement.setNull(i, java.sql.Types.INTEGER);
             }
             try(ResultSet re = preparedStatement.executeQuery()){
                 while(re.next()) {
                     System.out.println(re.getInt(1) + " "+ re.getString(2));
                 }
             }catch (SQLException e) {
                 e.printStackTrace();
            }
         }catch (SQLException e) {
             e.printStackTrace();
        }
    }

 

 

2.3 CallableStatement

 調用儲存過程

SQL語句

DROP procedure IF exists SHOW_CUSTOMERS;
DELIMITER //
CREATE PROCEDURE SHOW_CUSTOMERS(
    IN in_cust_id INT,
    OUT out_cust_name VARCHAR(40)
)
BEGIN
    SELECT cust_name FROM customers
    WHERE cust_id = in_cust_id INTO out_cust_name;
END//
DELIMITER ;

CALL SHOW_CUSTOMERS(10006,@custID);
SELECT @custID;

 

演示代碼:

建立存儲過程

    public void createStoredProcedure(Connection con) {
        String dropProcedure = "DROP procedure IF exists SHOW_CUSTOMERS;";
        String createProcedure = "CREATE PROCEDURE SHOW_CUSTOMERS(\r\n" + 
                "    IN in_cust_id INT,\r\n" + 
                "    OUT out_cust_name VARCHAR(40)\r\n" + 
                ")\r\n" + 
                "BEGIN\r\n" + 
                "    SELECT cust_name FROM customers\r\n" + 
                "    WHERE cust_id = in_cust_id INTO out_cust_name;\r\n" + 
                "END;";
        try(CallableStatement cs = con.prepareCall(createProcedure);PreparedStatement ps = con.prepareStatement(dropProcedure)){
            ps.executeUpdate();
            cs.executeUpdate();
        }catch(SQLException e){
            e.printStackTrace();
        }
    }

 

使用存儲過程

    public void useStoredProcedure(Connection con) {
        try(CallableStatement cs = con.prepareCall("{CALL SHOW_CUSTOMERS(?,?)}")){
            cs.setInt(1, 10005);
            cs.registerOutParameter(2, Types.CHAR);
            cs.executeQuery();
            System.out.println(cs.getString(2));
        }catch(SQLException e) {
            e.printStackTrace();
        }
    }

 

3.批處理 + 刪除表格

主要用到statement中

addBatch ,executeBatch ,clearBatch 三個方法.

即添加語句,執行語句,清除含有的語句.

 

接下來咱們須要用 批處理 從新建立 填充表格的方法,

爲了容易說明, 咱們須要刪除掉剛剛的表,而後從新建立,再填充.

 

刪表的原理相似與建表

    public void dropTable(Connection con) throws SQLException{ String query = "DROP TABLE customers"; try(Statement statement = con.createStatement()){//建立Statement
            statement.executeUpdate(query);//編譯SQL語句
        }catch (SQLException e) { System.out.println(e); } }

 

刪除表以後,咱們從新建立表(原理相似),而後再使用填充方法

用批處理寫的填充方法:

    public void populateTable(Connection con) throws SQLException{ try(Statement statement = con.createStatement()){ con.setAutoCommit(false);//開啓事務
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com');"); statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)\r\n" + 
                    "VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse');"); statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10003, 'Wascals', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'rabbit@wascally.com');"); statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10004, 'Yosemite Place', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Y Sam', 'sam@yosemite.com');"); statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)\r\n" + 
                    "VALUES(10005, 'E Fudd', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'E Fudd');"); int[] successNum = statement.executeBatch();//返回成功數,而且執行批語句
            System.out.println("成功插入:" + Arrays.toString(successNum)); con.commit(); }catch (Exception e) { System.out.println(e); }finally{ con.setAutoCommit(true); } }

 

4.DataSource 與鏈接池,事務

1.1DataSource 與鏈接池

推薦閱讀:http://www.javashuo.com/article/p-cygaddcx-a.html

通常開源的數據鏈接池有兩種:C3P0 與 DBCP 區別與介紹

 

接下來咱們要用

javax.sql.DataSource;

中 DataSource 來寫一個簡易鏈接池.

首先明確幾個概念.

鏈接池:

  鏈接數據庫的操做十分昂貴,

  因此咱們想到能夠把已經創建好的鏈接先存起來,

  這就用到了鏈接池.

動態代理模式:

  參考文章1 參考文章2 參考文章3

  使用一個代理類 代理 原有的對象, 在代理類中原有對象的方法調用以前,

  都要通過代理類的處理,而後再決定是否回到原有對象.

  好比: 咱們的Connection中有一個close() 方法,意思就是關閉鏈接,

  那麼咱們須要修改Connecttion.close()的執行方式,這時候就須要

  寫一個代理類代理Connection類.

 

簡易鏈接池的思路:

1.實現DataSource接口,批量建立必定 鏈接(connection) 而且將其加入 容器 中

2.每次使用getConnection(),都從容器中去除一個 鏈接(connection),若是容器中沒有可用容器,提示"數據庫繁忙"

3.每次close的時候,保證用完的 鏈接(connection) 回到容器中.

簡易鏈接池代碼:

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.logging.Logger;

import javax.management.RuntimeErrorException;
import javax.sql.DataSource;

public class JDBCPool implements DataSource{
    private static LinkedList<Connection> conPool = new LinkedList<Connection>();
    static {
        //InputStream in = JDBCPool.class.getClassLoader().getResourceAsStream("db.properties");
        //Properties pro = new Properties();
        try {
            //pro.load(in);
            //String driver = pro.getProperty("driver");
            //String url = pro.getProperty("url");
            //String username = pro.getProperty("username");
            //String password = pro.getProperty("password");
            //int jdbcPoolInitSize = Integer.parseInt(pro.getProperty("jdbcPoolInitSize"));
            String driver = "mysql";
            String url = "jdbc:mysql://localhost:3306/crashcourse?serverTimezone=UTC&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true";
            String username = "root";
            String password = "***";
            int jdbcPoolInitSize = 10;
            for(int i=0;i<jdbcPoolInitSize;i++) {
                    Connection connection  = DriverManager.getConnection(url,username,password);
                    System.out.println("初始化數據庫 " + (i+1) + "個鏈接");
                    conPool.add(connection);
             }
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public Connection getConnection() throws SQLException {
        // TODO Auto-generated method stub
        if(conPool.size() > 0) {
            final Connection conn = conPool.removeFirst();
            return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(),
                    new Class[] {Connection.class},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if(!"close".equals(method.getName())) {
                                return method.invoke(conn, args);
                            }
                            else {
                                conPool.add(conn);
                                System.out.println("關閉鏈接,實際還給了鏈接池");
                                System.out.println("池中鏈接數爲 " + conPool.size());
                                return null;
                            }
                        }
                    });
        }else {
            throw new RuntimeErrorException(null, "數據庫繁忙");
        }
    }
    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // TODO Auto-generated method stub
        
    }
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // TODO Auto-generated method stub
        
    }
    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }
}

 

另外一個調用類代碼:

import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Properties;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.sql.DataSource;

public class JDBCTutorials {
    JDBCPool jdbcPool = new JDBCPool();
    String userName = "root"; //用戶名
    String password = "***";//用戶密碼
    String dbms = "mysql";//數據庫管理系統名稱
    String serverName = "localhost";//服務器名稱
    String portNumber = "3306";//服務器接口
    String Atribute = "serverTimezone=UTC"
            + "&characterEncoding=utf-8"
            + "&useSSL=false"
            + "&allowPublicKeyRetrieval=true";//時區與SLL鏈接方式禁用
    String dbName = "crashcourse";//數據庫名稱
    //jdbc:mysql://localhost:3306/
    //jdbc:derby:testdb;create=true
    public Connection getConnection() throws SQLException{
        Connection conn = jdbcPool.getConnection();
//        Connection conn = null;
//        Properties connectionPros = new Properties();
//        connectionPros.put("user", userName);
//        connectionPros.put("password", password);
//        if(this.dbms.equals("mysql")) {
//            conn = DriverManager.getConnection("jdbc:" + this.dbms + "://" + this.serverName +
//                                                    ":" + this.portNumber + "/" + this.dbName + "?" + this.Atribute,connectionPros);
//            //conn.setCatalog(this.dbName);
//        } else if (this.dbms.equals("derby")) {
//            conn = DriverManager.getConnection("jdbc:" + this.dbms + ":"+ this.dbName + ":" 
//                                                + ";create=true",connectionPros);
//        }
//        System.out.println("Connection DBMS Successfully");
        return conn;
    }
    
    public void viewTable(Connection con,String dbName) throws SQLException{
        String query =  
                "SELECT * FROM customers;";//SQL語句
        try(Statement statement = con.createStatement()){//建立Statement
            ResultSet resultSet = statement.executeQuery(query);//編譯SQL語句
            while(resultSet.next()) {
                int custID = resultSet.getInt(1);
                String custName = resultSet.getString(2);
                String custAddress = resultSet.getString(3);
                System.out.println(custID + ", " + custName + ", " + 
                                    custAddress);
            }
        }catch (SQLException e) {
            System.out.println(e);
        }
    }
    
    public void viewTablePreparedStatement(Connection con,String dbName) throws SQLException{
        String query =  
                "SELECT * FROM customers WHERE cust_id = ? AND cust_name = ?;";//? 能夠用一個參數代替
        HashMap<Integer, String> customerCondition = new HashMap<>();
        customerCondition.put(10001, "Coyote Inc.");
        customerCondition.put(10002, "Mouse House");
        customerCondition.put(10003, "Wascals");
        customerCondition.put(10004, "Yosemite Place");
        customerCondition.put(10005, "New Man");
        
        try(PreparedStatement statement = con.prepareStatement(query)){//建立Statement
            ResultSet resultSet = statement.executeQuery(query);//編譯SQL語句
            int i = 0;
            for(Map.Entry<Integer, String> e: customerCondition.entrySet()) {
                statement.setInt(1, e.getKey());
                statement.setString(2, e.getValue());
                resultSet.next();
                int custID = resultSet.getInt(1);
                String custName = resultSet.getString(2);
                String custAddress = resultSet.getString(3);
                System.out.println(custID + ", " + custName + ", " + 
                                    custAddress);
            }
        }catch (SQLException e) {
            System.out.println(e);
        }
    }
    public void viewTablePreaparedStatementNull(Connection con) {
         final String QUERY = "SELECT * FROM customers WHERE cust_id IN (?,?,?,?,?,?,?,?,?,?);";
         final int PARAM_SIZE = 10;
         int[] ids = {10001,10002,10003,10004,10005};
         if(ids.length > PARAM_SIZE) {
             System.out.println("MAXIMUM INPUT SIZE " + PARAM_SIZE);
             return;
         }
         int i = 1;
         try(PreparedStatement preparedStatement = con.prepareStatement(QUERY)){
             for(; i<= ids.length;i++) {
                 preparedStatement.setInt(i, ids[i-1]);
             }
             for(; i<=PARAM_SIZE;i++) {
                 preparedStatement.setNull(i, java.sql.Types.INTEGER);
             }
             try(ResultSet re = preparedStatement.executeQuery()){
                 while(re.next()) {
                     System.out.println(re.getInt(1) + " "+ re.getString(2));
                 }
             }catch (SQLException e) {
                 e.printStackTrace();
            }
         }catch (SQLException e) {
             e.printStackTrace();
        }
    }
    
    public void changeTable(Connection con,String dbName) throws SQLException{
        String query =  
                "SELECT * FROM customers;";//SQL語句
        try(Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE)){//建立Statement
            ResultSet resultSet = statement.executeQuery(query);//編譯SQL語句
            resultSet.afterLast();//從前日後更新ID,由於他是 PRIMARY KEY不能重複
            while(resultSet.previous()) {
                int custID = resultSet.getInt(1);
                resultSet.updateInt(1, custID+1);//客服ID + 1
                resultSet.updateRow();
            }
        }catch (SQLException e) {
            System.out.println(e);
        }
    }
    
    public void dropTable(Connection con) throws SQLException{
        final String QUERY = "DROP TABLE customers";
        try(Statement statement = con.createStatement()){//建立Statement
            statement.executeUpdate(QUERY);//編譯SQL語句
        }catch (SQLException e) {
            System.out.println(e);
        }
    }
    public void createTable(Connection con) throws SQLException{
        String createSQL = "CREATE TABLE customers\r\n" + 
                "(\r\n" + 
                "  cust_id      int       NOT NULL AUTO_INCREMENT,\r\n" + 
                "  cust_name    char(50)  NOT NULL ,\r\n" + 
                "  cust_address char(50)  NULL ,\r\n" + 
                "  cust_city    char(50)  NULL ,\r\n" + 
                "  cust_state   char(5)   NULL ,\r\n" + 
                "  cust_zip     char(10)  NULL ,\r\n" + 
                "  cust_country char(50)  NULL ,\r\n" + 
                "  cust_contact char(50)  NULL ,\r\n" + 
                "  cust_email   char(255) NULL ,\r\n" + 
                "  PRIMARY KEY (cust_id)\r\n" + 
                ") ENGINE=InnoDB;";
        try(Statement statement = con.createStatement()){
            statement.executeUpdate(createSQL);
        }catch (Exception e) {
            System.out.println(e);
        }
    }
    
    public void populateTableBatch(Connection con) throws SQLException{
        try(Statement statement = con.createStatement()){
            con.setAutoCommit(false);//關閉事務自動commit 和 rollback
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com');");
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)\r\n" + 
                    "VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse');");
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10003, 'Wascals', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'rabbit@wascally.com');");
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10004, 'Yosemite Place', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Y Sam', 'sam@yosemite.com');");
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)\r\n" + 
                    "VALUES(10005, 'E Fudd', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'E Fudd');");
            int[] successNum = statement.executeBatch();//返回成功數
            System.out.println("成功插入:" + Arrays.toString(successNum));
            con.commit();
        }catch (Exception e) {
            System.out.println(e);
        }finally{
            con.setAutoCommit(true);
        }
    }
    public void insertTableReSet(Connection con) throws SQLException{
        String query =  
                "SELECT * FROM customers;";//SQL語句
        try (Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE)){
            ResultSet resultSet = statement.executeQuery(query);
            resultSet.moveToInsertRow();
            resultSet.updateInt(1, 10006);
            resultSet.updateString(2, "New Man");
            resultSet.updateString(3, "3123 street");
            resultSet.insertRow();
        }catch (Exception e) {
            System.out.println(e);
        }
    }
    public void createStoredProcedure(Connection con) {
        String dropProcedure = "DROP procedure IF exists SHOW_CUSTOMERS;";
        String createProcedure = "CREATE PROCEDURE SHOW_CUSTOMERS(\r\n" + 
                "    IN in_cust_id INT,\r\n" + 
                "    OUT out_cust_name VARCHAR(40)\r\n" + 
                ")\r\n" + 
                "BEGIN\r\n" + 
                "    SELECT cust_name FROM customers\r\n" + 
                "    WHERE cust_id = in_cust_id INTO out_cust_name;\r\n" + 
                "END;";
        try(CallableStatement cs = con.prepareCall(createProcedure);PreparedStatement ps = con.prepareStatement(dropProcedure)){
            ps.executeUpdate();
            cs.executeUpdate();
        }catch(SQLException e){
            e.printStackTrace();
        }
    }
    public void useStoredProcedure(Connection con) {
        try(CallableStatement cs = con.prepareCall("{CALL SHOW_CUSTOMERS(?,?)}")){
            cs.setInt(1, 10005);
            cs.registerOutParameter(2, Types.CHAR);
            cs.executeQuery();
            System.out.println(cs.getString(2));
        }catch(SQLException e) {
            e.printStackTrace();
        }
    }
    
    
    public static void main(String[] args) {
        JDBCTutorials jdbcTutorials = new JDBCTutorials();
        try (Connection connection = jdbcTutorials.getConnection()){
            jdbcTutorials.dropTable(connection);
            jdbcTutorials.createTable(connection);
            jdbcTutorials.populateTableBatch(connection);
            jdbcTutorials.insertTableReSet(connection);
            jdbcTutorials.viewTablePreaparedStatementNull(connection);
            jdbcTutorials.changeTable(connection,"crashcourse");
            System.out.println();
            jdbcTutorials.viewTable(connection,"crashcourse");
            System.out.println();
            jdbcTutorials.createStoredProcedure(connection);
            jdbcTutorials.useStoredProcedure(connection);
        }catch (SQLException e) {
            System.out.println(e);
        }
    }
}
View Code

 

DBCP使用(JDNI):

推薦閱讀:http://www.javashuo.com/article/p-guhslqsa-e.html

去官方下三個包 http://commons.apache.org/proper/

基礎的BasicDataSource 就是咱們的第三類DataSource(鏈接池,Connection,事務)

    public Connection getConnection()  {
        Connection con = null;
        BasicDataSource ds = new BasicDataSource();
        ds.setUrl("jdbc:mysql://localhost:3306/crashcourse?serverTimezone=GMT&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true");
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUsername("root");
        ds.setPassword("***");
        ds.setMaxTotal(30);
        try{
            con = ds.getConnection( );
        }catch(SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

 

咱們能夠用.properties 來保存BasicDataSource的配置

########DBCP配置文件##########
#驅動名
driverClassName=com.mysql.jdbc.Driver
#url
url=jdbc:mysql://127.0.0.1:3306/mydb
#用戶名
username=sa
#密碼
password=123456
#初試鏈接數
initialSize=30
#最大活躍數
maxTotal=30
#最大idle數
maxIdle=10
#最小idle數
minIdle=5
#最長等待時間(毫秒)
maxWaitMillis=1000
#程序中的鏈接不使用後是否被鏈接池回收(該版本要使用removeAbandonedOnMaintenance和removeAbandonedOnBorrow)
#removeAbandoned=true
removeAbandonedOnMaintenance=true
removeAbandonedOnBorrow=true
#鏈接在所指定的秒數內未使用纔會被刪除(秒)(爲配合測試程序才配置爲1秒)
removeAbandonedTimeout=1

 

而後寫一個類來用它

public class JDBCPool {
    private static Properties properties = new Properties();
    private static DataSource dataSource;
    static {
        try (FileInputStream is = new FileInputStream("config/dbcp.properties")){
            dataSource = BasicDataSourceFactory.createDataSource(properties);
            properties.load(is);
        } catch (IOException e){
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        }catch(SQLException e){
            e.printStackTrace();
        }
        return connection;
    }
}

 

 1.2 事務

  • 數據庫中的一組相關的SQL語句,做爲一個執行單元,所有執行成功,或者所有不執行,不能拆分的這種SQL語句組合的執行方式叫作事務
  • 轉帳是一種典型的使用事務的案例

    轉出帳戶和轉入帳戶必須同時執行成功才能算做是一次轉帳,若是一方沒有執行成功,那麼另外一方也要不執行

由於JDBC中commit是默認開啓,

要使用事務就得將其關閉,而且事務內的鏈接(Connection)只有一個

Connection.setAutoCommit(false);

在語句的最後使用 Connection.commit(); 提交

或者在中途使用Connection.rollback();回滾

    public void populateTableBatch(Connection con) throws SQLException{
        try(Statement statement = con.createStatement()){
            con.setAutoCommit(false);//開啓事務
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com');");
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)\r\n" + 
                    "VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse');");
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10003, 'Wascals', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'rabbit@wascally.com');");
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)\r\n" + 
                    "VALUES(10004, 'Yosemite Place', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Y Sam', 'sam@yosemite.com');");
            statement.addBatch("INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)\r\n" + 
                    "VALUES(10005, 'E Fudd', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'E Fudd');");
            int[] successNum = statement.executeBatch();//返回成功數
            System.out.println("成功插入:" + Arrays.toString(successNum));
            con.commit();
        }catch (Exception e) {
            System.out.println(e);
            con.rollback();
        }finally{
            con.setAutoCommit(true);
        }
    }

 

=============== 專題介紹結束 ===============

4.結尾

 

1. ORM (Object relation mapping) 對象關係映射

ORM 是一種思想,將數據庫中的數據變成對象,對象變成數據庫中的數據。

由於咱們編程語言是面對對象的,一切都是對象,而關係數據庫是有數學理論發展起來的,

這二者直接有必定區別,ORM就是爲了解決這個問題而存在的。

 https://blog.csdn.net/ma15732625261/article/details/74332441

ORM 工具介紹:ADO.NET等

 

2. DAO 封裝本身的JDBC

DAO: DAO 就是一個模型,它將數據庫與業務邏輯之間,將訪問數據庫的代碼封裝成一個類。(增刪改,不包含業務信息)

須要的包:

http://commons.apache.org/proper/

仍是用上面的表

 寫一個Customers 類

public class Customers {
    int custID;
    String custName;
    String custAddress;
    String custCity;
    String custState;
    String custZip;
    @Override
    public String toString() {
        return "Customers [custID=" + custID + ", custName=" + custName + ", custAddress=" + custAddress + ", custCity="
                + custCity + ", custState=" + custState + ", custZip=" + custZip + ", custCountry=" + custCountry
                + ", custContact=" + custContact + ", custEmail=" + custEmail + "]";
    }
    String custCountry;
    String custContact;
    String custEmail;
    public int getCustID() {
        return custID;
    }
    public void setCustID(int custID) {
        this.custID = custID;
    }
    public String getCustName() {
        return custName;
    }
    public void setCustName(String custName) {
        this.custName = custName;
    }
    public String getCustAddress() {
        return custAddress;
    }
    public void setCustAddress(String custAddress) {
        this.custAddress = custAddress;
    }
    public String getCustCity() {
        return custCity;
    }
    public void setCustCity(String custCity) {
        this.custCity = custCity;
    }
    public String getCustState() {
        return custState;
    }
    public void setCustState(String custState) {
        this.custState = custState;
    }
    public String getCustZip() {
        return custZip;
    }
    public void setCustZip(String custZip) {
        this.custZip = custZip;
    }
    public String getCustCountry() {
        return custCountry;
    }
    public void setCustCountry(String custCountry) {
        this.custCountry = custCountry;
    }
    public String getCustContact() {
        return custContact;
    }
    public void setCustContact(String custContact) {
        this.custContact = custContact;
    }
    public String getCustEmail() {
        return custEmail;
    }
    public void setCustEmail(String custEmail) {
        this.custEmail = custEmail;
    }
    
}

DAO類:

 

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
public class Dao {
    
    public void updata(String sql,Object... args) {
        try(Connection con = JDBCTutorials.getConnection();
                PreparedStatement pre = con.prepareStatement(sql)){
            for(int i=0;i<args.length;i++) {
                pre.setObject(i+1, args);
            }
            pre.executeUpdate();
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    @SuppressWarnings("deprecation")
    /**
     * 返回一條查詢記錄
     * @param rs
     * @return
     * @throws SQLException
     */
    public <T> T get(Class<T> dataClass,String sql,Object... args){
        List<T> list = new LinkedList<>();
        list = getForList(dataClass,sql,args);
        if(list.size() > 0) {
            return list.get(0);
        }else {
            return null;
        }
    }
    
    /**
     * 返回多條查詢記錄
     * @param rs
     * @return
     * @throws SQLException
     */
    @SuppressWarnings("deprecation")
    public <T> List<T> getForList(Class<T> dataClass,String sql,Object... args){
        //1.獲得結果集
        List<T> list = new LinkedList<T>();
        
        try(Connection con = JDBCTutorials.getConnection();
                PreparedStatement pre = con.prepareStatement(sql)){
            for(int i=0;i<args.length;i++) {
                pre.setObject(i+1, args[i]);
            }
            try(ResultSet rs = pre.executeQuery()){
                //2.處理結果集, 獲得Map的List,其中一個Map對象對應着一條記錄
                //Map 的 Key 是 ReusltSet別名,value爲列的值。
                List<Map<String, Object>> mapList = handlerResultSetToMap(rs);
                
                //3.處理Map 中的List 變爲 dataClass 的List
                //Map的key 爲 dataClass的屬性名,value 爲dataClass的屬性值
                list = transferMapListToLinkedList(dataClass, mapList);
            }catch (SQLException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }

    private <T> List<T> transferMapListToLinkedList(Class<T> dataClass,
            List<Map<String, Object>> mapList)
            throws InstantiationException, IllegalAccessException, InvocationTargetException {
        T entity = null;
        LinkedList<T> list = new LinkedList<T>();
        if(mapList.size()>0) {
            for(Map<String, Object> mt:mapList) {
                entity = dataClass.newInstance();
                for(Map.Entry<String, Object> m:mt.entrySet()) {
                    String proName = m.getKey();
                    Object value = m.getValue();
                    BeanUtils.setProperty(entity, proName, value);//填充entity的屬性                
                }
                list.add(entity);
            }                    
        }
        return list;
    }

    private List<Map<String, Object>> handlerResultSetToMap(ResultSet rs) throws SQLException {
        List<Map<String,Object>> mapList = new LinkedList<Map<String,Object>>();
        List<String> columnLabel = getColumnLabels(rs);
        while(rs.next()) {
            Map<String,Object> mapz = new HashMap<String,Object>();
            for(String colName:columnLabel) {
                Object colObject = rs.getObject(colName);
                mapz.put(colName, colObject);
            }
            mapList.add(mapz);
        }
        return mapList;
    }

    public List<String> getColumnLabels(ResultSet rs) throws SQLException{
        List<String> list = new LinkedList<String>();
        ResultSetMetaData resultSetMetaData = rs.getMetaData();
        for(int i=0;i<resultSetMetaData.getColumnCount();i++) {
            list.add(resultSetMetaData.getColumnLabel(i+1));
        }
        return list;
    }
    
    /**
     * 返回某條記錄的字段
     * @param sql
     * @param args
     * @return
     */
    public <E> E getForValue(String sql,Object... args) {
        try(Connection con = JDBCTutorials.getConnection();
                PreparedStatement pre = con.prepareStatement(sql)){
            for(int i=0;i<args.length;i++) {
                pre.setObject(i+1, args[i]);
            }
            try(ResultSet rs = pre.executeQuery()){
                if(rs.next()) {
                    return (E)rs.getObject(1);
                }
            }catch (SQLException e) {
                e.printStackTrace();
            }
        }catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}

 

DaoTest類

class DaoTest {
    Dao dao = new Dao();
    
    @Test
    void testUpdata() {
        String sql = "SELECT cust_id custID,cust_name custName FROM customers WHERE cust_id = ?;";
        Customers customers = dao.get(Customers.class, sql,10003);
        System.out.println("---");
        System.out.println(customers);
    }

    @Test
    void testGet() {
        String sql = "SELECT cust_id custID,cust_name custName FROM customers WHERE cust_id = ? OR cust_id = ?;";
        List<Customers> customers = dao.getForList(Customers.class, sql,10003,10004);
        for(Customers l:customers) {
            System.out.println(l);
        }
    }
    
    @Test
    void testGetValue() {
        String sql = "Select MAX(cust_id) FROM customers";
        int maxCustID = dao.getForValue(sql);
        System.out.println(maxCustID);
    }
}

 

 

日期:2018-10-18 基礎教程寫好,後續拓展待補:

2018-10-19 14:20:15 更新拓展.

2018-10-20 21:55:52 第二大部分即將完成,編寫鏈接池概念與原理中

2018-10-22 修正

2018-10-23 13:57:55 完結撒花

相關文章
相關標籤/搜索