Java 核心技術 卷II (4章)--數據庫編程

Java 核心技術 卷II  (4章)

---數據庫編程

 

1、jdbc設計

1)Jdbc是java能夠通過SQL訪問數據庫的一套javaAPI;

2)Jdbc是一套能與多種關係數據庫提供統一訪Java接口方法;

 

2、核心API方法介紹

創建執行對象:

Stattement stat = conn.createStatement();

stat.executeUpdate():返回受SQL命令影響的行數,或者對不返回行數的語句(DDL語句)返回0

ResultSet rs = stat.executeQuery(String SQL)

結果集使用:

while(rs.next()){ . . . . . .}

如何取值?不同的參數類型有不同的訪問器,可以通過下標和類名獲取

rs.getDouble (1);
rs.getDouble(“price”)

說明:

1)數據庫索引與數組的索引不同,數據庫的列序號從1開始計算的。

2)使用get方法的類型與列的數據類型不一致,get方法會嘗試合理的類型轉換,如rs.getString(“price”),會將price列的浮點值轉換爲字符串。

3)做通用話數據封裝時,可以使用getObject進行通用取。

如何管理連接??

使用完ResultSet、Statement或Connection對象,應立即使用close方法關閉,調用順序從裏到外(ResultSet、Statement、Connection)

如何解析SQL異常

SQLWarning w = stat.getWarning();
while(w != null){
            do something with w
            w = w.nextWarning();
}

說明:還有其他的很多方法,見書!!

 

相關其他方法:

Connection :
            Statement createStatement()
             voidcolse()
Statement
            ResultSetexecuteQuery(String sql)
            intexecuteUpdate(String sql)
            booleanexecute(String sql)
            ResultSetgetResultSet()
            intgetUpdateCount()
            voidclose()
            booleanisColsed()
            void coseOncompleion()
ResultSet
            booleannext()
            XxxgetXxx(int columnNumber)
            XxxgetXxx(String columnName)
            <T> TgetObject(int columnNumber,Class<T> type)
            <T> T getObject(intcolumnName,Class<T> type)
            int findColumn(String columnName)
            void close()
             boolean isClosed();

3、不同的執行對象

3.1預備語句(預編譯語句)

如何進行參數化查詢??

PreparedStatement stat = conn.prepareStatement(“select *from user where id = ?”);
stat.serInt(1,12);
ResultSet rs = stat.executeQuery();

        許多數據庫通常都會有自動緩存預備語句,如果相同的查詢被預備兩次,數據庫通常會直接重用查詢策略。

不使用Statement:

1)使用Statement,若是進行參數化查詢,每次查詢都會建立新的查詢語句,消耗查詢性能;

2)使用Statement,如果進行參數化查詢,需要拼接SQL,容易造成SQL注入。

使用statement:

如果進行的查詢是不帶參數的,使用Statement比PrepredStatement更高校,

 

相關其他方法:

Connection
            PreparedStatementprepareStatement(String sql)
PreparedStatement
            voidsetXxx(int n, Xxx x);
            voidclearParameters();
            ResultSetexecuteQuery()
            intexecuteUpdate()                 【如果執行的是數據定義語句(DDL),如CREATE TABLE,則返回0】

3.2 CallableStatement執行存儲過程

CALL pro_findById2(5,@NAME);

(1)存儲過程

mysql> DELIMITER $
mysql> CREATE PROCEDURE pro_findById2(IN eidINT,OUT vname VARCHAR(20))
-> BEGIN
->      SELECT empname INTO vname FROM employeeWHERE id = eid;
-> END $;

(2)測試

String sql = "CALL pro_findById2(?,?)"; //第一個?是輸入參數,第二個?是輸出參數
cstmt =conn.prepareCall(sql);
//設置輸入參數
cstmt.setInt(1, 6);
//設置輸出參數(註冊輸出參數)
cstmt.registerOutParameter(2,java.sql.Types.VARCHAR);
//返回結果到輸出參數中
cstmt.executeQuery();
//從輸出參數的索引中,獲取結果
String result = cstmt.getString(2);            

