1.簡介
JDBC(Java Data Base Connectivity,java數據庫連接)是一種用于執行SQL語句的Java API,可以為多種關系數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基準,據此可以構建更高級的工具和接口,使數據庫開發人員能夠編寫數據庫應用程序。
本文介紹如何通過 JDBC 連接瀚高數據庫并執行各種數據操作。
2.數據庫驅動
JDBC是對數據庫操作的接口抽象,而不同數據庫廠商的數據庫驅動程序則對應JDBC接口實現,通過抽象出JDBC接口,應用程序和實際的數據庫驅動即JDBC實現解耦。
在數據庫安裝目錄 interfaces/jdbc 下,獲得驅動jar包hgdb-jdbc-xxx.jar。該驅動包與PostgreSQL保持兼容,其中類名、類結構與 PostgreSQL 驅動完全一致,曾經運行于 PostgreSQL 的應用程序可以直接移植到當前系統使用。
3.常用接口
3.1.Driver接口
Driver接口由數據庫廠家提供,作為java開發人員,只需要使用Driver接口就可以了。在編程中要連接數據庫,必須先裝載特定廠商的數據庫驅動程序,不同的數據庫有不同的裝載方法。如:
裝載瀚高驅動:Class.forName(“org.postgresql.Driver”);
3.2.Connection接口
Connection與特定數據庫的連接(會話),在連接上下文中執行sql語句并返回結果。
連接數據庫:Connection conn = DriverManager.getConnection(“url”, “user”, “password”);
參數說明:
參數 | 描述 |
---|---|
url | 瀚高數據庫連接URL支持的格式如下: ? jdbc:postgresql:database ? jdbc:postgresql://host/database ? jdbc:postgresql://host:port/database ? jdbc:postgresql://host:port/database?param1=value1¶m2=value2 ? jdbc:postgresql://host1:port1,host2:port2/database?param1=value1¶m2=value2 說明: ? database 為要連接的數據庫名稱。 ? host 為數據庫服務器名稱或IP地址。缺省情況下,連接服務器為localhost。 ? port 為數據庫服務器端口。缺省情況下,會嘗試連接到5866端口的database。 ? param 為參數名稱,即數據庫連接屬性。參數可以配置在URL中,以“?”開始配置,以“=”給參數賦值,以“&”作為不同參數的間隔。 ? value 為參數值,即數據庫連接屬性值。連接時需配置connectTimeout、socketTimeout,如果未配置,默認為0,即不會超時。 |
user | 數據庫用戶名 |
password | 數據庫用戶密碼 |
常用方法:
createStatement():創建向數據庫發送sql的statement對象。
prepareStatement(sql) :創建向數據庫發送預編譯sql的PrepareSatement對象。
prepareCall(sql):創建執行存儲過程的callableStatement對象。
setAutoCommit(boolean autoCommit):設置事務是否自動提交。
commit() :在鏈接上提交事務。
rollback() :在此鏈接上回滾事務。
3.3.Statement接口
用于執行靜態SQL語句并返回它所生成結果的對象。
三種Statement類:
- Statement:由createStatement創建,用于發送簡單的SQL語句(不帶參數)。
- PreparedStatement :繼承自Statement接口,由preparedStatement創建,用于發送含有一個或多個參數的SQL語句。PreparedStatement對象比Statement對象的效率更高,并且可以防止SQL注入,所以我們一般都使用PreparedStatement。
- CallableStatement:繼承自PreparedStatement接口,由方法prepareCall創建,用于調用存儲過程。
常用Statement方法:
- execute(String sql):運行語句,返回是否有結果集
- executeQuery(String sql):運行select語句,返回ResultSet結果集。
- executeUpdate(String sql):運行insert/update/delete操作,返回更新的行數。
- addBatch(String sql) :把多條sql語句放到一個批處理中。
- executeBatch():向數據庫發送一批sql語句執行。
3.4.ResultSet接口
ResultSet提供檢索不同類型字段的方法,常用的有:
- getString(int index)、getString(String columnName):獲得在數據庫里是varchar、char等類型的數據對象。
- getFloat(int index)、getFloat(String columnName):獲得在數據庫里是Float類型的數據對象。
- getDate(int index)、getDate(String columnName):獲得在數據庫里是Date類型的數據。
- getBoolean(int index)、getBoolean(String columnName):獲得在數據庫里是Boolean類型的數據。
- getObject(int index)、getObject(String columnName):獲取在數據庫里任意類型的數據。
ResultSet還提供了對結果集進行滾動的方法:
- next():移動到下一行
- Previous():移動到前一行
- absolute(int row):移動到指定行
- beforeFirst():移動resultSet的最前面。
- afterLast() :移動到resultSet的最后面。
注意:使用后依次關閉對象及連接:ResultSet → Statement → Connection
4.使用JDBC步驟
加載JDBC驅動程序 → 建立數據庫連接Connection → 創建執行SQL的語句Statement → 處理執行結果ResultSet → 釋放資源
4.1.注冊驅動
Class.forName("org.postgresql.Driver");
4.2.建立連接
Connection conn = DriverManager.getConnection(url, user, password);
4.3.創建執行SQL語句的statement
statement
存在sql注入的危險,如果用戶傳入的id為“5 or 1=1”,那么將刪除表中的所有記錄。
String id = "5";
String sql = "delete from t where id=" + id;
Statement st = conn.createStatement();
st.executeQuery(sql);
PreparedStatement
有效的防止sql注入(SQL語句在程序運行前已經進行了預編譯,當運行時動態地把參數傳給PreprareStatement時,即使參數里有敏感字符如 or ‘1=1’ 數據庫會作為一個參數一個字段的屬性值來處理而不會作為一個SQL指令)
String sql = "insert into user (name,pwd) values(?,?)";
PreparedStatement ps = conn.preparedStatement(sql);
ps.setString(1, "col_value"); //占位符順序從1開始
ps.setString(2, "123456"); //也可以使用setObject
ps.executeQuery();
4.4.處理執行結果(ResultSet)
ResultSet rs = ps.executeQuery();
While(rs.next()){rs.getString("col_name");rs.getInt(1);//…
}
4.5.釋放資源
數據庫連接(Connection)非常耗資源,盡量晚創建,盡量早的釋放 。
都要加try catch 以防前面關閉出錯,后面的就不執行了 。
try { if (rs != null) {rs.close();}
} catch (SQLException e) {e.printStackTrace();
} finally {try {if (st != null) {st.close();}} catch (SQLException e) {e.printStackTrace();} finally {try {if (conn != null) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}
}
5.示例
5.1.連接數據庫
通過 JDBC 連接瀚高數據庫的過程如下:
- 使用 Class.forName() 方法顯式地加載驅動類;
- 利用 DriverManager 類的 getConnection() 方法獲取一個 Connection 連接對象。
private static final String JDBC_DRIVER = "org.postgresql.Driver";
private static final String JDBC_URL = "jdbc:postgresql://192.168.100.101:5866/highgo?escapeSyntaxCallMode=callIfNoReturn";
private static final String JDBC_USER = "highgo";
private static final String JDBC_PASSWORD = "111111";/*** 獲取數據庫連接* @return Connection 數據庫連接對象* @throws SQLException,ClassNotFoundException*/
public static Connection getConnection() throws SQLException, ClassNotFoundException {//下面一行注冊驅動代碼可省略。Class.forName(JDBC_DRIVER);return DriverManager.getConnection(JDBC_URL,JDBC_USER,JDBC_PASSWORD);
}
- JDBC_DRIVER 指定數據庫驅動,從 JDBC 4.0 開始,驅動程序會通過 META-INF/services/java.sql.Driver 文件自動注冊,因此不需要顯式調用Class.forName();
- JDBC_URL 中指定了數據庫的 IP 地址、端口以及目標數據庫;
- JDBC_USER 和 JDBC_PASSWORD 指定數據庫的用戶和密碼。
JDBC_URL 中 escapeSyntaxCallMode 是一個重要的配置參數,用于控制如何將 JDBC 轉義調用語法(如 {call …} 或 {?= call …})轉換為底層的 SQL 語句。這個參數對存儲過程(PROCEDURE)和函數(FUNCTION)的調用行為有直接影響。
escapeSyntaxCallMode 的三種模式及行為:
- select 模式(默認)
- 行為:JDBC 驅動會將所有 {call …} 或 {?= call …} 轉換為 SELECT 語句。
- 適用場景:
- 僅適用于調用函數(FUNCTION),因為瀚高數據庫的函數可以通過 SELECT 調用。
- 不支持直接調用存儲過程(PROCEDURE)。
- callIfNoReturn 模式
- 行為:
- 如果調用沒有返回值(即沒有 OUT 參數或返回結果集),JDBC 驅動會使用 CALL 語句。
- 如果調用有返回值,則仍然使用 SELECT 語句。
- 適用場景:
- 適用于同時調用存儲過程和函數的混合場景。
- 存儲過程通常不返回值,因此會使用 CALL。
- 函數可能返回值,因此會使用 SELECT。
- call 模式
- 行為:JDBC 驅動始終使用 CALL 語句,無論是否有返回值。
- 適用場景:
- 僅適用于調用 存儲過程(PROCEDURE)。
- 不支持調用函數(FUNCTION),因為瀚高數據庫的函數無法通過 CALL 調用。
5.2.創建表
通過 JDBC 連接瀚高數據庫并執行 DDL 語句的過程如下:
- 利用 DriverManager 類的 getConnection() 方法獲取一個 Connection 連接對象;
- 使用連接對象的 createStatement() 方法創建一個 Statement 語句對象;
- 利用語句對象的 execute() 方法執行 SQL 語句;
- 釋放 Statement 以及 Connection 對象資源。
/*** 創建表*/
public static void create(){Connection conn = null;Statement stmt = null;String sql = "create table users (" +"id serial primary key," +"name varchar(50) not null unique," +"create_time timestamp not null" +")";try {conn = getConnection(); //獲取連接System.out.println("連接瀚高數據庫成功!");stmt = conn.createStatement(); //創建statement對象stmt.execute(sql); //執行sqlSystem.out.println("成功創建 users 表!");} catch (Exception e) {e.printStackTrace();} finally { //釋放資源try {if(stmt != null) stmt.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(conn != null) conn.close();} catch (Exception e){e.printStackTrace();}}}
}
5.3.插入數據
通過 JDBC 連接瀚高數據庫并執行插入操作的過程如下:
- 利用 DriverManager 類的 getConnection() 方法獲取一個 Connection 連接對象;
- 使用連接對象的 createStatement() 方法創建一個 Statement 或者 PreparedStatement 語句對象;
- 利用語句對象的 execute() 或者 executeBatch() 方法執行 INSERT 語句;
- 釋放 Statement 以及 Connection 對象資源。
/***插入數據*/
public static void insert(){Connection conn = null;PreparedStatement pstmt = null;String sql = "insert into users (name,create_time) values(?,?)";try {conn = getConnection();//獲取連接pstmt = conn.prepareStatement(sql); //創建prepareStatement對象pstmt.setString(1,"John"); //綁定參數1pstmt.setTimestamp(2,new Timestamp(System.currentTimeMillis())); //綁定參數2pstmt.addBatch();pstmt.setString(1,"Robert"); //綁定參數1pstmt.setTimestamp(2,new Timestamp(System.currentTimeMillis())); //綁定參數2pstmt.addBatch();int[] rowsAffected = pstmt.executeBatch(); //執行sqlSystem.out.println("數據寫入{" + rowsAffected.length + "}行!");} catch (Exception e) {e.printStackTrace();} finally { //釋放資源try {if(pstmt != null) pstmt.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(conn != null) conn.close();} catch (Exception e){e.printStackTrace();}}}
}
5.4.查詢數據
通過 JDBC 連接瀚高數據庫并執行查詢語句的過程如下:
- 利用 DriverManager 類的 getConnection() 方法獲取一個 Connection 連接對象;
- 使用連接對象的 createStatement() 方法創建一個 Statement 或者 PreparedStatement 語句對象;
- 利用語句對象的 executeQuery() 方法執行 SQL 語句或者存儲過程,返回一個 ResultSet 結果集對象;
- 遍歷結果集,獲取并處理查詢結果;
- 釋放 ResultSet、Statement 以及 Connection 對象資源。
/***查詢數據*/
private static void select(){Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;String sql = "select * from users";try {conn = getConnection(); //獲取連接pstmt = conn.prepareStatement(sql); //創建prepareStatement對象rs = pstmt.executeQuery(); //執行sqlwhile(rs.next()){ //處理結果集System.out.println(rs.getInt("id") + "\t" +rs.getString("name") + "\t" +rs.getTimestamp("create_time"));}} catch (Exception e) {e.printStackTrace();} finally { //釋放資源try {if(rs != null) rs.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(pstmt != null) pstmt.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(conn != null) conn.close();} catch (Exception e){e.printStackTrace();}}}}
}
5.5.修改數據
通過 JDBC 連接瀚高數據庫并執行更新語句的過程如下:
- 利用 DriverManager 類的 getConnection() 方法獲取一個 Connection 連接對象;
- 使用連接對象的 createStatement() 方法創建一個 Statement 或者 PreparedStatement 語句對象;
- 利用語句對象的 executeUpdate() 方法執行 UPDATE 語句;
- 釋放 Statement 以及 Connection 對象資源。
/*** 修改數據*/
private static void update(){Connection conn = null;PreparedStatement pstmt = null;String sql = "update users set name=? where id=?";try {conn = getConnection(); //獲取連接pstmt = conn.prepareStatement(sql); //創建prepareStatement對象pstmt.setString(1,"Tom"); //綁定參數1pstmt.setInt(2,1); //綁定參數2int rowsAffected = pstmt.executeUpdate(); //執行sqlSystem.out.println("更新行數: {"+rowsAffected+"}");} catch (Exception e) {e.printStackTrace();} finally { //釋放資源try {if(pstmt != null) pstmt.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(conn != null) conn.close();} catch (Exception e){e.printStackTrace();}}}
}
5.6.刪除數據
通過 JDBC 連接瀚高數據庫并執行刪除語句的過程如下:
- 利用 DriverManager 類的 getConnection() 方法獲取一個 Connection 連接對象;
- 使用連接對象的 createStatement() 方法創建一個 Statement 或者 PreparedStatement 語句對象;
- 利用語句對象的 executeUpdate() 方法執行 DELETE 語句;
- 釋放 Statement 以及 Connection 對象資源。
/*** 刪除數據*/
private static void delete(){Connection conn = null;PreparedStatement pstmt = null;String sql = "delete from users where id=?";try {conn = getConnection(); //獲取連接pstmt = conn.prepareStatement(sql); //創建prepareStatement對象pstmt.setInt(1,1); //綁定參數1int rowsAffected = pstmt.executeUpdate(); //執行sqlSystem.out.println("刪除行數: {"+rowsAffected+"}");} catch (Exception e) {e.printStackTrace();} finally {//釋放資源try {if(pstmt != null) pstmt.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(conn != null) conn.close();} catch (Exception e){e.printStackTrace();}}}
}
5.7.處理事務
默認情況下,JDBC 連接瀚高數據庫時使用自動提交模式,意味著每個 SQL 語句都會自動執行事務的提交操作。如果我們想要在一個事務中執行多條 SQL 語句,需要禁用連接對象的自動提交屬性,并且手動執行 COMMIT 或者 ROLLBACK 操作。
創建連接之后,使用 setAutoCommit() 方法禁用自動提交;然后分別執行插入語句和更新語句,并提交事務;在異常處理中回滾事務并打印錯誤消息。
/*** 處理事務*/
private static void transaction(){Connection conn = null;PreparedStatement pstmt = null;PreparedStatement pstmt2= null;String sql = "insert into users (name,create_time) values(?,?)";String sql2 = "update users set name = ? where id = ?";try {conn = getConnection(); //獲取連接conn.setAutoCommit(false); //設置手動提交pstmt = conn.prepareStatement(sql); //創建prepareStatement對象pstmt.setString(1,"James"); //綁定參數1pstmt.setTimestamp(2,new Timestamp(System.currentTimeMillis())); //綁定參數2pstmt.executeUpdate(); //執行sqlpstmt2 = conn.prepareStatement(sql2); //創建prepareStatement對象pstmt2.setString(1,"James"); //綁定參數1pstmt2.setInt(2,2); //綁定參數2pstmt2.executeUpdate(); //執行sqlconn.commit(); // 提交事務System.out.println("事務提交成功!");} catch (Exception e) {try {conn.rollback(); // 回滾事務System.out.println("事務回滾!");} catch (SQLException ex) {e.printStackTrace();}e.printStackTrace();} finally {try {if(pstmt != null) pstmt.close();if(pstmt2 != null) pstmt2.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(conn != null) {conn.setAutoCommit(true); //設置自動提交conn.close();}} catch (Exception e){e.printStackTrace();}}}
}
5.8.調用函數
通過 JDBC 連接瀚高數據庫并執行函數調用的過程如下:
- 利用 DriverManager 類的 getConnection() 方法獲取一個 Connection 連接對象;
- 使用連接對象的 setAutoCommit() 方法禁用自動提交;
- 使用連接對象的 prepareCall() 方法創建一個 CallableStatement 語句對象;
- 利用語句對象的 execute() 方法執行函數調用,返回一個 ResultSet 結果集對象;
- 遍歷結果集,獲取并處理查詢結果;
- 釋放 ResultSet、CallableStatement 以及 Connection 對象資源。
CREATE OR REPLACE FUNCTION get_users_with_cursor_function() returns REFCURSOR AS $$
DECLAREref_cursor REFCURSOR;
BEGINOPEN ref_cursor FOR SELECT id, name, create_time FROM users;return ref_cursor;
END;
$$ LANGUAGE plpgsql;
/*** 調用函數*/
private static void callFunction(){Connection conn = null;CallableStatement cstmt = null;ResultSet rs = null;String callSQL = "{? = call get_users_with_cursor_function()}";try {conn = getConnection(); //獲取連接conn.setAutoCommit(false); //設置手動提交cstmt = conn.prepareCall(callSQL); //創建prepareCall對象cstmt.registerOutParameter(1, Types.REF_CURSOR); //注冊輸出參數為游標類型cstmt.execute(); //執行存儲過程rs = (ResultSet) cstmt.getObject(1); //獲取返回的游標結果集// 遍歷游標中的數據while (rs.next()) {System.out.println(rs.getInt("id") + "\t" +rs.getString("name") + "\t" +rs.getTimestamp("create_time"));}conn.commit(); //提交事務} catch (Exception e) {try {conn.rollback(); // 回滾事務System.out.println("事務回滾!");} catch (SQLException ex) {e.printStackTrace();}e.printStackTrace();} finally {try {if(rs != null) rs.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(cstmt != null) cstmt.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(conn != null) {conn.setAutoCommit(true); //設置自動提交conn.close();}} catch (Exception e){e.printStackTrace();}}}}
}
5.9.調用存儲過程
通過 JDBC 連接瀚高數據庫并執行存儲過程調用的過程如下:
- 利用 DriverManager 類的 getConnection() 方法獲取一個 Connection 連接對象;
- 使用連接對象的 setAutoCommit() 方法禁用自動提交;
- 使用連接對象的 prepareCall() 方法創建一個 CallableStatement 語句對象;
- 利用語句對象的 execute() 方法執行存儲過程調用,返回一個 ResultSet 結果集對象;
- 遍歷結果集,獲取并處理查詢結果;
- 釋放 ResultSet、CallableStatement 以及 Connection 對象資源。
CREATE OR REPLACE PROCEDURE get_users_with_cursor_procedure(OUT ref_cursor REFCURSOR) AS $$
BEGINOPEN ref_cursor FOR SELECT id, name, create_time FROM users;
END;
$$ LANGUAGE plpgsql;
/*** 調用存儲過程*/
private static void callProcedure(){Connection conn = null;CallableStatement cstmt = null;ResultSet rs = null;String callSQL = "{ CALL get_users_with_cursor_procedure(?) }";try {conn = getConnection(); //獲取連接conn.setAutoCommit(false); //設置手動提交cstmt = conn.prepareCall(callSQL); //創建prepareCall對象cstmt.registerOutParameter(1, Types.REF_CURSOR); //注冊輸出參數為游標類型cstmt.execute(); //執行存儲過程rs = (ResultSet) cstmt.getObject(1); //獲取返回的游標結果集// 遍歷游標中的數據while (rs.next()) {System.out.println(rs.getInt("id") + "\t" +rs.getString("name") + "\t" +rs.getTimestamp("create_time"));}conn.commit(); //提交事務} catch (Exception e) {try {conn.rollback(); // 回滾事務System.out.println("事務回滾!");} catch (SQLException ex) {e.printStackTrace();}e.printStackTrace();} finally {try {if(rs != null) rs.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(cstmt != null) cstmt.close();} catch (Exception e) {e.printStackTrace();} finally {try {if(conn != null) {conn.setAutoCommit(true); //設置自動提交conn.close();}} catch (Exception e){e.printStackTrace();}}}}
}