天天看點

MySQL資料庫與JDBC程式設計

文章目錄

  • ​​MySQL資料庫與JDBC程式設計​​
  • ​​JDBC (Java Database Connectivity)​​
  • ​​DDL(Data Definition Language,資料定義語言)語句​​
  • ​​建立表​​
  • ​​修改表結構​​
  • ​​增加列定義​​
  • ​​修改列定義​​
  • ​​删除列​​
  • ​​删除資料表​​
  • ​​清空表​​
  • ​​MySQL重命名資料表​​
  • ​​MySQL修改列名​​
  • ​​資料庫限制​​
  • ​​索引(一個資料庫對象)​​
  • ​​視圖​​
  • ​​DML(Data Definition Language,資料定義語言)語句​​
  • ​​insert into語句​​
  • ​​update語句​​
  • ​​delete from語句​​
  • ​​單表查詢​​
  • ​​where後的其他運算符​​
  • ​​order by輸出排序​​
  • ​​資料庫函數​​
  • ​​MySQL單行函數​​
  • ​​分組群組函數​​
  • ​​group by分組​​
  • ​​多表連接配接查詢​​
  • ​​交叉連接配接​​
  • ​​自然連接配接​​
  • ​​using子句連接配接​​
  • ​​on子句連接配接​​
  • ​​左、右、全外連接配接​​
  • ​​子查詢​​
  • ​​集合運算​​
  • ​​union并運算​​
  • ​​minus差運算​​
  • ​​intersect交運算​​
  • ​​JDBC的經典用法​​
  • ​​JDBC程式設計步驟​​
  • ​​示例:簡單SQL查詢​​
  • ​​執行SQL語句的方法​​
  • ​​使用executeLargeUpdate方法執行DDL和DML語句​​
  • ​​示例:讀取ini檔案,連接配接并建立資料表​​
  • ​​使用PreparedStatement執行SQL語句​​
  • ​​示例:使用PreparedStatement插入記錄​​
  • ​​使用CallableStatement調用存儲過程​​
  • ​​示例:調用存儲功能​​
  • ​​管理結果集​​
  • ​​可滾動、可更改的結果集​​
  • ​​示例:建立可滾動、可更改的結果集​​
  • ​​處理Blob類型資料​​
  • ​​示例:通過SQL的Blob存儲并讀取圖檔資料​​
  • ​​使用ResultSetMetaData分析結果集​​
  • ​​示例:分析結果集​​
  • ​​使用RowSet包裝結果集​​
  • ​​RowSetFactory和RowSet​​
  • ​​示例:通過RowSetFactory使用jdbcRowSet​​
  • ​​離線RowSet​​
  • ​​示例:CachedRowSet離線操作SQL​​
  • ​​事務處理​​
  • ​​事務的概念​​
  • ​​JDBC事務支援​​
  • ​​使用批量更新​​

MySQL資料庫與JDBC程式設計

JDBC (Java Database Connectivity)

對于關系資料庫而言,最基本的資料存儲單元是資料表。

SQL:Structured Query Language,結構化查詢語言。

DDL(Data Definition Language,資料定義語言)語句

主要操作資料庫對象。

建立表

CREATE TABLE [模式名.] 表名
(
  columnName1 datatype [default expr],
  ...
)      

利用子查詢建表

CREATE TABLE [模式名.] 表名 [col[, col]]
as
select * from user_inf;      

修改表結構

增加列定義
ALTER TABLE 表名
add
(
  columnName1 datatype [default expr],
  ...
);      

字元串值由單引号引起。

修改列定義
ALTER TABLE 表名
modify columnName datatype [default expr] [first|afterColName];      

first、afterColName指定需要将目标修改到指定位置。

删除列
ALTER TABLE 表名
drop columnName;      
删除資料表
DROP TABLE 表名;      

表結構删除,表對象不再存在;表的所有資料被删除;該表所有相關的索引、限制也被删除。

清空表
TRUNCATE 表名;      