4、其他

4.1讀寫Lob

Lob分兩種:一種字符型大對象Clob,二進制大對象Blob

Blob:
存入數據庫:

String sql = " insert into test(img)values(?)";
// 連接
con = JdbcUtil.getConnection();
// pstmt 對象
pstmt = con.prepareStatement(sql);
// 獲取圖片流
InputStream in = App_text.class.getResourceAsStream("7.jpg");
pstmt.setBinaryStream(1, in);            
// 執行保存圖片
pstmt.execute(); 

從數據庫讀取:

String sql = "select img from  test where id=2;";
// 連接
con = JdbcUtil.getConnection();
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
if (rs.next()) {
      // 獲取圖片流
      InputStream in =rs.getBinaryStream("img");
      // 圖片輸出流
      FileOutputStream out = new FileOutputStream(new File("c://1.jpg"));
      int len = -1;
      byte b[] = new byte[1024];
      while ((len = in.read(b)) != -1) {
            out.write(b, 0,len);
      }
}

Clob:

存入數據庫:

String sql = "insert into test(content)values(?)";
con = JdbcUtil.getConnection();
pstmt = con.prepareStatement(sql);
// 設置參數
// 先獲取文件路徑
String path = App_text.class.getResource("tips.txt").getPath();
FileReader reader = new FileReader(new File(path));
pstmt.setCharacterStream(1,reader);
// 執行sql
pstmt.executeUpdate();

從數據庫讀取:

String sql = "select * from  test;";
con = JdbcUtil.getConnection();
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
if (rs.next()) {
      // 獲取長文本數據,方式1:
      //Reader r = rs.getCharacterStream("content");
                       
      // 獲取長文本數據,方式2:
      System.out.print(rs.getString("content"));
}
 

4.2獲取主鍵

String sql = "insert into articlevalues(null,?,now(),?)";
//執行語句的同時返回一個主鍵集合
PreparedStatement pst =DB.prepareStmt(conn, sql,Statement.RETURN_GENERATED_KEYS);
pst.setInt(1,0);
pst.setInt(2, 0);
pst.executeUpdate();
      
//得到主鍵集合
ResultSet rsKey =pst.getGeneratedKeys();
rsKey.next();
rootId = rsKey.getInt(1);

4.3多結果集

若是執行存儲過程或者在單個查詢中提交了多個select語句,那麼一個數據庫能返回多個結果集。

 

關鍵點:重複調用getMoreResult方法移動到下一項結果集。

CREATE PROCEDURE proc_test()
BEGIN
select * from person;
select * from person;
END;

Connection conn = getConn();
String sql = "{call proc_test()}";   
CallableStatement ctmt = conn.prepareCall(sql);
boolean hadResults = ctmt.execute();   
int i=0;   
ResultSet rs = null; 
while (hadResults) {   
    System.out.println("result No:----"+(++i));   
    rs = ctmt.getResultSet();   
    while (rs !=null && rs.next()){   
        int id1 = rs.getInt(1);   
        String name1 = rs.getString(2);   
        System.out.println(id1 +":" + name1);   
    }   
    hadResults = ctmt.getMoreResults(); //檢查是否存在更多結果集   
} 

4.4可滾動&可更新結果集

可滾動結果集:

Connection conn = getConn();
/*
 *conn.createStatement(type,concurrency)
 */
// TYPE_SCROLL_SENSITIVE:結果集能滾動,對數據庫變化敏感
// CONCUR_UPDATABLE:結果集可以用於更新數據庫
Statement stat = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stat.executeQuery("select * from user");
// 移動遊標方法
rs.next();
rs.relative(3);
rs.previous();
rs.relative(-1);
rs.absolute(5);
 
// 特殊位置
rs.first();
rs.last();
rs.beforeFirst();
rs.afterLast();

ps:因爲在操作過程中,一直連接着數據庫,可滾動結果集,消耗數據庫資源巨大。

 

可更新結果集:

