藍橋杯Java課程學習——JDBC(二)
技術標籤:藍橋杯Java課程學習筆記資料庫mysqljavajdbc
文章目錄
JDBC操作資料庫
通過使用 JDBC Statement
, CallableStatement
和PreparedStatement
介面定義的方法和屬性,使可以使用 SQL 或 PL/SQL 命令和從資料庫接收資料。它們還定義了許多方法,幫助消除 Java 和資料庫之間資料型別的差異。
介面 | 應用場景 |
---|---|
Statement | 當在執行時使用靜態 SQL 語句時(Statement 介面不能接收引數) |
CallableStatement | 當要訪問資料庫中的儲存過程時(CallableStatement 物件的介面還可以接收執行時輸入引數) |
PreparedStatement | 當計劃多次使用 SQL 語句時(PreparedStatement 介面接收在執行時輸入引數) |
Statement
我們要使用Statement
介面,第一步肯定是建立一個Statement
物件了。我們需要使用Connection
物件的createStatement()
方法進行建立。
Statement stmt = null;
try {
stmt = conn.createStatement( );
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
一旦建立了一個 Statement 物件,我們就可以用它來執行 SQL 語句了
方法 | 說明 |
---|---|
boolean execute(String SQL) | 如果 ResultSet 物件可以被檢索返回布林值 true,否則返回 false。使用這個方法來執行 SQL DDL 語句,或當需要使用真正的動態 SQL |
int executeUpdate(String SQL) | 用於執行 INSERT、UPDATE 或 DELETE 語句以及 SQLDDL(資料定義語言)語句。返回值是一個整數,指示受影響的行數(即更新計數) |
ResultSet executeQuery(String SQL) | 返回 ResultSet 物件。用於產生單個結果集的語句,例如 SELECT 語句 |
正如關閉一個 Connection 物件來釋放資料庫連線資源,出於同樣的原因,也應該關閉 Statement 物件。
Statement stmt = null;
try {
stmt = conn.createStatement( );
. . .
}
catch (SQLException e) {
. . .
}
finally {
stmt.close();
}
注:如果關閉了 Connection 物件首先它會關閉 Statement 物件,然而應該始終明確關閉 Statement 物件,以確保正確的清除。
PreparedStatement
PreparedStatement
介面擴充套件了Statement
介面,有利於高效地執行多次使用的 SQL 語句。我們先來建立一個PreparedStatement
物件。 Statement
為一條 SQL 語句生成執行計劃。如果要執行兩條 SQL 語句,會生成兩個執行計劃。一萬個查詢就生成一萬個執行計劃!
select colume from table where colume=1;
select colume from table where colume=2;
PreparedStatement 用於使用繫結變數重用執行計劃。
select colume from table where colume=:x;
通過 set 不同資料,只需要生成一次執行計劃,並且可以重用。
PreparedStatement pstmt = null;
try {
/*
在JDBC中所有的引數都被代表?符號,這是已知的引數標記。在執行SQL語句之前,必須提供值的每一個引數。
*/
String SQL = "Update Students SET age = ? WHERE id = ?";
pstmt = conn.prepareStatement(SQL);
. . .
}
/*
setXXX()方法將值繫結到引數,其中XXX表示希望繫結到輸入引數值的 Java 資料型別。如果忘了提供值,將收到一個 SQLException。
*/
catch (SQLException e) {
. . .
}
finally {
//同理,我們需要關閉 PreparedStatement 物件
pstmt.close();
}
示例
- 先建立資料庫和相應的內容:
create database EXAMPLE;
use EXAMPLE
create table Students
(
id int not null,
age int not null,
name varchar(255),
primary key(id)
);
insert into Students values(1,18,'Tom'),
(2,20,'Aby'),(4,20,'Tomson');
- Java程式碼
import java.sql.*;
public class JdbcTest {
// JDBC 驅動器的名稱和資料庫地址
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/EXAMPLE";
static final String USER = "root";
static final String PASS = "";
public static void main(String[] args) {
Connection conn = null;
PreparedStatement stmt = null;
try{
//註冊 JDBC 驅動器
Class.forName("com.mysql.jdbc.Driver");
//開啟連線
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
//執行查詢
System.out.println("Creating statement...");
//這裡我們要更改一個同學的年齡,引數待定
String sql = "UPDATE Students set age=? WHERE id=?";
stmt = conn.prepareStatement(sql);
//將值繫結到引數,引數從左至右序號為1,2...
stmt.setInt(1, 22); // 繫結 age 的值(序號為1)
stmt.setInt(2, 1); // 繫結 ID 的值
// 更新 ID 為1的同學的年齡
int rows = stmt.executeUpdate();
System.out.println("被影響的行數 : " + rows );
// 查詢所有記錄,並顯示.
sql = "SELECT id, name, age FROM Students";
ResultSet rs = stmt.executeQuery(sql);
//處理結果集
while(rs.next()){
//檢索
int id = rs.getInt("id");
int age = rs.getInt("age");
String name = rs.getString("name");
//顯示
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", Name: " + name);
System.out.println();
}
//清理
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
se.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(stmt!=null)
stmt.close();
}catch(SQLException se2){
}
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
}
CallableStatement
CallableStatement
物件為所有的 DBMS 提供了一種以標準形式呼叫儲存過程的方法。儲存過程儲存在資料庫中。對儲存過程的呼叫是 CallableStatement
物件所含的內容。三種類型的引數有:IN
,OUT
和INOUT
。
PreparedStatement
物件只使用 IN 引數。CallableStatement
物件可以使用所有三個
引數 | 描述 |
---|---|
IN | 它的值是在建立 SQL 語句時未知的引數,將 IN 引數傳給 CallableStatement 物件是通過 setXXX() 方法完成的 |
OUT | 其值由它返回的 SQL 語句提供的引數。從 OUT 引數的 getXXX() 方法檢索值 |
INOUT | 同時提供輸入和輸出值的引數,繫結的 setXXX() 方法的變數,並使用 getXXX() 方法檢索值 |
在 JDBC 中呼叫儲存過程的語法如下所示。注意,方括號表示其間的內容是可選項;方括號本身並不是語法的組成部份。
{call 儲存過程名[(?, ?, ...)]}
返回結果引數的過程的語法為:
{? = call 儲存過程名[(?, ?, ...)]}
不帶引數的儲存過程的語法類似:
{call 儲存過程名}
CallableStatement 物件是用 Connection 方法 prepareCall 建立的。
CallableStatement cstmt = null;
try {
String SQL = "{call getEXAMPLEName (?, ?)}";
cstmt = conn.prepareCall (SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
cstmt.close();
}
JDBC 結果集
結果集通常是通過執行查詢資料庫的語句生成,表示資料庫查詢結果的資料表。
ResultSet 介紹
ResultSet
物件具有指向其當前資料行的游標。最初,游標被置於第一行之前。next
方法將游標移動到下一行;因為該方法在ResultSet
物件沒有下一行時返回false
,所以可以在while
迴圈中使用它來迭代結果集。游標可以方便我們對結果集進行遍歷。預設的ResultSet
物件不可更新,僅有一個向前移動的游標。因此,只能迭代它一次,並且只能按從第一行到最後一行的順序進行。
ResultSet
介面的方法可分為三類:
- 導航方法:用於移動游標
- 獲取方法:用於檢視當前行的游標所指向的列中的資料
- 更新方法:用於更新當前行的列中的資料
JDBC 提供下列連線方法來建立所需的ResultSet
語句:
createStatement(int RSType, int RSConcurrency);
prepareStatement(String SQL, int RSType, int RSConcurrency);
prepareCall(String sql, int RSType, int RSConcurrency);
RSType
表示 ResultSet 物件的型別
RSConcurrency
是 ResultSet 常量,用於指定一個結果集是否為只讀或可更新。
ResultSet 的型別
,如果不指定 ResultSet 型別,將自動獲得一個是 TYPE_FORWARD_ONLY:
型別 | 描述 |
---|---|
ResultSet.TYPE_FORWARD_ONLY | 遊標只能向前移動的結果集 |
ResultSet.TYPE_SCROLL_INSENSITIVE | 遊標可以向前和向後滾動,但不及時更新,就是如果資料庫裡的資料修改過,並不在 ResultSet 中反應出來 |
ResultSet.TYPE_SCROLL_SENSITIVE | 遊標可以向前和向後滾動,並及時跟蹤資料庫的更新,以便更改 ResultSet 中的資料 |
併發性的 ResultSet,如果不指定任何併發型別,將自動獲得一個為 CONCUR_READ_ONLY
併發 | 描述 |
---|---|
ResultSet.CONCUR_READ_ONLY | 建立結果集只讀。這是預設的 |
ResultSet.CONCUR_UPDATABLE | 建立一個可更新的結果集 |
如初始化一個 Statement 物件來建立一個雙向、可更新的 ResultSet 物件:
try {
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
}
catch(Exception ex) {
....
}
finally {
....
}
導航
方法 | 說明 |
---|---|
public void beforeFirst() throws SQLException | 將游標移動到正好位於第一行之前 |
public void afterLast() throws SQLException | 將游標移動到剛剛結束的最後一行 |
public boolean first() throws SQLException | 將游標移動到第一行 |
public void last() throws SQLException | 將游標移動到最後一行 |
public boolean absolute(int row) throws SQLException | 將游標移動到指定的行 |
public boolean relative(int row) throws SQLException | 從它目前所指向向前或向後移動游標行的給定數量 |
public boolean previous() throws SQLException | 將游標移動到上一行。上一行關閉的結果集此方法返回 false |
public boolean next() throws SQLException | 將游標移動到下一行。如果沒有更多的行結果集中的此方法返回 false |
public int getRow() throws SQLException | 返回的行號,該游標指向的行 |
public void moveToInsertRow() throws SQLException | 將游標移動到一個特殊的行,可以用來插入新行插入到資料庫中的結果集。當前游標位置被記住 |
public void moveToCurrentRow() throws SQLException | 移動游標返回到當前行,如果游標在當前插入行,否則,這個方法不執行任何操作 |
示例
- 先建立資料庫和相應的內容:
create database EXAMPLE;
use EXAMPLE
create table Students
(
id int not null,
age int not null,
name varchar(255),
primary key(id)
);
insert into Students values(1,18,'Tom'),
(2,20,'Aby'),(4,20,'Tomson');
- Java程式碼
import java.sql.*;
public class JdbcTest {
// JDBC 驅動器名稱 和資料庫地址
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
//資料庫的名稱為 EXAMPLE
static final String DB_URL = "jdbc:mysql://localhost/EXAMPLE";
// 資料庫使用者和密碼
static final String USER = "root";
static final String PASS = "";
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{
//註冊JDBC 驅動程式
Class.forName("com.mysql.jdbc.Driver");
//開啟連線
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
System.out.println("Creating statement...");
//建立所需的ResultSet,雙向,只讀
stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
String sql;
sql = "SELECT id, name, age FROM Students";
ResultSet rs = stmt.executeQuery(sql);
// 將游標移到最後一行
System.out.println("Moving cursor to the last...");
rs.last();
//處理結果集
System.out.println("Displaying record...");
int id = rs.getInt("id");
int age = rs.getInt("age");
String name = rs.getString("name");
//顯示
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", Name: " + name);
System.out.println();
// 將游標移到第一行
System.out.println("Moving cursor to the first row...");
rs.first();
System.out.println("Displaying record...");
id = rs.getInt("id");
age = rs.getInt("age");
name = rs.getString("name");
//顯示
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", Name: " + name);
//將游標移至下一行
System.out.println("Moving cursor to the next row...");
rs.next();
System.out.println("Displaying record...");
id = rs.getInt("id");
age = rs.getInt("age");
name = rs.getString("name");
// 顯示
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", Name: " + name);
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
se.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(stmt!=null)
stmt.close();
}catch(SQLException se2){
}
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
}
獲取
ResultSet
介面中我們經常使用 get
方法來檢視結果集。
方法 | 說明 |
---|---|
public int getInt(String columnName) throws SQLException | 當前行中名為 ColumnName 列的值 |
public int getInt(int columnIndex) throws SQLException | 當前行中指定列的索引的值。列索引從 1 開始,意味著一個行的第一列是 1,行的第二列是 2,依此類推 |
當然還有getString()
等等。
更新
方法 | 說明 |
---|---|
public void updateString(int columnIndex, String s) throws SQLException | 指定列中的字串更改為 s 的值 |
public void updateString(String columnName, String s) throws SQLException | 類似於前面的方法,不同之處在於由它的名稱,而不是它的索引指定的列 |
類似的還有updateDouble()
等等。
我們在更新了結果集中的內容,當然需要更新一下資料庫了。我們可以呼叫下面的方法更新資料庫。
方法 | 說明 |
---|---|
public void updateRow() | 通過更新資料庫中相應的行更新當前行 |
public void deleteRow() | 從資料庫中刪除當前行 |
public void refreshRow() | 重新整理在結果集的資料,以反映最新變化在資料庫中 |
public void cancelRowUpdates() | 取消所做的當前行的任何更新 |
public void insertRow() | 插入一行到資料庫中。當游標指向插入行此方法只能被呼叫 |
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
String sql = "SELECT id, name, age FROM Students";
ResultSet rs = stmt.executeQuery(sql);
//結果集中插入新行
rs.moveToInsertRow();
rs.updateInt("id",5);
rs.updateString("name","John");
rs.updateInt("age",21);
//更新資料庫
rs.insertRow();
JDBC 事務
我們在編寫 java 程式的時候,在預設情況下,JDBC 連線是在自動提交模式下,即每個 SQL 語句都是在其完成時提交到資料庫。但有時候我們為了提高程式執行的效能或者保持業務流程的完整性,以及使用了分散式事務管理方式,這個時候我們可能想關閉自動提交而自己管理和控制自己的事務。
讓多條 SQL 在一個事務中執行,並且保證這些語句是在同一時間共同執行的時候,我們就應該為這多條語句定義一個事務。一個事務是把單個 SQL 語句或一組 SQL 語句作為一個邏輯單元,並且如果事務中任何語句失敗,則整個事務失敗。
如果我們要啟動一個事務,而不是讓 JDBC 驅動程式預設使用 auto-commit 模式支援。這個時候我們就要使用 Connection 物件的setAutoCommit()
方法。我們傳遞一個布林值 false 到setAutoCommit()
中,就可以關閉自動提交。反之我們傳入一個 true 便將其重新開啟。
Connection conn = null;
conn = DriverManager.getConnection(URL);
//關閉自動提交
conn.setAutoCommit(false);
我們關閉了自動提交後,**如果我們要提交資料庫更改怎麼辦呢?**這時候就要用到我們的提交和回滾了。我們要提交更改,可以呼叫commit()
方法:
conn.commit();
尤其不要忘記,在 catch 塊內添加回滾事務,表示操作出現異常,撤銷事務:
conn.rollback();
插入資料
JDBC 插入資料使用的頻率非常高,接下來說明如何使用 JDBC 插入資料。
示例
import java.sql.*;
public class JdbcInsertTest {
public static Connection connection = null;
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/EXAMPLE", "root", "");
Statement statement = connection.createStatement();
//單條插入
boolean execute = statement.execute("insert into Students values (0,1,'shiyanlou')");
if (execute) {
System.out.println("插入失敗");
}else {
System.out.println("單條插入成功");
}
// 批量插入 需要關閉自動提交
connection.setAutoCommit(false);
String sql = "insert into Students values (?,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//設定插入的值
for (int i = 1; i <= 10; i++) {
preparedStatement.setInt(1, i);
preparedStatement.setInt(2, i + 10);
preparedStatement.setString(3, "shiyanlou");
preparedStatement.addBatch();
}
//執行批量插入,使用executeBatch 方法
preparedStatement.executeBatch();
//提交到資料庫
connection.commit();
System.out.println("提交批量插入完成");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}