删除表内的全部資料,但保留表結構。

MySQL重命名資料表
ALTER TABLE 表名 rename to 新表名;      
MySQL修改列名
ALTER TABLE 表名 CHANGE 列名 新列名 type [default expr] [first|afterColName]      

資料庫限制

5種完整性限制

1、NOT NULL:非空限制,指定某列不能為空。

CREATE TABLE 表名 (id int NOT NULL);      

2、UNIQUE:唯一限制,指定某列或者某幾列組合不能重複。

1、列級限制
name VARCHAR(255) UNIQUE;

2、表級限制(為多列組合建立唯一限制,或想自行指定限制名)
CREATE TABLE 表名(
# 表級限制文法建立唯一限制
  UNIQUE (name),
# 并指定限制名
  CONSTRAINT 新限制名 UNIQUE(pass),
# 指定兩列組合不允許重複
  CONSTRAINT 新限制名 UNIQUE(name, pass)
)

3、MySQL删除限制
ALTER TABLE 表名 DROP INDEX 限制名;

4、大部分資料庫删除限制
ALTER TABLE 表名 DROP CONSTRAINT 限制名;      

3、PRIMARY KEY:主鍵,指定該列的值可以唯一地辨別該條記錄。

1、列級限制
CREATE TABLE 表名 (id INT PRIMARY KEY);

2、表級限制
CREATE TABLE 表名 (
  id INT,
  CONSTRAINT 限制名 PRIMARY KEY(id)
);

3、建立多列組合的主鍵限制
CREATE TABLE 表名 (
  id INT,
  name VARCHAR(255),
  PRIMARY KEY(id, name)
);

4、删除主鍵限制
ALTER TABLE 表名 DROP PRIMARY KEY;

5、增加主鍵限制
MODIFY:采用列級限制文法
ADD:采用表級限制文法

6、主鍵自增長
id INT AUTO_INCREMENT PRIMARY KEY      

4、FOREIGN KEY:外鍵,指定該行記錄從屬于主表中的一條記錄,主要用于保證一個或兩個資料表之間的參照完整性。

1、指定兩列的聯合外鍵
CREATE TABLE 表名(
  Sname VARCHAR(255),
  Spass VARCHAR(255),
  CONSTRAINT 限制名 FOREIGN KEY(Sname, Spass) REFERENCES 主表名(Fname, Fpass)
 );
 
 
2、級聯删除(定義當删除主表記錄時,從表記錄也會随之級聯删除/從表記錄的外鍵設定為null)
CREATE TABLE 表名(
  Sname VARCHAR(255),
  FOREIGN KEY(Sname) REFERENCES 主表名(Fid) ON DELETE CASCADE # 也可用ON DELETE SET NULL 
);      

5、CHECK:檢查,制定一個布爾表達式,用于指定對應列的值必須滿足該表達式。(MySQL不支援)

CREATE TABLE 表名(
  key INT,
  CHECK(key>10)
);      

索引(一個資料庫對象)

建立索引的唯一作用是加速對表的查詢,索引通過使用快速路徑通路方法來快速定位資料,進而減少了磁盤的I/0。

建立索引:

1、自動
當在表上定義主鍵限制、唯一限制、外鍵限制時,系統自動建立對應的索引。
2、手動
CREATE INDEX 索引名 ON 表名(name, pass);      

删除索引:

1、自動
資料表被删除時,該表上的索引自動被删除。
2、手動
DROP INDEX 索引名 ON 表名;      

視圖

資料表中資料的邏輯顯示。

建立視圖,且不允許修改資料:

CREATE OR REPLACE VIEW 視圖名 AS 查詢語句 WITH CHECK OPTION;      

删除視圖

DROP VIEW 視圖名;      

DML(Data Definition Language,資料定義語言)語句

主要操作資料表裡的資料。由insert into、update、delete from三個指令組成。

insert into語句

向資料表中插入記錄:

INSERT INTO 表名 (id, name, age) VALUES(1, "sxf", null);      

