文章目錄
- 1.概述
- 2.jdbc相關概念
- 3.jbdc的使用
- 3.1 獲取連接
- 3.2 預編譯sql
- 3.3 設定引數
- 3.4 執行
- 3.5 獲取結果集
- 4. statement特點
- 4.1 普通statment
- 4.2 PreparedStatement
- 4.3 CallableStatement
1.概述
使用過mybatis的都清楚底層封裝了jdbc的操作,將繁瑣的jdbc的操作給屏蔽了,所以分析mybatis的原理之前,先來看看jdbc是如何作業的,statement有哪些特點,
2.jdbc相關概念
JDBC是由SUN公司提出的一些列規范,只定義了介面規范,具體實作由各個資料庫廠商去實作,它是一種典型的橋接模式,
jdbc是一種規范,所謂規范,就是自己定義了標準介面,做了如下抽象:用Connection代表和資料庫的連接,用Statement執行SQL,用ResultSet表示SQL回傳的結果,
上面說的Connection、Statement、ResultSet都應該是介面,具體實作由各個資料庫提供商提供,有了規范,可以通過統一的介面,訪問多種型別的資料庫,可隨便切換資料庫,
3.jbdc的使用
如下的測驗代碼就是對jdbc的使用,
public class JdbcTest {
public static final String URL = "jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=UTF-8";
public static final String USERNAME = "root";
public static final String PASSWORD = "341341";
public Connection connection;
@Before
public void init() throws SQLException {
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD); // 1.獲取連接
}
@After
public void over() throws SQLException {
connection.close();
}
@Test
public void jdbcTest() throws SQLException {
String sql = "select * from users where `name` = ?";
PreparedStatement statement = connection.prepareStatement(sql);//2. 預編譯sql
statement.setString(1, "森林");//3. 給占位符?設定引數
statement.execute();//4. 執行sql
ResultSet resultSet = statement.getResultSet();
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
System.out.println("===============================");
statement.setString(1, "依依");//重復使用這個statement 設定引數即可執行
statement.execute();
resultSet = statement.getResultSet();
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
// 釋放資源
resultSet.close();
statement.close();
}
}
3.1 獲取連接
介面的由各個廠商來提供,實作類的類名無法得到統一,去創建Connection物件的時候,代碼就會寫死某個實作類,例如
Connection con=MySqlConnectionImpl("127.0.0.1",3306,"mybatis",userName,pwd);
為了解決這個問題,抽象出了驅動的概念Driver,Driver是通過反射的機制來動態的創建連接,而不同的Driver又交給DriverManager來管理,這樣可以在URL中加上前綴,來識別使用哪個資料庫的驅動來創建連接,例如
public static final String URL = "jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=UTF-8";
這里的前綴中加了mysql,所以當執行
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
DriverManager會根據前綴獲得mysql的Driver驅動來創建資料庫連接,
3.2 預編譯sql
有了資料庫連接后,通過如下的代碼可以預編譯sql得到一個sql宣告,
PreparedStatement statement = connection.prepareStatement(sql);
可以一次編譯多次執行,sql陳述句一樣的時候,只需要設定引數就可以執行,
3.3 設定引數
第一個引數是索引,表示第幾個’?'占位符,從1開始 并不是從0開始,
有多個占位符,則需要執行多次這條陳述句,
statement.setString(1, "森林");
3.4 執行
執行sql,回傳結果是個boolean型別,執行的結果集放在了statement當中
statement.execute();
也可以使用executeQuery(),或者executeUpdate(),
兩者的區別在于executeQuery只能執行查詢操作,
executeUpdate執行增刪改操作,
3.5 獲取結果集
執行完execute操作后,會將執行結果保存在statement中,通過getResultSet可以獲得執行的結果,
ResultSet resultSet = statement.getResultSet();
4. statement特點
4.1 普通statment
最基本的功能是執行靜態的sql陳述句,
傳輸相關的功能,可以執行批處理和設定資料庫回傳的行數,
-
批處理
這里使用PreparedStatement無關緊要,因為這個介面繼承了Statment介面,
以下代碼是執行了100條插入,通過addBatch方法添加批處理引數,
設定完100條的引數后,executeBatch批處理執行這100條sql陳述句,記錄時間,為了對比批處理的效率,設定了對照試驗,不使用批處理,而是一條一條的執行,記錄時間,
@Test public void prepareBatchTest() throws SQLException { String sql = "INSERT INTO `users` (`name`,age) VALUES (?,18);"; PreparedStatement preparedStatement = connection.prepareStatement(sql); long l = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { preparedStatement.setString(1, UUID.randomUUID().toString()); preparedStatement.addBatch(); // 添加批處理引數 } preparedStatement.executeBatch(); // 批處理 一次發射 System.out.print("批處理:"); System.out.println(System.currentTimeMillis() - l); preparedStatement.close(); } @Test public void Test() throws SQLException { String sql = "INSERT INTO `users` (`name`,age) VALUES (?,18);"; PreparedStatement preparedStatement = connection.prepareStatement(sql); long l = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { preparedStatement.setString(1, UUID.randomUUID().toString()); preparedStatement.execute(); //單條執行 } System.out.print("一條條執行:"); System.out.println(System.currentTimeMillis() - l); preparedStatement.close(); }結果可以看出批處理的效率提高了一倍,

-
設定資料庫回傳行數setFetchSize
setFetchSize最主要是為了減少網路互動次數設計的,訪問ResultSet時,如果它每次只從服務器上取一行資料,則會產生大量的開銷,setFetchSize的意思是當呼叫rs.next時,ResultSet會一次性從服務器上取得多少行資料回來,這樣在下次rs.next時,它可以直接從記憶體中獲取出資料而不需要網路互動,提高了效率, 這個設定可能會被某些JDBC驅動忽略的,而且設定過大也會造成記憶體的上升,
4.2 PreparedStatement
預處理sql陳述句,可以一次編譯多次執行,只需要設定引數就可以執行,第一個jdbc使用的例子中已經體現了這一點,
還有一個更重要的優點,就是防止sql注入
-
sql注入
什么是sql注入呢,例如
String sql = "select * from users where `name` ='" + name + "'";需要傳入一個name 來補全sql,但是有人惡意在name中拼接了一個字串 or 1=1;這樣就會導致資訊的泄漏,

-
實驗
做對比實驗,分別使用Statment和PreparedStatment去執行相同的sql陳述句,看看有什么差別,代碼如下
public int selectByName(String name) throws SQLException { String sql = "select * from users where `name` ='" + name + "'"; System.out.println(sql); Statement statement = connection.prepareStatement(sql); statement.executeQuery(sql); ResultSet resultSet = statement.getResultSet(); int count = 0; while (resultSet.next()) { count++; } statement.close(); return count; } public int selectByName2(String name) throws SQLException { String sql = "SELECT * FROM users WHERE `name`=?"; PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1, name); System.out.println(statement); statement.executeQuery(); ResultSet resultSet = statement.getResultSet(); int count = 0; while (resultSet.next()) { count++; } statement.close(); return count; } @Test//防止sql注入 測驗 public void injectTest() throws SQLException { System.out.println(selectByName("森林")); System.out.println(selectByName("森林' or '1'='1")); System.out.println(selectByName2("森林' or '1'='1")); }兩個幾乎相同的方法,只是使用的Statement不同,selectByName使用的是Statment,selectByName2使用的是PreparedStatment,得到的結果如下,

正常操作查詢name為森林得到結果有3個,但是通過sql注入之后,將資料庫中所有的資料都查了出來,這就造成了安全隱患,而最后一行的結果可以看出PreparedStatment可以防止sql注入的發生,
那么PreparedStatment是如何做到防止sql注入的呢?
答:在設定引數的時候,setString方法中會給引號加上轉譯字符,轉移操作在資料庫端執行,這樣就起到了防止sql注入的功能,
4.3 CallableStatement
這是一個和存盤程序相關的statement
-
存盤程序
存盤程序 (Stored Procedure) 是在大型資料庫系統中 , 一組為了完成特定功能的 SQL 陳述句集 , 存盤在資料庫中 , 經過第一次編譯后再次呼叫不需要再次編譯 , 用戶通過指定存盤程序的名字并給出引數 (如果該存盤程序帶有引數) 來執行它 , 存盤程序是資料庫中的一個重要物件 ; 存盤程序中可以包含
邏輯控制陳述句和資料操縱陳述句, 它可以接受引數 , 輸出引數 , 回傳單個或多個結果集以及回傳值 ; -
mysql中的存盤程序

第一行存盤程序的名字后面跟著入參和出參,
sql陳述句中結果傳入給出參 使用INTO
-
jdbc使用存盤程序
可以設定入參、出參、讀取出參,
@Test // 存盤程序 public void processTest() throws SQLException { CallableStatement statement = connection.prepareCall("call select_all(?,?)"); // 設定入參 statement.setString(1, "森林"); // 設定出參 statement.registerOutParameter(2, Types.INTEGER); statement.execute(); ResultSet resultSet = statement.getResultSet(); int count = 0; while (resultSet.next()) { count++; } System.out.println(count); // 讀取出參 int totalCount = statement.getInt(2); System.out.println("total:" + totalCount); statement.close(); }執行結果如下

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/72769.html
標籤:其他
上一篇:mySql事務處理TPL的小總結
