1. 程式人生 > >JDBC、事務、資料庫連線池、DBUtils

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 + ", "

+ password);

           }

       } 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模板和引數陣列