帶子查詢的插入(要求所選的資料列個數相等、類型比對):

INSERT INTO 表名 (name) SELECT name FROM 表2;      

MySQL多條插入

INSERT INTO 表名 (id, name) VALUES(1, "a"), (2,"b");      

update語句

修改資料表的記錄。

UPDATE 表名 SET name="sxf", pass="123" WHERE id=1;      

delete from語句

删除指定資料表的記錄。總是整行删除。

DELETE FROM 表名 WHERE id>1 AND id<5;      

單表查詢

字元串連接配接用:concat();為資料列和表達式起别名用:as或空格;為表起别名用:as或空格;去除重複行用:distinct;

SELECT DISTINCT CONCAT("na", "me") AS myName FROM table t WHERE id*2>4;      

where後的其他運算符

1、expr2<=expr1<=expr3
expr1 BETWEEN expr2 AND expr3;

2、expr1等于括号裡的任意一個表達式的值
expr1 IN(expr2, expr3, ...);

3、字元串比對,下劃線代表一個字元,百分号代表任意多個字元
like ‘_%’;

4、是否為null
is null;      

order by輸出排序

desc降序,預設asc升序。如果按多列排序,則每列的asc、desc必須單獨設定。

SELECT * FROM table ORDER BY name DESC, id ASC;      

資料庫函數

多用在select和where後面。

MySQL單行函數

選出字元長度
SELECT char_length(ip) FROM IP;

計算sin值
SELECT sin(1.57);

為日期添加一定時間
SELECT ADDDATE('1998-01-02', 3);

擷取目前日期
SELECT CURDATE();

擷取目前時間
SELECT curtime();

如果expr1=null,傳回expr2,否則傳回expr1
ifnull(expr1, expr2)

如果expr1=expr2,傳回null,否則傳回expr1
nullif(expr1, expr2)

類似于三目運算符“:?”
if(expr1, expr2, expr3)

如果expr1為null,傳回true,否則傳回false
isnull(expr1)

選擇流程控制
CASE value
WHEN value1 THEN result1
WHEN value2 THEN result2
...
ELSE result
END

CASE 
WHEN condition1 THEN result1
WHEN condition2 THEN result2
...
ELSE result
END      

分組群組函數

将一組記錄作為整體計算,傳回一個結果。

計算多行expr的平均值,資料類型必須是數值型。distinct不計算重複值;預設all計算重複值
avg([distinct|all] expr)

計算多行expr的總條數,資料類型可以是任意類型。*表示統計該表内的記錄行數;distinct不計算重複值
count({*|[distinct|all] expr})

計算多行expr的最大值
max(expr)

計算多行expr的最小值
min(expr)

計算多行expr的綜合
sum([distinct|all] expr)      
group by分組

對記錄進行顯示分組。

相同的記錄當成一組,并過濾組having
SELECT * FROM ip GROUP BY ip HAVING count(*)>2;

多列的值完全相同時才會被當成一組
SELECT count(*) FROM ip GROUP BY id, ip;      

多表連接配接查詢

交叉連接配接

無需任何連接配接條件。

SELECT s.*, name FROM studentTable s CROSS JOIN teacherTable t;      

自然連接配接

以兩個表中的同名列作為連接配接條件;若沒有同名列,則效果等同交叉連接配接。

SELECT s.*, name FROM studentTable s NATURAL JOIN teacherTable t;      

using子句連接配接

顯式指定兩個表中的哪些同名列作為連接配接條件,要求必須有同名列;自然連接配接無法指定。

SELECT s.*, name FROM studentTable s JOIN teacherTable t USING(id);      

on子句連接配接

每個on子句隻能指定一個連接配接條件。如果需要進行N表連接配接,則需要N-1個join…on對。

SELECT s.*, name FROM studentTable s JOIN teacherTable t ON s.teacher=t.id;      

左、右、全外連接配接