Connection conn = getConn();
// TYPE_SCROLL_SENSITIVE:結果集能滾動,對數據庫變化敏感
// CONCUR_UPDATABLE:結果集可以用於更新數據庫
Statement stat = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stat.executeQuery("select * from user");
rs.next();
rs.updateString("name","u-2");
rs.updateInt("age", 22);
rs.updateRow();
 
 
rs.insertRow();
rs.deleteRow();

ps:

①更新數據庫行,必須要updateRow(),這樣可以將變化同步到數據庫;插入數據庫一行,使用updateXxx,之後要使用insertRow(),rs.moveToCurrentRow();deleteRow()直接將當前行刪除。

②如果查詢語句涉及到多個表的連接操作,那麼它所產生的結果集將是不可更新的;若是利用主鍵連接表的,那麼它還是可更新結果集。

4.5行集

         RowSet接口擴展自ResultSet接口,卻無需始終保持與數據庫連接,可以完美解決可滾動結果集的,數據庫資源消耗問題。

行集分幾種:

CachedRowSet:緩存的行集。

WebRowSet:緩存的行集,可保存爲XML文件,該文件可移動到Web應用。

FilteredRowSet和JoinRowSet:支持對行集的輕量級操作。

JdbcRowSet:是ResultSet接口的瘦包裝器。

 

行集中的數據來源??

使用JDBC驅動程序從數據庫檢索的數據

從其他數據源獲得的數據,如文件數據

 

行集(Row  Set)的優點??

1)可以斷開數據庫連接操作數據;

2)可以在分佈式系統中的不同組件之間傳遞;

3)默認可更新,可滾動,可序列化,可以方便的在網絡間傳輸;

4)可以方便的使數據在行集與JavaBean對象之間進行轉換。行集中的一行數據可以封裝爲一個JavaBean對象。

Connection conn = getConn();
PreparedStatement pst=conn.prepareStatement("select * from user"); 
//必須設置非自動提交 
conn.setAutoCommit(false); 
ResultSet rs=pst.executeQuery(); 
//創建行集實例 
CachedRowSetImpl rowset=newCachedRowSetImpl(); 
//填充 
rowset.populate(rs); 
 
//這裏已經關閉結果集了!!!
rs.close(); 
pst.close(); 
 
// 在關閉連接之前進行更新操作
rowset.absolute(5); 
rowset.updateInt("English", 55); 
rowset.updateRow();  //更新 
rowset.acceptChanges(conn); //提交 
 
//輸出結果集之前,關閉連接 
conn.close(); 
 
//輸出行集數據 
while(rowset.next()){ 
    System.out.print(rowset.getInt("id")+"\t"); 
    System.out.print(rowset.getInt("Chinese")+"\t"); 
    System.out.print(rowset.getInt("English")+"\t"); 
    System.out.println(rowset.getInt("history")); 
}    

5、元數據

元數據:描述數據庫或者其組成部分的數據。

 

元數據分爲:

關於數據庫的元數據(DatabaseMetaData)

關於結果集的元數據(ResultSetMetaData)

關於預備語句參數的元數據(ParameterMetaData)

 

數據庫元數據

// 獲取連接
Connection conn = JdbcUtil.getConnection();
// 獲取數據庫元數據
DatabaseMetaData metaData = conn.getMetaData();//alt + shift + L 快速獲取方法返回值
     
System.out.println(metaData.getUserName());
System.out.println(metaData.getURL());
System.out.println(metaData.getDatabaseProductName());

參數元數據

// 獲取連接
Connection conn = JdbcUtil.getConnection();
// SQL
String sql = "select * from dept wheredeptid=? and deptName=?";
// Object[] values = {"tom","888"};
           
PreparedStatement pstmt = conn.prepareStatement(sql);
// 參數元數據
ParameterMetaData p_metaDate = pstmt.getParameterMetaData();
// 獲取參數的個數
int count = p_metaDate.getParameterCount();
// 測試
System.out.println(count); 

結果集元數據

