使用PreparedStatement實作CRUD操作
相關完整代碼前往Github
文章目錄
- 使用PreparedStatement實作CRUD操作
- Statement操作資料庫的弊端
- PreparedStatement的使用
- PreparedStatement vs Statement
- Java與SQL對應資料類型轉換表
- ResultSet與ResultSetMetaData
- ResultSet
- ResultSetMetaData
- JDBC API小結
- 使用Statement弊端
通路和操作資料庫
- 資料庫連接配接被用于向資料庫伺服器發送指令和sql語句,并接受資料庫伺服器傳回來的結果,其實一個資料庫連接配接就是一個socket連接配接
- 在java.sql包中由3 個接口分别定義了對資料庫的調用的不同方式:
Statement:用于執行靜态sql語句,并傳回它所生成結果的對象。
PrepatedStatement:sql語句别預編譯并儲存在對象中,可以使用次對象多次高效的執行該語句
CallableStatement:用于執行 SQL 存儲過程
Statement操作資料庫的弊端
- 通過connection對象的
方法建立對象,該對象用于執行靜态的SQL語句,并且傳回執行結構createStatement()
- statement接口定義了下列方法執行SQL語句
int executeUpdate(String sql) 執行更新insert,update delete
resultset executeQuery(String sql):執行查詢操作SELECT
- 使用statement操作資料庫表存在弊端
問題一 存在拼串操作,繁瑣
問題二:存在SQL注入問題
- sql注入問題是利用某些系統沒有對使用者輸入的資料進行從分的檢查,而在使用者輸入資料注入非法的SQL語句段或指令(如:
) ,進而利用系統的 SQL 引擎完成惡意行為的做法。SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1'
- 對于java而言,要防範sql注入,隻要用PreparedStatement(從Statement擴充而來) 取代statement就可以了
PreparedStatement的使用
- 可以通過調用Connection 對象的
方法擷取preparedStatement(String sql)
對象preparedStatement
-
是preparedStatement
的子接口,他表示一條預編譯過的SQL語句Statement
-
對象所表示的SQL語句的參數用(?)表示,調用preparedStatement
對象的setXxx() 方法來設定這些參數.preparedStatement
- setXxx() 方法中有兩個參數,第一個參數是設定sql語句中的參數的索引(從一開始),第二個是設定的SQL語句中的參數的值
PreparedStatement vs Statement
- 提高了代碼的而可讀性和可維護性
- PreparedStatement能最大可能提高性能
DBServe會對
預編譯
語句提供性能優化,因為預編譯語句可能會被重複使用,是以語句在被DBServer的編譯器編譯後的執行代碼被緩存下載下傳,下次調用的時候隻要是相同的預編譯語句就不需要編譯,隻要将參數直接傳入編譯過的語句執行代碼,就會得到執行
在
語句中,即使是相同操作但是因為資料不一樣,是以整個語句本身不能比對,沒有緩存語句的意義,事實是沒有資料庫會對普通語句編譯後的執行代碼緩存這樣
statement
每執行一次都要對傳入的語句編譯一次。
(文法檢查,語義檢查,翻譯成二進制指令,緩存)
- PreparedStatement 可以防止 SQL 注入
- PreparedStatement 可以實作批量操作,效率高 …Statement無
- PreparedStatemen可以操作Blob類型變量…Statement不能
Java與SQL對應資料類型轉換表
Java類型 | SQL類型 |
boolean | BIT |
byte | TINYINT |
short | SMALLINT |
int | INTEGER |
long | BIGINT |
String | CHAR,VARCHAR,LONGVARCHAR |
byte array | BINARY , VAR BINARY |
java.sql.Date | DATE |
java.sql.Time | TIME |
java.sql.Timestamp | TIMESTAMP |
ResultSet與ResultSetMetaData
ResultSet
查詢需要調用
PreparedStatement
的
executeQuery
方法,查詢結果是一個Resultset對象
- ResultSet對象以邏輯表格的形式封裝了執行資料庫的結果集,ResultSet 接口由資料庫廠商提供
- ResultSet傳回的實際是一張資料表,有一個指針指向資料表的第一條資料的前面
- ResultSet 對象維護一個指向目前資料行的遊标,初始的時候,遊标在第一行之前,可以通過 ResultSet 對象的 next() 方法移動到下一行.調用next()方法檢測下一行是否有效,若有效,該方法傳回true,且指針下移相當于Iterator對象的 hasNext() 和 next() 方法的結合體。
- 當指針指向一行時,可以通過調用 getXxx(int index) 或 getXxx(int columnName) 擷取每一列的值。
- 例如: getInt(1), getString(“name”)
- 注意:Java與資料庫互動涉及到的相關Java API中的索引都從1開始。
ResultSetMetaData
可用于擷取關于 ResultSet 對象中列的類型和屬性資訊的對象,ResultSetMetaData meta = rs.getMetaData();
-
:擷取指定列的名稱getColumnName(int column)
-
:擷取指定列的别名getColumnLabel(int column)
-
:傳回目前 ResultSet 對象中的列數。getColumnCount()
- getColumnTypeName(int column):檢索指定列的資料庫特定的類型名稱。
- getColumnDisplaySize(int column):訓示指定列的最大标準寬度,以字元為機關。
-
:訓示指定列中的值是否可以為 null。isNullable(int column)
- isAutoIncrement(int column):訓示是否自動為指定列進行編号,這樣這些列仍然是隻讀的。
問題1:得到結果集後, 如何知道該結果集中有哪些列 ? 列名是什麼?
需要使用一個描述 ResultSet 的對象, 即 ResultSetMetaData
問題2:關于ResultSetMetaData
- 如何擷取 ResultSetMetaData: 調用 ResultSet 的 getMetaData() 方法即可
- 擷取 ResultSet 中有多少列:調用 ResultSetMetaData 的 getColumnCount() 方法
- 擷取 ResultSet 每一列的列的别名是什麼:調用 ResultSetMetaData 的getColumnLabel() 方法
釋放資源:
- 釋放ResultSet, Statement,Connection。
- 資料庫連接配接(Connection)是非常稀有的資源,用完後必須馬上釋放,如果Connection不能及時正确的關閉将導緻系統當機。Connection的使用原則是盡量晚建立,盡量早的釋放。
- 可以在finally中關閉,保證及時其他代碼出現異常,資源也一定能被關閉。
JDBC API小結
- 兩種思想
- 面向接口程式設計的思想
- ORM思想(object relational mapping)
- 一個資料表對應一個java類
- 表中的一條記錄對應java類的一個對象
- 表中的一個字段對應java類的一個屬性
sql是需要結合列名和表的屬性名來寫。注意起别名。
- 兩種技術
- JDBC結果集的中繼資料:ResultSetMetaData
- 擷取列數:getColumnCount()
- 擷取列的别名:getColumnLabel()
- 通過反射,建立指定類的對象,擷取指定的屬性并指派
使用Statement弊端
弊端:
問題一 存在拼串操作,繁瑣
問題二:存在SQL注入問題
- sql注入問題是利用某些系統沒有對使用者輸入的資料進行從分的檢查,而在使用者輸入資料注入非法的SQL語句段或指令(如:
) ,進而利用系統的 SQL 引擎完成惡意行為的做法。SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1'
Scanner scan = new Scanner(System.in);
String name = scan.next();
String sql = "insert into customers(name,email,birth)values('" + name +"','"+email+"','"+birth+"')";
SQL注入:
SELECT user,password FROM user_table WHERE user = '1' or ' AND password = '=1 or '1' = '1'
- Statement沒辦法操作Blob類型變量
- Statement實作批量插入時,效率較低