分别使用left[outer]join、right[outer]join、full[outer]join,連接配接條件通過on子句指定。

  • 左外連接配接:把左邊表中所有不滿足連接配接條件的記錄全部列出。
  • 右外連接配接:把右邊表中所有不滿足連接配接條件的記錄全部列出。
  • 全外連接配接(MySQL不支援):把兩個表中所有不滿足連接配接條件的記錄全部列出。
SELECT s.*, name FROM studentTable s LEFT JOIN teacherTable t ON s.teacher=t.id;      

子查詢

在查詢語句中嵌套另一個查詢,支援多層嵌套。

  • 出現在from語句後當成資料表;
  • 出現在where條件後作為過濾條件的值;
  • 子查詢要用括号括起來;
  • 把子查詢當成資料表時,可以為該子查詢起别名。
SELECT * FROM (SELECT * FROM ip) t WHERE t.id>1;

SELECT * FROM ip WHERE id>(SELECT Sid FROM Stable WHERE Sname='SXF');      

集合運算

  • 兩個結果集所包含的資料列的數量必須相等。
  • 兩個結果集所包含的資料列的資料類型也必須一一對應。

union并運算

SELECT 語句 UNION SELECT 語句;      

minus差運算

MySQL不支援,使用not in代替。

SELECT 語句 MINUS SELECT 語句;

SELECT XX FROM XX WHERE (XX, XX) NOT IN (XX, XX);      

intersect交運算

MySQL不支援,使用join…on代替。

SELECT 語句 INTERSECT SELECT 語句;

SELECT XX FROM XX JOIN XX ON (XX=XX) WHERE XX=XX;      

JDBC的經典用法

JDBC程式設計步驟

Java 連接配接 MySQL 需要驅動包,最新版下載下傳位址為:​​http://dev.mysql.com/downloads/connector/j/​​,解壓後得到 jar 庫檔案,然後在對應的項目中導入該庫檔案。

1、加載資料庫驅動

通常使用Class類的forName()靜态方法來加載驅動。

Class.forName("com.mysql.ch.hdbc.Driver");
Class.forName("oracle.jdbc.driver.OracleDriver");      

2、通過DriverManager擷取資料庫連接配接

DriverManager.getConnection("jdbc:mysql://hostname:port/databasename", "user", "pwd");      

3、通過Connection對象建立Statement對象

  • createStatement():建立基本的Statement對象;
  • prepareStatement(String sql):根據傳入的SQL語句建立預編譯的Statement對象;
  • prepareCall(String sql):根據傳入的SQL語句建立CallableStatement對象;

4、使用Statement執行SQL語句

  • execute():可以執行任何SQL語句,但比較麻煩;
  • executeUpdate():主要用于執行DML和DDL語句。執行DML語句傳回受SQL語句影響的行數,執行DDL語句傳回0;
  • executeQuery():隻能執行查詢語句,執行後傳回代表查詢結果的ResultSet對象;

5、操作結果集

  • next()、previous()、first()、last()、beforeFirst()、afterLast()、absolute()等移動記錄指針的方法;
  • getXxx()方法擷取指針指向行、特定列的值。既可以使用列索引作為參數,也可使用列名作為參數。

6、回收資料庫資源

示例:簡單SQL查詢