Connection conn = JdbcUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement("select * from dept ");
ResultSet rs = pstmt.executeQuery();
// 得到結果集元數據(目標:通過結果集元數據,得到列的名稱)
ResultSetMetaData rs_metaData = rs.getMetaData();          
// 迭代每一行結果
while (rs.next()) {
      // 1. 獲取列的個數
      int count = rs_metaData.getColumnCount();
      // 2. 遍歷,獲取每一列的列的名稱
      for (int i=0; i<count; i++) {
            // 得到列的名稱
            String columnName= rs_metaData.getColumnName(i + 1);
            // 獲取每一行的每一列的值
            ObjectcolumnValue = rs.getObject(columnName);
            // 測試
            System.out.print(columnName +"=" + columnValue + ",");
      }
      System.out.println();
}

說明:getObject可以拿到任意類型行的值

6、事務

            事務:將一組語句構建成一個事務,當所有語句都順利執行,事務可以被提交。否則,如果其中某個語句遇到錯誤,那麼事務將被回滾。

            事務:確保數據庫萬整性。

 

默認情況下,數據庫連接處於自動提交模式。

Connection con;
PreparedStatement pstmt;
String sql_zs = "UPDATE account SETmoney=money-1000 WHERE accountName='張三';";
String sql_ls = "UPDATE1 account SETmoney=money+1000 WHERE accountName='李四';";
try {
      con = JdbcUtil.getConnection();// 默認開啓的隱士事務
      // 一、設置事務爲手動提交
      con.setAutoCommit(false);
 
      /*** 第一次執行SQL ***/
      pstmt = con.prepareStatement(sql_zs);
      pstmt.executeUpdate();
 
      /*** 第二次執行SQL ***/
      pstmt = con.prepareStatement(sql_ls);
      pstmt.executeUpdate();
} catch(Exception e) {
      try {
            // 二、出現異常,需要回滾事務
            con.rollback();
      } catch (SQLException e1) {
            e.printStackTrace();
}
} finally {
      try {
            // 三、所有的操作執行成功,提交事務
            con.commit();
            JdbcUtil.closeAll(con,pstmt, null);
      } catch (SQLException e) {
      }
}


保存點:使用保存點可以更細粒度的控制回滾操作

. . .
try {
      con = JdbcUtil.getConnection();// 默認開啓的隱士事務
      // 一、設置事務爲手動提交
      con.setAutoCommit(false);
 
      /*** 第一次執行SQL ***/
      pstmt = con.prepareStatement(sql_zs);
      pstmt.executeUpdate();
 
      Savepoint svpt = pstmt.setSavepoint();
 
      /*** 第二次執行SQL ***/
      pstmt = con.prepareStatement(sql_ls);
      pstmt.executeUpdate();
} catch(Exception e) {
      // 二、出現異常,需要回滾到回滾點
      con.rollback(svpt);
      . . .
} finally {
      . . .
}

Ps:當不需要回滾點時,可以釋放回滾點:使用conn.releaseSavepoint(svpt)。

7、批量操作

         當程序需要執行多條語句時,可以使用批量操作,如執行上萬條insert語句,可以使用批量更新來提高程序性能。

// SQL
String sql = "INSERT INTOadmin(userName,pwd) values(?,?)";
try {
      // 獲取連接
      con = JdbcUtil.getConnection();
      // 創建stmt
      pstmt = con.prepareStatement(sql);             // 【預編譯SQL語句】
      for (int i=0; i<list.size(); i++) {
            Admin admin =list.get(i);
            // 設置參數
            pstmt.setString(1,admin.getUserName());
            pstmt.setString(2, admin.getPwd());            
            // 添加批處理
            pstmt.addBatch();                              // 【不需要傳入SQL】
                       
            // 測試:每5條執行一次批處理
            if (i % 5 == 0) {
                  // 批量執行
                  pstmt.executeBatch();
                  // 清空批處理
                  pstmt.clearBatch();
            }
      }
                 
      // 批量執行
      pstmt.executeBatch();
      // 清空批處理
      pstmt.clearBatch();
} catch(Exception e) {
      e.printStackTrace();
} finally {
      JdbcUtil.closeAll(con, pstmt, rs);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章