1.什麼是JDBC
DBC(Java Data Base Connectivity,java資料庫連線)是一種用於執行SQL語句的Java API,可以為多種關聯式資料庫提供統一訪問,它由一組用Java語言編寫的類和介面組成。JDBC提供了一種基準,據此可以構建更高階的工具和介面,使資料庫開發人員能夠編寫資料庫應用程式。
在程式中包含資料庫程式設計所需的JDBC類。大多數情況下,使用import java.sql.*
1.Driver介面
Driver介面由資料庫廠家提供,作為java開發人員,只需要使用Driver介面就可以了。在程式設計中要連線資料庫,必須先裝載特定廠商的資料庫驅動程式,不同的資料庫有不同的裝載方法。如:
// Oracle (thin)資料庫
Class.forName("oracle.jdbc.driver.OracleDriver");
// MySQL資料庫
Class.forName("com.mysql.jdbc.Driver");
// SQLServer2000資料庫
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
2.Connection介面
Connection與特定資料庫的連線(會話),在連線上下文中執行sql語句並返回結果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定義的資料庫Connection連線上。
// Oracle (thin)資料庫
String URL="jdbc:oracle:thin:@localhost:1521:myDB";
// MySQL資料庫
String URL = "jdbc:mysql://localhost:3306/myDB";
// SQLServer2000資料庫
String URL ="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=myDB";
//建立連線
Connection con = DriverManager.getConnection(URL,UserName,Password);
常用方法:
方法名 | 說明 |
---|---|
Statement createStatement() | 建立向資料庫傳送sql的statement物件 |
PreparedStatement prepareStatement(String sql) | 建立向資料庫傳送預編譯sql的PrepareSatement物件 |
prepareCall(sql) | 建立執行儲存過程的callableStatement物件 |
setAutoCommit(boolean autoCommit) | 設定事務是否自動提交 |
boolean isClosed() | 查詢此Connection物件是否已經被關閉 |
commit() | 在連結上提交事務 |
rollback() | 在此連結上回滾事務 |
3.Statement介面
用於執行靜態SQL語句並返回它所生成結果的物件。
三種Statement類:
類名 | 說明 |
---|---|
Statement | 由createStatement建立,用於傳送簡單的SQL語句(不帶引數) |
PreparedStatement | 繼承自Statement介面,由preparedStatement建立,用於傳送含有一個或多個引數的SQL語句。PreparedStatement物件比Statement物件的效率更高,並且可以防止SQL隱碼攻擊,所以我們一般都使用PreparedStatement |
CallableStatement | 繼承自PreparedStatement介面,由方法prepareCall建立,用於呼叫儲存過程 |
常用Statement方法:
方法 | 說明 |
---|---|
boolean execute(String SQL) | 執行語句,返回是否有結果集 |
ResultSet executeQuery(String SQL) | 執行select語句,返回ResultSet結果集 |
int executeUpdate(String SQL) | 執行insert/update/delete操作,返回更新的行數 |
addBatch(String sql) | 把多條sql語句放到一個批處理中 |
executeBatch() | 向資料庫傳送一批sql語句執行 |
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) | 獲取在資料庫裡任意型別的資料 |
JDBC資料型別:
下表列出了預設的JDBC資料型別與Java資料型別轉換,當使用PreparedStatement或CallableStatement物件時可呼叫setXXX()方法或ResultSet.updateXXX()方法。
SQL | JDBC/Java | setXXX | getXXX | updateXXX |
---|---|---|---|---|
VARCHAR | java.lang.String | setString | getString | updateString |
CHAR | java.lang.String | setString | getString | updateString |
LONGVARCHAR | java.lang.String | setString | getString | updateString |
BIT | boolean | setBoolean | getBoolean | updateBoolean |
NUMERIC | java.math.BigDecimal | setBigDecimal | getBigDecimal | updateBigDecimal |
TINYINT | byte | setByte | getByte | updateByte |
SMALLINT | short | setShort | getShort | updateShort |
INTEGER | int | setInt | getInt | updateInt |
BIGINT | long | setLong | getLong | updateLong |
REAL | float | setFloat | getFloat | updateFloat |
FLOAT | float | setFloat | getFloat | updateFloat |
DOUBLE | double | setDouble | getDouble | updateDouble |
VARBINARY | byte[ ] | setBytes | getBytes | updateBytes |
BINARY | byte[ ] | setBytes | getBytes | updateBytes |
DATE | java.sql.Date | setDate | getDate | updateDate |
TIME | java.sql.Time | setTime | getTime | updateTime |
TIMESTAMP | java.sql.Timestamp | setTimestamp | getTimestamp | updateTimestamp |
CLOB | java.sql.Clob | setClob | getClob | updateClob |
BLOB | java.sql.Blob | setBlob | getBlob | updateBlob |
ARRAY | java.sql.Array | setARRAY | getARRAY | updateARRAY |
REF | java.sql.Ref | SetRef | getRef | updateRef |
STRUCT | java.sql.Struct | SetStruct | getStruct | updateStruct |
JDBC 3.0 增強了對 BLOB,CLOB,ARRAY 和 REF 資料型別的支援。 ResultSet 物件現在有 UPDATEBLOB(),updateCLOB(), updateArray(),和 updateRef()方法,通過這些方法可以直接操作伺服器上的相應資料。
java.sql.Date 類對映 SQL DATE 型別,java.sql.Time 類和 java.sql.Timestamp 類也分別對映 SQL TIME 資料型別和 SQL TIMESTAMP 資料型別。
ResultSet還提供了對結果集進行滾動的方法:
方法 | 說明 |
---|---|
boolean next() | 將游標移動到下一行,如果是結果集的最後一行則返回 false |
boolean previous() | 將游標移動到上一行,如果超過結果集的範圍則返回 false |
void close() | 關閉 ResultSet 物件 |
int getRow() | 返回當前游標指向的行數的值 |
void beforeFirst() | 將游標移動到第一行之前 |
void afterLast() | 將游標移動到最後一行之後 |
boolean first() | 將游標移動到第一行 |
void last() | 將游標移動到最後一行 |
boolean relative(int row) | 將游標移動到當前指向的位置往前或往後第 row 行的位置 |
boolean absolute(int row) | 將游標移動到指定的第 row 行 |
void moveToInsertRow() | 將游標移動到結果集中指定的行,可以在資料庫中插入新的一行。當前游標位置將被記住 |
void moveToCurrentRow() | 如果游標處於插入行,則將游標返回到當前行,其他情況下,這個方法不執行任何操作 |
使用後依次關閉物件及連線:ResultSet → Statement → Connection
處理 NULL 值
SQL 使用 NULL 值和 Java 使用 null 是不同的概念。可以使用三種策略來處理 Java 中的 SQL NULL 值-
- 避免使用返回原始資料型別的 getXXX()方法。
- 使用包裝類的基本資料型別,並使用 ResultSet 物件的 wasNull()方法來測試收到 getXXX()方法返回的值是否為 null,如果是 null,該包裝類變數則被設定為 null。
- 使用原始資料型別和 ResultSet 物件的 wasNull()方法來測試通過 getXXX()方法返回的值,如果是 null,則原始變數應設定為可接受的值來代表 NULL。
Statement stmt = conn.createStatement( );
String sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
int id = rs.getInt(1);
if( rs.wasNull( ) ) {
id = 0;
}
載入JDBC驅動程式 → 建立資料庫連線Connection → 建立執行SQL的語句Statement → 處理執行結果ResultSet → 釋放資源
(賈璉欲執事)
1.載入JDBC驅動
使用Class.forName()方法將給定的JDBC驅動類載入到Java虛擬機器中。若系統中不存在給定的類,則會引發異常,異常型別為ClassNotFoundException。
方式一:推薦這種方式,不會對具體的驅動類產生依賴。
Class.forName(“com.MySQL.jdbc.Driver”);
方式二:會造成DriverManager中產生兩個一樣的驅動,並會對具體的驅動類產生依賴。
DriverManager.registerDriver(com.mysql.jdbc.Driver);
2.建立連線
DriverManager類是JDBC的管理層,作用於使用者和驅動程式之間。 DriverManager類跟蹤可用的驅動程式,並在資料庫和相應的驅動程式之間建立連線。當呼叫getConnection()方法時, DriverManager類首先從已載入的驅動程式列表中找到一個可以接收該資料庫URL的驅動程式,然後請求該驅動程式使用相關的URL、使用者名稱和密碼連線到資料庫中,於是就建立了與資料庫的連線,建立連線物件並返回引用。
Connection conn = DriverManager.getConnection(url, user, password);
URL用於標識資料庫的位置,通過URL地址告訴JDBC程式連線哪個資料庫,URL的寫法為:
其他引數如:useUnicode=true&characterEncoding=utf8
3.建立執行SQL語句的statement
//Statement
String id = "5";
String sql = "delete from table where id=" + id;
Statement st = conn.createStatement();
st.executeQuery(sql);
//存在sql注入的危險
//如果使用者傳入的id為“5 or 1=1”,那麼將刪除表中的所有記錄
//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.處理執行結果(ResultSet)
ResultSet rs = ps.executeQuery();
While(rs.next()){
rs.getString(“col_name”);
rs.getInt(1);
//…
}
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();
}
}
}
- 為了提高效能
- 為了保持業務流程的完整性
- 使用分散式事務
可以通過事務在任意時間來控制以及更改應用到資料庫。它把單個 SQL 語句或一組 SQL 語句作為一個邏輯單元,如果其中任一語句失敗,則整個事務失敗。
若要啟用手動事務模式來代替 JDBC 驅動程式預設使用的自動提交模式的話,使用 Connection 物件的的 setAutoCommit()方法。如果傳遞一個布林值 false 到 setAutoCommit()方法,你就關閉自動提交模式。你也可以傳遞一個布林值 true 將其再次開啟。
conn.setAutoCommit(false);
提交和回滾
當完成了的修改,並且要提交修改,可以在 connection 物件裡呼叫 commit()方法,如下所示-
conn.commit( );
另外,用名為 conn 的連線回滾資料到資料庫,使用如下所示的程式碼-
conn.rollback( );
try{
//Assume a valid connection object conn
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
//Submit a malformed SQL statement that breaks
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
// If there is no error.
conn.commit();
}catch(SQLException se){
// If there is any error.
conn.rollback();
}
在這種情況下,之前的 INSERT 語句不會成功,一切都將被回滾到最初狀態。
使用還原點
新的 JDBC 3.0 還原點介面提供了額外的事務控制。大部分現代的資料庫管理系統的環境都支援設定還原點,例如 Oracle 的 PL/SQL。
當在事務中設定一個還原點來定義一個邏輯回滾點。如果在一個還原點之後發生錯誤,那麼可以使用 rollback 方法來撤消所有的修改或在該還原點之後所做的修改。
Connection 物件有兩個新的方法來管理還原點-
- setSavepoint(String savepointName): 定義了一個新的還原點。它也返回一個 Savepoint 物件。
- releaseSavepoint(Savepoint savepointName): 刪除一個還原點。請注意,它需要一個作為引數的 Savepoint 物件。這個物件通常是由 setSavepoint() 方法生成的一個還原點。
有一個 rollback (String savepointName) 方法,該方法可以回滾到指定的還原點。
try{
//Assume a valid connection object conn
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
//set a Savepoint
Savepoint savepoint1 = conn.setSavepoint("Savepoint1");
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
//Submit a malformed SQL statement that breaks
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Tez')";
stmt.executeUpdate(SQL);
// If there is no error, commit the changes.
conn.commit();
}catch(SQLException se){
// If there is any error.
conn.rollback(savepoint1);
}
異常處理可以允許處理一個異常情況,例如可控方式的程式定義錯誤。
當異常情況發生時,將丟擲一個異常。丟擲這個詞意味著當前執行的程式停止,控制器被重定向到最近的適用的 catch 子句。如果沒有適用的 catch 子句存在,那麼程式執行被終止。
JDBC 的異常處理是非常類似於 Java 的異常處理,但對於 JDBC,最常見的異常是 java.sql.SQLException。
SQLException 異常在驅動程式和資料庫中都可能出現。當出現這個異常時,SQLException 型別的物件將被傳遞到 catch 子句。
傳遞的 SQLException 物件具有以下的方法,以下的方法可用於檢索該異常的額外資訊
方法 | 說明 |
---|---|
getErrorCode( ) | 獲取與異常關聯的錯誤號。 |
getMessage( ) | 獲取 JDBC 驅動程式的錯誤資訊,該錯誤是由驅動程式處理的,或者在資料庫錯誤中獲取 Oracl 錯誤號和錯誤資訊。 |
getSQLState( ) | 獲取 XOPEN SQLstate 字串。對於 JDBC 驅動程式錯誤,使用該方法不能返回有用的資訊。對於資料庫錯誤,返回第五位的 XOPEN SQLstate 程式碼。該方法可以返回 null。 |
getNextException( ) | 獲取異常鏈的下一個 Exception 物件。 |
printStackTrace( ) | 列印當前異常或者丟擲,其回溯到標準的流錯誤。 |
printStackTrace(PrintStream s) | 列印該丟擲,其回溯到你指定的列印流。 |
printStackTrace(PrintWriter w) | 列印該丟擲,其回溯到你指定的列印寫入。 |
//STEP 1. Import required packages
import java.sql.*;
public class JDBCExample {
// JDBC driver name and database URL
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/EMP";
// Database credentials
static final String USER = "username";
static final String PASS = "password";
public static void main(String[] args) {
Connection conn = null;
try{
//STEP 2: Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//STEP 3: Open a connection
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
//STEP 4: Execute a query
System.out.println("Creating statement...");
Statement stmt = conn.createStatement();
String sql;
sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
//STEP 5: Extract data from result set
while(rs.next()){
//Retrieve by column name
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
//Display values
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
}
//STEP 6: Clean-up environment
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//finally block used to close resources
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
}//end main
}//end JDBCExample
批處理是指將關聯的 SQL 語句組合成一個批處理,並將他們當成一個呼叫提交給資料庫。
當一次傳送多個 SQL 語句到資料庫時,可以減少通訊的資源消耗,從而提高了效能。
- JDBC 驅動程式不一定支援該功能。可以使用 DatabaseMetaData.supportsBatchUpdates() 方法來確定目標資料庫是否支援批處理更新。如果你的JDBC驅動程式支援此功能,則該方法返回值為 true。
- Statement,PreparedStatement 和 CallableStatement 的 addBatch() 方法用於新增單個語句到批處理。
- executeBatch() 方法用於啟動執行所有組合在一起的語句。
- executeBatch() 方法返回一個整數陣列,陣列中的每個元素代表了各自的更新語句的更新數目。
- clearBatch() 此方法刪除所有用 addBatch() 方法新增的語句。但是,不能有選擇性地選擇要刪除的語句。
批處理和 Statement 物件
使用 Statement 物件來使用批處理所需要的典型步驟如下所示-
- 使用 createStatement() 方法建立一個 Statement 物件。
- 使用 setAutoCommit() 方法將自動提交設為 false。
- 被建立的 Statement 物件可以使用 addBatch() 方法來新增你想要的所有SQL語句。
- 被建立的 Statement 物件可以用 executeBatch() 將所有的 SQL 語句執行。
- 最後,使用 commit() 方法提交所有的更改。
// Create statement object
Statement stmt = conn.createStatement();
// Set auto-commit to false
conn.setAutoCommit(false);
// Create SQL statement
String SQL = "INSERT INTO Employees (id, first, last, age) " +
"VALUES(200,'Zia', 'Ali', 30)";
// Add above SQL statement in the batch.
stmt.addBatch(SQL);
// Create one more SQL statement
String SQL = "INSERT INTO Employees (id, first, last, age) " +
"VALUES(201,'Raj', 'Kumar', 35)";
// Add above SQL statement in the batch.
stmt.addBatch(SQL);
// Create one more SQL statement
String SQL = "UPDATE Employees SET age = 35 " +
"WHERE id = 100";
// Add above SQL statement in the batch.
stmt.addBatch(SQL);
// Create an int[] to hold returned values
int[] count = stmt.executeBatch();
//Explicitly commit statements to apply changes
conn.commit();
批處理和 PrepareStatement 物件
使用 prepareStatement 物件來使用批處理需要的典型步驟如下所示-
- 使用佔位符建立 SQL 語句。
- 使用任一 prepareStatement() 方法建立 prepareStatement 物件。
- 使用 setAutoCommit() 方法將自動提交設為 false。
- 被建立的 Statement 物件可以使用 addBatch() 方法來新增你想要的所有 SQL 語句。
- 被建立的 Statement 物件可以用 executeBatch() 將所有的 SQL 語句執行。
- 最後,使用 commit() 方法提交所有的更改。
// Create SQL statement
String SQL = "INSERT INTO Employees (id, first, last, age) " +
"VALUES(?, ?, ?, ?)";
// Create PrepareStatement object
PreparedStatemen pstmt = conn.prepareStatement(SQL);
//Set auto-commit to false
conn.setAutoCommit(false);
// Set the variables
pstmt.setInt( 1, 400 );
pstmt.setString( 2, "Pappu" );
pstmt.setString( 3, "Singh" );
pstmt.setInt( 4, 33 );
// Add it to the batch
pstmt.addBatch();
// Set the variables
pstmt.setInt( 1, 401 );
pstmt.setString( 2, "Pawan" );
pstmt.setString( 3, "Singh" );
pstmt.setInt( 4, 31 );
// Add it to the batch
pstmt.addBatch();
//add more batches
.
.
.
.
//Create an int[] to hold returned values
int[] count = stmt.executeBatch();
//Explicitly commit statements to apply changes
conn.commit();
建立 CallableStatement 物件
假設,需要執行下面的 Oracle 儲存過程-
CREATE OR REPLACE PROCEDURE getEmpName
(EMP_ID IN NUMBER, EMP_FIRST OUT VARCHAR) AS
BEGIN
SELECT first INTO EMP_FIRST
FROM Employees
WHERE ID = EMP_ID;
END;
注意:上面的儲存過程是在 Oracle 使用的,但使用的是 MySQL 資料庫,所以在 MySQL 的環境下需要重新寫出相同功能的程式碼,下面的程式碼是在 EMP 資料庫中建立相同功能的程式碼-
DELIMITER $$
DROP PROCEDURE IF EXISTS `EMP`.`getEmpName` $$
CREATE PROCEDURE `EMP`.`getEmpName`
(IN EMP_ID INT, OUT EMP_FIRST VARCHAR(255))
BEGIN
SELECT first INTO EMP_FIRST
FROM Employees
WHERE ID = EMP_ID;
END $$
DELIMITER ;
當前有三種型別的引數:IN,OUT 和 INOUT。PreparedStatement 物件只能使用 IN 引數。CallableStatement 物件可以使用所有的三種型別。
下面是三種型別引數的定義
引數 | 描述 |
---|---|
IN | 當 SQL 語句建立的時候,該引數的值是未知的。你可以用 setXXX() 方法將值繫結到 IN 引數裡。 |
OUT | 該引數的值是由 SQL 語句的返回值。你可以用 getXXX() 方法從 OUT 引數中檢索值。 |
INOUT | 該引數同時提供輸入和輸出值。你可以用 setXXX() 方法將值繫結到 IN 引數裡,並且也可以用 getXXX() 方法從 OUT 引數中檢索值。 |
下面的程式碼片段展示瞭如何使用 Connection.prepareCall() 方法實現一個基於上述儲存過程的 CallableStatement 物件
CallableStatement cstmt = null;
try {
String SQL = "{call getEmpName (?, ?)}";
cstmt = conn.prepareCall (SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}
字串變數 SQL 使用引數佔位符來表示儲存過程。
使用 CallableStatement 物件就像使用 PreparedStatement 物件。在執行該語句前,你必須將值繫結到所有的引數,否則你將收到一個 SQL 異常。
如果你有 IN 引數,只要按照適用於 PreparedStatement 物件相同的規則和技巧;用 setXXX()方法來繫結對應的 Java 資料型別。
當你使用 OUT 和 INOUT 引數就必須採用額外的 CallableStatement 方法:registerOutParameter()。registerOutParameter() 方法將 JDBC 資料型別繫結到儲存過程返回的資料型別。
一旦你呼叫了儲存過程,你可以用適當的 getXXX()方法從 OUT 引數引數中檢索數值。這種方法將檢索出來的 SQL 型別的值對映到 Java 資料型別。
關閉 CallableStatement 物件
正如關閉其它的 Statement 物件,出於同樣的原因,也應該關閉 CallableStatement 物件。
close()方法簡單的呼叫就可以完成這項工作。如果你先關閉了 Connection 物件,那麼它也會關閉 CallableStatement 物件。然而,你應該始終明確關閉 CallableStatement 物件,以確保該物件被徹底關閉。
CallableStatement cstmt = null;
try {
String SQL = "{call getEmpName (?, ?)}";
cstmt = conn.prepareCall (SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
cstmt.close();
}
PreparedStatement 物件必須具備使用輸入和輸出流來提供引數資料的能力。能夠將整個檔案儲存到資料庫列中,這樣資料庫就能儲存大型資料,例如 CLOB 和 BLOB 資料型別。
用於流資料有下列幾種方法-
- setAsciiStream(): 該方法是用來提供較大的 ASCII 值。
- setCharacterStream(): 該方法是用來提供較大的 UNICODE 值。
- setBinaryStream(): 該方法是用來提供較大的二進位制值。
setXXXStream()方法需要一個額外的引數,該引數是除了引數佔位符的檔案大小。這個引數通知驅動程式通過使用流有多少資料被髮送到資料庫中。
假如我們到要上傳一個名為 XML_Data.xml 的 XML 檔案到資料庫的表中。下面是該 XML 檔案的內容
<?xml version="1.0"?>
<Employee>
<id>100</id>
<first>Zara</first>
<last>Ali</last>
<Salary>10000</Salary>
<Dob>18-08-1978</Dob>
<Employee>
將該 XML 檔案和要執行的示例儲存在相同的目錄的。
這個示例將建立一個資料庫表 XML_Data ,然後 XML_Data.xml 將被上傳到該表中。
將下面的示例拷貝並粘帖到 JDBCExample.java 中,編譯並執行它,如下所示
// Import required packages
import java.sql.*;
import java.io.*;
import java.util.*;
public class JDBCExample {
// JDBC driver name and database URL
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/EMP";
// Database credentials
static final String USER = "username";
static final String PASS = "password";
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
Statement stmt = null;
ResultSet rs = null;
try{
// Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
// Open a connection
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
//Create a Statement object and build table
stmt = conn.createStatement();
createXMLTable(stmt);
//Open a FileInputStream
File f = new File("XML_Data.xml");
long fileLength = f.length();
FileInputStream fis = new FileInputStream(f);
//Create PreparedStatement and stream data
String SQL = "INSERT INTO XML_Data VALUES (?,?)";
pstmt = conn.prepareStatement(SQL);
pstmt.setInt(1,100);
pstmt.setAsciiStream(2,fis,(int)fileLength);
pstmt.execute();
//Close input stream
fis.close();
// Do a query to get the row
SQL = "SELECT Data FROM XML_Data WHERE id=100";
rs = stmt.executeQuery (SQL);
// Get the first row
if (rs.next ()){
//Retrieve data from input stream
InputStream xmlInputStream = rs.getAsciiStream (1);
int c;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while (( c = xmlInputStream.read ()) != -1)
bos.write(c);
//Print results
System.out.println(bos.toString());
}
// Clean-up environment
rs.close();
stmt.close();
pstmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//finally block used to close resources
try{
if(stmt!=null)
stmt.close();
}catch(SQLException se2){
}// nothing we can do
try{
if(pstmt!=null)
pstmt.close();
}catch(SQLException se2){
}// nothing we can do
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
}//end main
public static void createXMLTable(Statement stmt)
throws SQLException{
System.out.println("Creating XML_Data table..." );
//Create SQL Statement
String streamingDataSql = "CREATE TABLE XML_Data " +
"(id INTEGER, Data LONG)";
//Drop table first if it exists.
try{
stmt.executeUpdate("DROP TABLE XML_Data");
}catch(SQLException se){
}// do nothing
//Build table.
stmt.executeUpdate(streamingDataSql);
}//end createXMLTable
}//end JDBCExample
對於共享資源,有一個很著名的設計模式:資源池(Resource Pool)。
該模式正是為了解決資源的頻繁分配﹑釋放所造成的問題。
連線池技術的核心思想是連線複用,通過建立一個資料庫連線池以及一套連線使用、分配和管理策略,使得該連線池中的連線可以得到高效、安全的複用,避免了資料庫連線頻繁建立、關閉的開銷。
連線池的工作原理
主要由三部分組成,分別為連線池的建立、連線池中連線的使用管理、連線池的關閉。
1.連線池的建立
一般在系統初始化時,連線池會根據系統配置建立,並在池中建立了幾個連線物件,以便使用時能從連線池中獲取。連線池中的連線不能隨意建立和關閉,這樣避免了連線隨意建立和關閉造成的系統開銷。Java中提供了很多容器類可以方便的構建連線池,例如Vector、Stack等。
2.連線池的管理
連線池管理策略是連線池機制的核心,連線池內連線的分配和釋放對系統的效能有很大的影響。其管理策略是:
當客戶請求資料庫連線時,首先檢視連線池中是否有空閒連線,如果存在空閒連線,則將連線分配給客戶使用;如果沒有空閒連線,則檢視當前所開的連線數是否已經達到最大連線數,如果沒達到就重新建立一個連線給請求的客戶;如果達到就按設定的最大等待時間進行等待,如果超出最大等待時間,則丟擲異常給客戶。
當客戶釋放資料庫連線時,先判斷該連線的引用次數是否超過了規定值,如果超過就從連線池中刪除該連線,否則保留為其他客戶服務。
該策略保證了資料庫連線的有效複用,避免頻繁的建立、釋放連線所帶來的系統資源開銷。
3.連線池的關閉
當應用程式退出時,關閉連線池中所有的連線,釋放連線池相關的資源,該過程正好與建立相反。
連線池的主要優點
1.減少連線建立時間。連線池中的連線是已準備好的、可重複使用的,獲取後可以直接訪問資料庫,因此減少了連線建立的次數和時間。
2.簡化的程式設計模式。當使用連線池時,每一個單獨的執行緒能夠像建立一個自己的JDBC連線一樣操作,允許使用者直接使用JDBC程式設計技術。
3.控制資源的使用。
DBCP 資料庫連線池
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
public class JdbcUtils_DBCP {
private static DataSource ds = null;
static{
try{
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory();
ds = factory.createDataSource(prop);
System.out.println(ds);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
C3P0 資料庫連線池
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource ds = null;
static{
try{
ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/day16");
ds.setUser("root");
ds.setPassword("root");
ds.setInitialPoolSize(10);//最初連線數
ds.setMinPoolSize(5);//最小連線數
ds.setMaxPoolSize(20);//最大連線數
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
也可以將配置資訊放在xml檔案中(src下):c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
<named-config name="oracle">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
這樣JdbcUtils_C3p0可以改為:
/*
ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/day16");
ds.setUser("root");
ds.setPassword("root");
ds.setInitialPoolSize(10);//最初連線數
ds.setMinPoolSize(5);//最小連線數
ds.setMaxPoolSize(20);//最大連線數
*/
ds = new ComboPooledDataSource(""mysql"");//如果預設()
JDBC工具類
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/store28
user=root
password=root1234
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
public final class JDBCUtilsPlus {
private static final String DRIVER;
private static final String URL;
private static final String USER;
private static final String PASSWORD;
private JDBCUtilsPlus(){}
static {
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
DRIVER = bundle.getString("driver");
URL = bundle.getString("url");
USER = bundle.getString("user");
PASSWORD = bundle.getString("password");
/**
* 驅動註冊
*/
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* 獲取 Connetion
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(URL, USER, PASSWORD);
}
/**
* 釋放資源
* @param conn
* @param st
* @param rs
*/
public static void colseResource(Connection conn,Statement st,ResultSet rs) {
closeResultSet(rs);
closeStatement(st);
closeConnection(conn);
}
/**
* 釋放連線 Connection
* @param conn
*/
public static void closeConnection(Connection conn) {
if(conn !=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
conn = null;
}
/**
* 釋放語句執行者 Statement
* @param st
*/
public static void closeStatement(Statement st) {
if(st !=null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
st = null;
}
/**
* 釋放結果集 ResultSet
* @param rs
*/
public static void closeResultSet(ResultSet rs) {
if(rs !=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//等待垃圾回收
rs = null;
}
/**
* insert方法
* @param conn
* @param sql
* @param parems
* @return
* @throws SQLException
*/
public static int insert(Connection conn,String sql,Object[] parems) throws SQLException{
PreparedStatement stmt = null;
try{
stmt = conn.prepareStatement(sql);
if(null != parems && parems.length>0){//判斷是否有資訊增加
for(int i = 0;i<parems.length;i++){
stmt.setObject(i+1, parems[i]);//使用給定物件設定指定引數的值
}
}
int count = stmt.executeUpdate();
return count;
}finally{
close(stmt);
}
}
/**
* 泛型查詢方法
* @param conn
* @param sql
* @param paramsList
* @param rm
* @return
* @throws SQLException
*/
public static <T> List<T> executeQuery(Connection conn,String sql,Object[] paramsList,RowsMapper<T> rm) throws SQLException{
PreparedStatement stmt = null;
ResultSet rs = null;
List<T> list = new ArrayList<T>();
try{
stmt = conn.prepareStatement(sql);
if(null != paramsList && paramsList.length>0){//判斷是否有資訊增加
for(int i = 0;i<paramsList.length;i++){
stmt.setObject(i+1, paramsList[i]);
}
}
rs = stmt.executeQuery();
while(rs.next()){
T t = rm.getEntity(rs);
list.add(t);
}
return list;
}finally{
close(rs);
close(stmt);
}
}
}
時間處理(Date和Time以及Timestamp區別、隨機日期生成)
java.util.Date
- 子類:java.sql.Date
- 子類:java.sql.Time
- 子類:java.sql.Timestamp
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Random;
/**
* 測試時間處理(java.sql.Date,Time,Timestamp)
*/
public class Demo07 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");
for (int i = 0; i < 1000; i++) {
ps = conn.prepareStatement("insert into t_user(userName,pwd,regTime,lastLoginTime)values(?,?,?,?)");
ps.setObject(1, "小高" + i);
ps.setObject(2, "123");
//
int random = 1000000000 + new Random().nextInt(1000000000); //隨機時間
java.sql.Date date = new java.sql.Date(System.currentTimeMillis() - random); //插入隨機時間
java.sql.Timestamp stamp = new Timestamp(System.currentTimeMillis()); //如果需要插入指定時間,可以使用Calendar、DateFormat
ps.setDate(3, date);
ps.setTimestamp(4, stamp);
//
ps.execute();
}
System.out.println("插入");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally{
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
/**
* 測試時間處理(java.sql.Date,Time,Timestamp),取出指定時間段的資料
*/
public class Demo08 {
/**
* 將字串代表的時間轉為long數字(格式:yyyy-MM-dd hh:mm:ss)
* @param dateStr
* @return
*/
public static long str2DateTime(String dateStr){
DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
return format.parse(dateStr).getTime();
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");
//
ps = conn.prepareStatement("select * from t_user where regTime > ? and regTime < ?");
java.sql.Date start = new java.sql.Date(str2DateTime("2016-06-20 00:00:00"));
java.sql.Date end = new java.sql.Date(str2DateTime("2016-06-24 00:00:00"));
ps.setObject(1, start);
ps.setObject(2, end);
rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id") + "--" + rs.getString("userName")+"--"+rs.getDate("regTime"));
}
//
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally{
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
CLOB文字大物件操作
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 測試CLOB 文字大物件的使用
* 包含:將字串、檔案內容插入資料庫中的CLOB欄位和將CLOB欄位值取出來的操作。
*/
public class Demo09 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
PreparedStatement ps2 = null;
ResultSet rs = null;
Reader r = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");
//插入//
ps = conn.prepareStatement("insert into t_user(userName,myInfo)values(?,?)");
ps.setString(1, "小高");
//將文字檔案內容直接輸入到資料庫中
// ps.setClob(2, new FileReader(new File("G:/JAVA/test/a.txt")));
//將程式中的字串輸入到資料庫中的CLOB欄位中
ps.setClob(2, new BufferedReader(new InputStreamReader(new ByteArrayInputStream("aaaa".getBytes()))));
ps.executeUpdate();
System.out.println("插入");
//
//查詢//
ps2 = conn.prepareStatement("select * from t_user where id=?");
ps2.setObject(1, 223021);
rs = ps2.executeQuery();
System.out.println("查詢");
while (rs.next()) {
Clob c = rs.getClob("myInfo");
r = c.getCharacterStream();
int temp = 0;
while ((temp=r.read())!=-1) {
System.out.print((char)temp);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if (r!=null) {
r.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (rs!=null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps2!=null) {
ps2.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
BLOB二進位制大物件的使用
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 測試BLOB 二進位制大物件的使用
*/
public class Demo10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
PreparedStatement ps2 = null;
ResultSet rs = null;
InputStream is = null;
OutputStream os = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc","root","mysql");
//插入//
ps = conn.prepareStatement("insert into t_user(userName,headImg)values(?,?)");
ps.setString(1, "小高");
ps.setBlob(2, new FileInputStream("G:/JAVA/test/d.jpg"));
ps.execute();
//
//查詢//
ps2 = conn.prepareStatement("select * from t_user where id=?");
ps2.setObject(1, 223024);
rs = ps2.executeQuery();
System.out.println("查詢");
while (rs.next()) {
Blob b = rs.getBlob("headImg");
is = b.getBinaryStream();
os = new FileOutputStream("G:/JAVA/test/h.jpg");
int temp = 0;
while ((temp=is.read())!=-1) {
os.write(temp);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if (os!=null) {
os.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (is!=null) {
is.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (rs!=null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps2!=null) {
ps2.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (ps!=null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結