JDBC、事務、資料庫連線池、DBUtils
JDBC
使用JDBC連線資料庫的規範程式碼(要求能默寫出)
@Test public void query() { Statement stmt = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/mydb1"; con = DriverManager.getConnection(url, "root", "123" stmt = con.createStatement(); String sql = "select * from user"; rs = stmt.executeQuery(sql); while(rs.next()) { String username = rs.getString(1); String password = rs.getString(2); System.out.println(username + ", " } } catch(Exception e) { throw new RuntimeException(e); } finally { try { if(rs != null) rs.close(); if(stmt != null) stmt.close(); if(con != null) con.close(); } catch(SQLException e) {} } } |
物件介紹:
1 JDBC中的主要類(介面)
在JDBC中常用的類有:
l DriverManager;
l Connection;
l Statement;
l ResultSet。
2 DriverManager
用於獲取與資料庫的連線:
1. Class.forName(“com.mysql.jdbc.Driver”);//註冊驅動
2. Stringurl = “jdbc:mysql://localhost:3306/mydb1”;
3. Stringusername = “root”;
4. Stringpassword = “123”;
5. Connectioncon = DriverManager.getConnection(url, username, password);
上面程式碼可能出現的兩種異常:
1. ClassNotFoundException:這個異常是在第1句上出現的,出現這個異常有兩個可能:
l 你沒有給出mysql的jar包;
l 你把類名稱打錯了,檢視類名是不是com.mysql.jdbc.Driver。
2. SQLException:這個異常出現在第5句,出現這個異常就是三個引數的問題,往往username和password一般不是出錯,所以需要認真檢視url是否打錯。
3 Connection
Connection最為重要的方法就是獲取Statement,即獲取一個SQL語句的執行器:
l Statementstmt = con.createStatement();
l Statementstmt = con.createStatement(int,int[c3] );
4 Statement
StatementSQL語句執行器,用於向資料庫傳送SQL語句:
l int executeUpdate[c4] (Stringsql):執行更新操作,即執行insert、update、delete語句;
l ResultSetexecuteQuery[c5] (Stringsql):執行查詢操作,執行查詢操作會返回ResultSet,即結果集。
5 ResultSet
兩個重要的方法:next()和getXXX(“”)
可以通過next()方法使ResultSet的遊標向下移動。
當遊標移動到你需要的行時,就需要來獲取該行的資料了,ResultSet提供了一系列的獲取列資料的方法:
l StringgetString(int columnIndex):獲取指定列的String型別資料;
l intgetInt(int columnIndex):獲取指定列的int型別資料;
l doublegetDouble(int columnIndex):獲取指定列的double型別資料;
l boolean getBoolean(intcolumnIndex):獲取指定列的boolean型別資料;
l ObjectgetObject(int columnIndex):獲取指定列的Object型別的資料。
PreparedStatement
l 它是Statement介面的子介面;
l 優點:
Ø 防SQL攻擊;
Ø 提高程式碼的可讀性、可維護性;
Ø 提高效率!
l 使用:
Ø 獲取PreparedStatement物件:
¨ 給出SQL模板!
¨ 呼叫Connection的PreparedStatement prepareStatement(Stringsql模板);
¨ 呼叫pstmt的setXxx()系列方法sql模板中的?賦值!
¨ 呼叫pstmt的executeUpdate()或executeQuery(),但它的方法都沒有引數。
l PreparedStatement使用預處理:
Ø 每個PreparedStatement都與一個SQL模板繫結在一起。
Ø 資料庫對SQL語句進行
¨ 校驗SQL語句的語法
¨ 編譯:生成一個與函式相似的東西
¨ 執行:執行時只是把引數傳遞過去而已,類似函式呼叫。
Ø 若第二次執行時,不用再次校驗語法,不用再次校驗和編譯,而是直接執行。
批處理
PreparedStatement批處理
每個PreparedStatement物件都繫結一條SQL模板,所以向PreparedStatement中新增的不是SQL語句,而是給“?”賦值。
con = JdbcUtils.getConnection(); String sql = "insert into stu values(?,?,?,?)"; pstmt = con.prepareStatement(sql); for(int i = 0; i < 10; i++) { pstmt.setString(1, "S_10" + i); pstmt.setString(2, "stu" + i); pstmt.setInt(3, 20 + i); pstmt.setString(4, i % 2 == 0 ? "male" : "female"); } |
事務
1. 事務的特性:ACID
1) 原子性:事務中所有操作是不可再分割的原子單位。事務中所有操作要麼全部執行成功,要麼全部執行失敗。
2) 一致性:事務執行後,資料庫狀態與其它業務規則保持一致。如轉賬業務,無論事務執行成功與否,參與轉賬的兩個賬號餘額之和應該是不變的。
3) 隔離性:在併發操作中,不同事務之間相互隔離,相互干擾。
4) 永續性:一旦事務提交成功,事務中所有的資料操作被持久化到資料庫中。即使提交事務後,資料庫馬上崩潰,在資料庫重啟時,也必須能保證通過某種機制恢復資料。
2. 使用
1) MySQL操作事務
Ø 開始事務:starttransaction
Ø 結束事務:commit或rollback
2) JDBC事務
Ø 開始事務:con.setAutoCommit(false);
Ø 結束事務;con.commit()或con.rollback();
Ø 儲存點:回滾到儲存點。
設定儲存點:Savepointsp = con.setSavepoint();
回滾到儲存點:con.rollback(sp);
3. 事務的隔離級別:
1) 三種併發讀問題
Ø 讀:讀到未提交
Ø 不可重複讀:兩次讀取不一致,讀取到另一事務修改的記錄
Ø 讀:兩次讀取不一致,讀取到另一事務插入的記錄
2) 四大隔離級別:
Ø SERIALIZABLE(序列化):對同一資料序列訪問的,即非併發。所以不會出現任何併發問題。缺點:易出現死鎖,效率太低,不用。
Ø REPEATABLE READ(可重複讀):防止了髒讀、不可重複讀,但沒有防止幻讀
Ø READ COMMITTED(讀已提交):防止了髒讀,但沒有防止不可重複讀,以及幻讀
Ø READ UNCOMMITTED(讀未提交):可能出現所有併發問題,效率最高,但不可用!
MySQL預設事務隔離級別為:REPEATABLE READ
Oracle預設事務隔離級別為:READ COMMITTED
3) MySQL設定事務隔離級別
Ø 檢視:[email protected]@tx_isolation
Ø 設定:settransaction isolation level 四選一
4) JDBC設定事務隔離級別
Ø con.setTransactionIsolation(四選一)
資料庫連線池:
1. 作用:用來管理資料庫連線的生命週期。
2. 優點:節省資源,提高效能。
3. 介面:java提供的連線池介面javax.sql.DataSource,連線池廠商的連線池類需要實現這一介面。
4. 常用的資料庫連線池:
1) DBCP:
實現類:BasicDataSource()
使用:
Ø 獲得一個數據庫連線池:BasicDataSourceds = new BasicDataSource()
Ø 設定四大引數:通過BasicDataSource提供的4個set方法實現
ds.setUsername("root");
ds.setPassword("123");
ds.setUrl("jdbc:mysql://localhost:3306/mydb1");
ds.setDriverClassName("com.mysql.jdbc.Driver")
Ø 設定連線池屬性:包含連線池最大連線數、初始連線數、最大空閒數和最小空閒數等。
Ø 通過資料庫連線池獲得連線:Connectioncon = ds.getConnection();
2) C3P0:
實現類:ComboPooledDataSource
使用:同DBCP
特點:
支援配置檔案配置:
Ø 通過預設配置初始化連線池:c3p0-config.xml放到src目錄下。
Ø 建立:ComboPooledDataSourceds = new ComboPooledDataSource(); 無參構造
通過命名配置初始化連線池
Ø 建立:
ComboPooledDataSource ds = newComboPooledDataSource("oracle-config");引數為在web.xml中配置的資料庫的名字。
DBUtils:
1. 核心類:QueryRunner、ResultSetHandler
1) QueryRunner:與資料庫連線池相關的執行器。
2) ResultSetHandler:用於查詢語句,規定表中的表中一行資料轉換為一個例項物件
2. 建立QueryRunner:需要為其傳入一個數據庫連線池或一個連線
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "select * from tab_student";
3. 更新:Update:qr.update(sql, "u1", "zhangSan","123");即傳入SQL模板和引數
4. 查詢:
1) 單表查詢:查詢結果中每一行對應一個Bean的例項
Ø 單行結果:使用BeanHandler<Class>(Class.class)
Student stu = qr.query(sql, newBeanHandler<Student>(Student.class));
Ø 多行結果:使用BeanHandlerList<Class>(Class.class)
List<Student> list = qr.query(sql, new BeanListHandler<Student>(Student.class));
2) 多表查詢:多個表聯合的查詢結果集中一行資料對應一個Map<key,value>物件
Ø 單行結果:使用MapHandler()
Map<String,Object> map = qr.query(sql, new MapHandler());
Ø 多行結果:使用MapListHandler()
List<Map<String,Object>> list = qr.query(sql, newMapListHandler() );
3) 結果集為單列
Ø 單列多行:使用ColumnListHandler(“列名稱”)
List<Object> list = qr.query(sql, newColumnListHandler("name")) ;
Ø 單列單行:使用ScalarHander()
Number number = (Number)qr.query(sql, new ScalarHandler());
5. 批處理:
DataSource ds =JdbcUtils.getDataSource();
QueryRunnerqr = new QueryRunner(ds);
Stringsql = "insert into tab_student values(?,?,?,?)";
Object[][]params = new Object[10][]; //表示要插入10行記錄
for(inti = 0; i < params.length; i++) {
params[i]= new Object[]{"S_300" + i, "name" + i, 30 + i,i%2==0?"男":"女"};
}
qr.batch (sql, params);//引數為SQL模板和引數陣列