Statement (操作 SQL 語句)
Statement, PreparedStatement, CallableStatement
一旦建立好連線, 就可以與資料庫互動. JDBC 中Statement
, PreparedStatement
和 CallableStatement
提供了SQL操作的相關API. 其中 CallableStatement
繼承自 PreparedStatement
, 而 PreparedStatement
又繼承自 Statement
. 他們的區別是:
Statement
提供基本的 SQL 操作. 適合靜態SQL語句, 且傳入的 SQL 語句無法接受引數.PreparedStatement
可以在 SQL 中傳遞引數, 適合多次使用的 SQL 語句.CallableStatement
可以呼叫 PL/SQL 儲存過程.
儘管介面功能有不同, 但是使用方式大體相同, 分以下幾步:
- 建立 Statement
- 執行 SQL 語句
- 關閉 Statement
在執行 SQL 語句的時候, 常用以下幾個方法:
boolean execute(String SQL)
: 如果有 ResultSet 產生返回true, 否則, 返回 false. 一般用於 CREATE, ALTER 這些操作, 或者用來檢查一個Query有沒有返回.int executeUpdate(String SQL)
: 返回被影響的記錄的條數, 一般用於 INSERT, UPDATE, DELETE 這些操作.ResultSet executeQuery(String SQL)
: 返回查詢結果集, 專用語 SELECT.
以下三個例子分別示例瞭如何適用他們.
Statement 例子.
public class StatementExample {
}private Properties dbProps = new Properties(); StatementExample() {} public void setDBProperties(Properties dbProps) { this.dbProps = dbProps; } public Connection getConnection() throws SQLException { String url = dbProps.getProperty("url"); String user = dbProps.getProperty("user"); Connection conn = null; if (user.length() == 0) { conn = DriverManager.getConnection(url); } else { conn = DriverManager.getConnection(url, dbProps); } String dbName = dbProps.getProperty("dbName"); conn.setCatalog(dbName); return conn; } public void deleteAll() throws SQLException { String sql = "DELETE FROM posts"; Connection conn = getConnection(); Statement stmt = conn.createStatement(); int nRows = stmt.executeUpdate(sql); System.out.println(nRows + (nRows == 1 ? " post is " : " posts are ") + "deleted."); stmt.close(); conn.close(); } public void insertPost(Post post) throws SQLException { String sql = "INSERT INTO posts VALUES"; String title = post.getTitle(); String content = post.getContent(); Boolean visible = post.isVisible(); sql += "(" + "NULL" + "," + "\"" +title + "\"" + "," + "\"" + content + "\"" + "," + "DEFAULT" + "," + (visible ? "TRUE" : "FALSE") + ")"; Connection conn = getConnection(); Statement stmt = conn.createStatement(); int nRows = stmt.executeUpdate(sql); stmt.close(); conn.close(); } public ArrayList<Post> queryAll() throws SQLException { ArrayList<Post> list = new ArrayList<Post>(); String sql = "SELECT * FROM posts"; Connection conn = getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); rs.beforeFirst(); while (rs.next()) { Post temp = new Post(); temp.setId(rs.getInt("id")); temp.setTitle(rs.getString("title")); temp.setContent(rs.getString("content")); temp.setDate(rs.getTimestamp("dt_create")); temp.setVisible(rs.getBoolean("visible")); list.add(temp); } stmt.close(); conn.close(); return list; } public static void main(String[] args) { try { Properties props = new Properties(); props.load(ClassLoader.getSystemResourceAsStream("db.mysql.props")); StatementExample example = new StatementExample(); example.setDBProperties(props); ArrayList<Post> posts = example.queryAll(); System.out.println(posts); Post toInsert = new Post(); toInsert.setTitle("new Post"); toInsert.setContent("This is a new post!"); example.insertPost(toInsert); posts = example.queryAll(); System.out.println(posts); example.deleteAll(); posts = example.queryAll(); System.out.println(posts); } catch (SQLException e) { DBUtils.printSQLException(e); } catch (Exception e) { e.printStackTrace(); } }
PreparedStatement 例子.
public class PreparedStatExample {
}private Properties dbProps = new Properties(); PreparedStatExample() {} public void setDBProperties(Properties dbProps) { this.dbProps = dbProps; } public Connection getConnection() throws SQLException { String url = dbProps.getProperty("url"); String user = dbProps.getProperty("user"); Connection conn = null; if (user.length() == 0) { conn = DriverManager.getConnection(url); } else { conn = DriverManager.getConnection(url, dbProps); } String dbName = dbProps.getProperty("dbName"); conn.setCatalog(dbName); return conn; } public void deletePost(int id) throws SQLException { String sql = "DELETE FROM posts WHERE id = ?"; Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql); stmt.setInt(1, id); stmt.executeUpdate(); DBUtils.printWarnings(stmt.getWarnings()); stmt.close(); conn.close(); } public void insertPost(Post post) throws SQLException { String sql = "INSERT INTO posts VALUES(NULL, ?, ?, DEFAULT, ?)"; Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, post.getTitle()); stmt.setString(2, post.getContent()); stmt.setBoolean(3, post.isVisible()); stmt.executeUpdate(); DBUtils.printWarnings(stmt.getWarnings()); stmt.close(); conn.close(); } public ArrayList<Post> queryByTitle(String title) throws SQLException { ArrayList<Post> list = new ArrayList<Post>(); String sql = "SELECT * FROM posts WHERE title like ?"; Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, title); ResultSet rs = stmt.executeQuery(); rs.beforeFirst(); while (rs.next()) { Post temp = new Post(); temp.setId(rs.getInt("id")); temp.setTitle(rs.getString("title")); temp.setContent(rs.getString("content")); temp.setDate(rs.getTimestamp("dt_create")); temp.setVisible(rs.getBoolean("visible")); list.add(temp); } stmt.close(); conn.close(); return list; } public static void main(String[] args) { try { Properties props = new Properties(); props.load(ClassLoader.getSystemResourceAsStream("db.mysql.props")); PreparedStatExample example = new PreparedStatExample(); example.setDBProperties(props); // 此時資料庫中有一條 title 為 111 的資料 ArrayList<Post> posts = example.queryByTitle("111"); System.out.println(posts); //[Post{id=34, title='111', content='111', date=2015-01-25 12:58:32.0, visible=true}] Post toInsert = new Post(); toInsert.setTitle("111"); toInsert.setContent("111111"); example.insertPost(toInsert); posts = example.queryByTitle("111"); System.out.println(posts); // [Post{id=39, title='111', content='111', date=2015-01-25 13:00:49.0, visible=true}, Post{id=41, title='111', content='111111', date=2015-01-25 13:00:59.0, visible=false}] example.deletePost(posts.get(0).getId()); posts = example.queryByTitle("111"); System.out.println(posts); // [Post{id=41, title='111', content='111111', date=2015-01-25 13:00:59.0, visible=false}] } catch (SQLException e) { DBUtils.printSQLException(e); } catch (Exception e) { e.printStackTrace(); } }
CallableStatement 例子.
public class CallableStatExample {
}private Properties dbProps = new Properties(); public CallableStatExample() {} public void setDBProperties(Properties dbProps) { this.dbProps = dbProps; } public Connection getConnection() throws SQLException { String url = dbProps.getProperty("url"); String user = dbProps.getProperty("user"); Connection conn = null; if (user.length() == 0) { conn = DriverManager.getConnection(url); } else { conn = DriverManager.getConnection(url, dbProps); } String dbName = dbProps.getProperty("dbName"); conn.setCatalog(dbName); return conn; } public String getTitleById(int id) throws SQLException { Connection conn = getConnection(); String sql = "{call getTitleById (?, ?)}"; CallableStatement stmt = conn.prepareCall(sql); // 繫結傳入引數 stmt.setInt(1, id); // 對於傳出引數, 要先註冊 stmt.registerOutParameter(2, java.sql.Types.VARCHAR); stmt.execute(); String title = stmt.getString(2); stmt.close(); conn.close(); return title; } public static void main(String[] args) throws IOException, SQLException { Properties props = new Properties(); props.load(ClassLoader.getSystemResourceAsStream("db.mysql.props")); CallableStatExample example = new CallableStatExample(); example.setDBProperties(props); int id = 35; String title = example.getTitleById(id); System.out.println("Find title : " + title + " by ID : " + id); // Find title : 222 by ID : 35 }
Note: 請先將以下儲存過程存入 MySQL 資料庫
DELIMITER $$
CREATE PROCEDURE `testdb`.`getTitleById`
(IN post_id INT, OUT post_name VARCHAR(255))
BEGIN
SELECT title INTO post_name
FROM posts
WHERE ID = post_id;
END $$
DELIMITER ;
SQL 的批處理操作
SQL 批處理能夠允許新增多個 SQL 到 一個Statement物件, 並一併提交執行結果. 這減少了與 SQL 通訊的頻率. 但是, SQL 批處理不是 JDBC 要求一定要支援的. 使用前應該用 DatabaseMetaData.supportsBatchUpdates()
檢查支援情況.
SQL 批處理相關的 API 有:
- Statement.addBatch(): 往批處理中新增 SQL 語句
- Statement.executeBatch(): 執行批處理, 並返回一個整型陣列, 其中每個元素代表對應序號 SQL 的執行結果.
- Statement.clearBatch(): 從批處理中刪除已新增的所有 SQL 語句.
以下示例如何使用批處理往資料庫新增資料:
public static void batchInsertPosts(ArrayList<Post> posts) throws SQLException {
Connection conn = getConnectionFromDS(dbProps);
conn.setAutoCommit(false); // 見 "事務" 一章
DatabaseMetaData md = conn.getMetaData();
System.out.println("If support batch updates: " + md.supportsBatchUpdates());
String sql = "INSERT INTO POSTS\n"
+ "VALUES(NULL, ?, ?, DEFAULT, ?)";
PreparedStatement stmt = conn.prepareCall(sql);
try {
for (Post post : posts) {
stmt.setString(1, post.getTitle());
stmt.setString(2, post.getContent());
stmt.setBoolean(3, post.isVisible());
stmt.addBatch();
}
stmt.executeBatch();
conn.commit();
} catch (SQLException e) {
DBUtils.printSQLException(e);
conn.rollback();
}
DBUtils.printWarnings(stmt.getWarnings());
stmt.close();
conn.close();
}
SQL 異常處理
JDBC 中最常用的異常就是 SQLException
, 不管是在建立連線, 還是在執行 SQL 語句的時候, 都有可能丟擲這個異常. SQLException
包含以下資訊:
- 關於錯誤的描述. 通過呼叫
getMessage()
獲得. - 一個 SQL 狀態碼. 通過呼叫
getSQLState( )
獲取. SQL 狀態碼由5位字母和數字組成, 符合XOPEN
規範. - 一個錯誤碼. 這個錯誤碼的含義由實現規定, 有可能是資料庫的錯誤碼. 通過呼叫
SQLException.getErrorCode()
獲取. - 錯誤緣由. 引發異常的緣由, 有可能是一個或者多個 Throwable 的物件組成的一條鏈. 要想檢查這些緣由, 要遞迴遍歷 SQLException.getCause() 直到返回一個 null.
- 異常鏈. 通過 getNextException() 獲取下一個異常.
以下程式碼示例如何列印異常鏈中的每個SQLException
異常, 並且列印每個異常的 cause 鏈.
public static void printSQLException(SQLException ex) {
for (Throwable e : ex) { // Iterator 會呼叫 getNextException()
if (e instanceof SQLException) {
e.printStackTrace(System.err);
System.err.println("SQLState: " +
((SQLException)e).getSQLState());
System.err.println("Error Code: " +
((SQLException)e).getErrorCode());
System.err.println("Message: " + e.getMessage());
Throwable t = ex.getCause();
while(t != null) { // 列印每個 cause
System.out.println("Cause: " + t);
t = t.getCause();
}
}
}
}
除了發生致命錯誤產生丟擲 SQLException 之外, Connection
, Statement
, ResultSet
都有一個 getWarnings()
方法, 它返回一個 SQLWarning
. SQLWarning
繼承自 SQLException
, 可以向遍歷 SQLException
一樣遍歷它:
public static void printWarnings(SQLWarning warning)
throws SQLException {
while (warning != null) {
System.out.println("Message: " + warning.getMessage());
System.out.println("SQLState: " + warning.getSQLState());
System.out.print("Vendor error code: ");
System.out.println(warning.getErrorCode());
System.out.println("");
warning = warning.getNextWarning();
}
}
相關文章
- MySql常用操作SQL語句彙總MySql
- 資料庫常用操作SQL語句資料庫SQL
- SQL語句操作符優化SQL優化
- 利用SQL語句完成位操作 (轉)SQL
- 【SQL】14 UNION 操作符、SELECT INTO 語句、INSERT INTO SELECT 語句、CREATE DATABASE 語句、CREATE TABLE 語句SQLDatabase
- SQL語句SQL
- Java中如何解析SQL語句、格式化SQL語句、生成SQL語句?JavaSQL
- EFCore常規操作生成的SQL語句一覽SQL
- LINQ to SQL語句之Insert/Update/Delete操作SQLdelete
- 【資料操作】SQL語句效能調整原則SQL
- JavaScript statement flow control || JavaScript 語句流程控制JavaScript
- sql語句大全SQL
- 共享SQL語句SQL
- SQL語句整理SQL
- SQL基本語句SQL
- 精妙Sql語句SQL
- SQL語句集合SQL
- oracle sql語句OracleSQL
- sql語句 求救!SQL
- SQL精妙語句SQL
- SQL語句收集SQL
- 常用SQL語句SQL
- sql常用語句SQL
- 【SQL】10 SQL UPDATE 語句SQL
- 【SQL】11 SQL DELETE 語句SQLdelete
- [Sqlite] Sqlite的基本日常SQL操作語句彙總SQLite
- 用 Phoenix 通過 SQL 語句更新操作 HBase 資料SQL
- 【SQL】9 SQL INSERT INTO 語句SQL
- 1.3. SQL 語句SQL
- Sql語句小整理SQL
- SQL語句優化SQL優化
- SQL SELECT 語句SQL
- SQL 語句學習SQL
- SQL語句IN的用法SQL
- Oracle基本SQL語句OracleSQL
- 重拾Sql語句SQL
- sql語句錯誤SQL
- SQL語句筆記SQL筆記