package com.sxf.sql;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Main {
    public static void main(String[] args) throws Exception{
        // 1、加載驅動,使用反射知識。
        Class.forName("com.mysql.cj.jdbc.Driver");
        try {
            // 2、使用DriverManager擷取資料庫連接配接。
            Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/tz_music?useSSL=false&serverTimezone=UTC",
                    "root", "1061700625");
            // 3、使用Connection建立Statement對象。
            Statement state = conn.createStatement();
            // 4、執行SQL語句。
            //   execute:可執行任何SQL語句,傳回一個boolean值
            //   executeQuery:執行查詢語句,傳回一個結果集
            //   executeUpdate:執行DML語句,傳回一個整數
            ResultSet res = state.executeQuery("select * from tz_music;");
            // 5、擷取資料。
            //   next:将記錄指針下移一行,若有效則傳回true
            //   getXxx:通過列名或列索引擷取記錄指針
            while (res.next()){
                System.out.println(res.getString("music_name"));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}      

執行SQL語句的方法

使用executeLargeUpdate方法執行DDL和DML語句

示例:讀取ini檔案,連接配接并建立資料表

; mysql.ini檔案
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/tz_music?useSSL=false&serverTimezone=UTC
user=root
pwd=1061700625      
package com.sxf.sql;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;


class Sql{
    private String driver;
    private String url;
    private String user;
    private String pwd;
    public void initParam(String paramFile) throws Exception{
        var props = new Properties();
        props.load(new FileInputStream(paramFile));
        driver = props.getProperty("driver");
        url = props.getProperty("url");
        user = props.getProperty("user");
        pwd = props.getProperty("pwd");
    }

    public void createTable(String sql) throws Exception{
        Class.forName(driver);
        try (
            Connection conn = DriverManager.getConnection(url, user, pwd);
            Statement state = conn.createStatement();
            )
        {
            state.executeUpdate(sql);
        }
    }
}

public class Main {
    public static void main(String[] args) throws Exception{
        Sql sql = new Sql();
        sql.initParam("mysql.ini");
        sql.createTable("create table jdbc(id int primary key, name varchar(255));");
        System.out.println("OK");
    }
}      

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-wfh2YzCv-1590568160482)(C:\Users\SXF\AppData\Roaming\Typora\typora-user-images\image-20200525151944809.png)]

使用PreparedStatement執行SQL語句

PreparedStatement的好處與作用:

  • 預編譯SQL語句,性能更好;
  • 無需“拼接”SQL語句,程式設計更簡單;
  • 可以防止SQL注入,安全性更好。

示例:使用PreparedStatement插入記錄

class Sql
{
  ... ...
  public void insertTable() throws Exception{
        Class.forName(driver);
        try (
                Connection conn = DriverManager.getConnection(url, user, pwd);
                PreparedStatement state = conn.prepareStatement("insert into jdbc values(?, ?);");
            )
        {
            state.setInt(1, 1);
            state.setString(2, "a");
            state.executeUpdate();
            state.setInt(1, 2);
            state.setString(2, "a");
            state.executeUpdate();
        }
    }
}

public class Main {
    public static void main(String[] args) throws Exception{
        Sql sql = new Sql();
        sql.initParam("mysql.ini");
        sql.insertTable();
        System.out.println("OK");
    }
}      

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-EN8mEgxf-1590568160485)(C:\Users\SXF\AppData\Roaming\Typora\typora-user-images\image-20200525154002693.png)]

使用CallableStatement調用存儲過程

調用存儲過程的SQL語句格式:{call 過程名(? ,?, ?..)},其中的問号作為存儲過程參數的占位符。

存儲過程的參數既有傳入參數,也有傳出參數。

  • 傳入參數:通過setXxx()方法為傳入參數設定值。
  • 傳出參數:調用registerOutParameter()方法注冊該參數;通過getXxx()方法擷取指定傳出參數的值。

示例:調用存儲功能

public void callProc() {
  try {
    Class.forName(driver);
    Connection conn= DriverManager.getConnection(url, user, pwd);
    CallableStatement cstmt = conn.prepareCall("{call add_pro(?, ?, ?)}");
    cstmt.setInt(1, 4);
    cstmt.setInt(2, 5);
    // 注冊第三個參數是int類型的輸出參數
    cstmt.registerOutParameter(3, Types.INTEGER);
    cstmt.execute();
    System.out.println(cstmt.getInt(3));
  }catch (Exception e){
    e.printStackTrace();
  }
}      

管理結果集

可滾動、可更改的結果集

可滾動:可以使用方法自由移動記錄指針的ResultSet。

可更新:可調用ResultSet的方法來修改記錄指針所指記錄、特定列的值。

示例:建立可滾動、可更改的結果集

public void query(String sql) throws Exception
{
  Class.forName(driver);
  try (
    Connection conn = DriverManager.getConnection(url, user, pwd);
    // 傳入控制結果集可滾動、可更新的參數
    PreparedStatement state = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
    ResultSet res = state.executeQuery();
  )
  {
    res.last();
    int rowCount = res.getRow();
    for (int i = rowCount; i >0; i--) {
      res.absolute(i);
      System.out.println(res.getString(1)+" - "+res.getString(2));
      res.updateString(2, "abc");
      res.updateRow();
    }
  }
}

sql.query("select * from jdbc");      

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-JEnHurJ1-1590568160487)(C:\Users\SXF\AppData\Roaming\Typora\typora-user-images\image-20200526175750855.png)]

處理Blob類型資料

  1. Blob(Binary Long Object)二進制長對象,用于存儲大檔案,典型的Blob内容是一張圖檔或一個聲音檔案。
  2. Blob資料插入資料庫需要使用PreparedStatement,該對象有一個方法:setBinaryStream(int index, InputStream x),該方法可以為指定參數傳入二進制輸入流,進而可以實作将Blob資料儲存到資料庫中。
  3. 調用ResultSet的**getBlob(int index)**方法可以從ResultSet裡取出Blob資料,該方法傳回一個Blob對象。
  4. Blob對象提供了**getBinaryStream()方法來擷取該Blob資料的輸入流,也可以使用Blob對象提供的getBytes()**方法直接取出該Blob對象封裝的二進制資料。
  5. 建立資料表時,建立一個mediumblob類型的資料列,用于儲存圖檔資料。
  6. MySQL裡資料庫裡的blob類型最多隻能存儲64KB内容,而mediumblob類型可以存儲16MB的内容。

示例:通過SQL的Blob存儲并讀取圖檔資料

public void blobDemo(String fileName) throws Exception
{
  Connection conn;
  PreparedStatement state;
  PreparedStatement query;
  Class.forName(driver);
  try {
    conn = DriverManager.getConnection(url, user, pwd);
    state = conn.prepareStatement("insert into jdbc values (?,?,?)", Statement.RETURN_GENERATED_KEYS);
    query = conn.prepareStatement("select image from jdbc");
    String imageName = fileName.substring(fileName.lastIndexOf('\\')+1, fileName.lastIndexOf('.'));
    var f = new File(fileName);
    var is = new FileInputStream(f);
    state.setObject(1, 8);
    state.setObject(2, "img");
    state.setBinaryStream(3, is, (int)f.length());
    int affect = state.executeUpdate();
    System.out.println(affect);
    ResultSet res = query.executeQuery();
    if(res.next()){
      Blob imgBlob = res.getBlob(3);
      var out = imgBlob.getBinaryStream();
    }
  }catch (Exception e){
    e.printStackTrace();
  }
}      

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-kpyagRVy-1590568160489)(C:\Users\SXF\AppData\Roaming\Typora\typora-user-images\image-20200526213528489.png)]

使用ResultSetMetaData分析結果集

描述其他資料的資料,用以擷取關于ResultSet的描述資訊。通過**getMetaData()**方法獲得ResultSetMetaData對象。需要一定的系統開銷。

三個常用方法:

  • int getColumnCount():傳回改ResultSet的列數量
  • String getColumnName(int column):傳回指定索引的列名
  • int getColumnType(int column):傳回指定索引的列類型

示例:分析結果集

public void metaData() throws Exception
{
  Class.forName(driver);
  Connection conn = DriverManager.getConnection(url, user, pwd);
  Statement state = conn.createStatement();
  ResultSet set = state.executeQuery("select * from jdbc");
  ResultSetMetaData rsmd = set.getMetaData();
  System.out.println(rsmd.getColumnCount());
  System.out.println(rsmd.getColumnName(2));
  System.out.println(rsmd.getColumnType(2));
}      

使用RowSet包裝結果集

RowSet接口下包含jdbcRowSet、CachedRowSet、FilteredRowSet、JoinRowSet、WebRowSet常用子接口。

jdbcRowSet需要保持與資料庫的連接配接;其餘4個是離線的RowSet,無需保持連接配接。

RowSetFactory和RowSet

RowSet常用方法:

setUrl(String url)、setUsername(String name)、setPassword(String pwd)、setCommand(String sql)、execute()

示例:通過RowSetFactory使用jdbcRowSet

public void update(String sql) throws Exception
{
  Class.forName(driver);
  // 建立RowSetFactory
  RowSetFactory factory = RowSetProvider.newFactory();
  // 建立執行個體
  JdbcRowSet jdbcRs = factory.createJdbcRowSet();
  // 設定連接配接資訊
  jdbcRs.setUrl(url);
  jdbcRs.setUsername(user);
  jdbcRs.setPassword(pwd);
  jdbcRs.setCommand(sql);
  // 執行查詢
  jdbcRs.execute();
  jdbcRs.afterLast();
  // 向前滾動結果集
  while (jdbcRs.previous()){
    System.out.println(jdbcRs.getString(1));
    // 修改記錄行
    jdbcRs.updateString(2, "123");
    // 送出修改
    jdbcRs.updateRow();
  }
}      

離線RowSet

直接将底層資料讀入記憶體中,封裝成RowSet對象,而RowSet對象完全可以當成Java Bean來使用。

示例:CachedRowSet離線操作SQL

public void query(String sql) throws Exception
{
  Class.forName(driver);
  // 擷取資料庫連接配接
  Connection conn = DriverManager.getConnection(url, user, pwd);
  Statement stmt = conn.createStatement();
  ResultSet res = stmt.executeQuery(sql);
  // 建立RowSetFactory
  RowSetFactory factory = RowSetProvider.newFactory();
  // 建立執行個體
  CachedRowSet cachedRs = factory.createCachedRowSet();
  // 裝填RowSet
  cachedRs.populate(res);
  // 關閉資源
  res.close();
  stmt.close();
  conn.close();

  cachedRs.afterLast();
  // 重新擷取資料庫連接配接
  Connection conn2 = DriverManager.getConnection(url, user, pwd);
  conn2.setAutoCommit(false);
  // 把對RowSet所做的修改同步到底層資料庫
  cachedRs.acceptChanges(conn2);
}      

事務處理

事務的概念

事務ACID特性:原子性atomicity、一緻性consistency、隔離性isolation、持續性durability

事務送出:

  • 顯示送出:使用commit
  • 自動送出:執行DDL或DCL語句,或程式正常退出

事務復原:

  • 顯式復原:使用rollback
  • 自動復原:系統錯誤,或強行退出

普通的送出、復原都會結束目前事務,但復原到指定中間點因為依然處于事務之中,是以不會結束目前事務。

JDBC事務支援

// 關閉自動送出,開啟事務
conn.setAutoCommit(false);
// 送出事務
conn.commit();
// 復原事務
conn.rollback();
// 建立儲存點
point = conn.setSavePoint();
// 復原到儲存點
conn.rollback(point);      

使用批量更新

  1. 多條SQL語句将被作為一批操作被同時收集,同時送出。
  2. 使用批量更新也需要先建立一個Statement對象,然後利用該對象的addBatch()方法将多條SQL語句同時收集起來,最後調用Statement對象的executeLargeBatch()或executeBatch()方法同時執行這些SQL語句。
  3. 如果在批量更新的addBatch()方法中添加select查詢語句,程式将直接出現錯誤。
  4. 為了讓批量操作可以正确地處理錯誤,必須把批量執行的操作視為單個事務,如果批量更新在執行過程中失敗,則讓事務復原到批量操作開始之前的狀态。薇樂大道這種效果,程式應該在開始批量操作之前先關閉自動送出,然後開始收集更新語句,當批量操作執行結束後,送出事務,并恢複之前的自動送出模式。
Statement stmt = conn.createStatement();
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
... ...
stmt.executeLargeBatch();