1. 程式人生 > >JAVA基礎--JDBC

JAVA基礎--JDBC

jdbc

一、JDBC

一、JDBC概述

  1. JDBC和ODBC都基於一個思想:根據API編寫的程序都可以與驅動管理器進行通信,而驅動管理器則通過驅動程序和實際的數據庫進行通信。

  2. 在三層應用模式中,客戶端不直接調用數據庫,而是調用服務器上的中間件層,最後由中間層完成對數據庫的操作

  3. 連接到數據庫:

        String driver = "com.mysql.jdbc.Driver";
        String dbName = "bookdb";
        String passwrod = "root";
        String userName = "123456";
        String url = "jdbc:mysql://localhost:3308/" + dbName;
        String sql = "select * from users";
        Class.forName(driver);
        Connection conn = DriverManager.getConnection(url, userName, passwrod);
       
4.關閉數據庫:
     操作完成以後要把所有使用的JDBC對象全都關閉,以釋放JDBC資源,關閉順序和聲   
     明順序相反:   
     1、關閉記錄集   
     2、關閉聲明   
     3、關閉連接對象   
          if(rs != null){   // 關閉記錄集   
        try{   
            rs.close() ;   
        }catch(SQLException e){   
            e.printStackTrace() ;   
        }   
          }   
          if(stmt != null){   // 關閉聲明   
        try{   
            stmt.close() ;   
        }catch(SQLException e){   
            e.printStackTrace() ;   
        }   
          }   
          if(conn != null){  // 關閉連接對象   
         try{   
            conn.close() ;   
         }catch(SQLException e){   
            e.printStackTrace() ;   
         }   
          }

二、執行SQL語句

  1. 創建Statement對象 Statement st = conn.createStatement();

  2. 調用Statement接口executeUpdate方法st.executeUpdate(sql);

    1】此方法返回受SQL命令影響的行數,或者如果沒影響即返回0.

    2】此方法可以執行INSERT、UPDATE、DELETE、CREATE、TABLE、DROP.但是不可以執行SELECT查詢

  3. execute方法可以執行任意的SQL語句。

  4. executeQuery方法可以執行查詢操作,返回一個ResultSet類型的對象。


ResultSet rs = st.executeQuery("SELECT * from table");

while(re.next()){

System.out.println("ID:"+rs.getInt("ID")+"name :"+rs.getString(2));

}

rs代表一行,通過數字型參數和字符串參數可以獲得到數據。索引是從1開始

5.每個Connection對象都可以創建一個或多個Statement對象。同一個Statement對象可以用於多個不相幹的命令和查詢,但是一個Statement對象最多只能有一個打開的結果集。


三、執行查詢操作

  1. 預備語句

 String sql = "insert into students (Name,Sex,Age) values(?,?,?)";
PreparedStatement pstmt;
pstmt = (PreparedStatement)conn.prepareStatement(sql);//使用預處理的方式創建對象
pstmt.setString(1, student.getName());//第一個?號的內容
pstmt.setInt(2, student.getSex());
pstmt.setDate(3, student.getAge());
int r = pstmt.executeUpdate();//執行SQL 語句,更新數據庫

變量用?來表示,通過setXXX來對變量賦值。如果是查詢那就調用pstmt.executeQuery();得到一個ResultSet


2.讀寫LOB:

數據庫還可以存儲大對象,二進制大對象成為BLOB,字符型大對象成為CLOB。


四、可滾動和可更新的結果集

1.設置

要讓ResultSet可以滾動個和更新,必須在創建Statement對象的時候使用下面的方式指定對應的參數:

Statement stmt = conn.createStatement(type, concurrency);

對於PreparedStatement,使用下面的方式指定參數:

PreparedStatement pstmt = conn.prepareStatement(sql, type, concurrency);

其中,type表示ResuleSet的類型,而concurrency表示是否可以使用ResuleSet來更新數據庫。

type和concurrency的取值以及含義:

ResultSet.TYPE_FORWARD_ONLY - 結果集不能滾動,這事默認值;

ResultSet.TYPE_SCROLL_INSENSITIVE - 結果集可以滾動,但ResuleSet對數據庫中發送的數據改變不敏感;

ResultSet.TYPE_SCROLL_SENSITIVE - 結果集可以滾動,並且ResuleSet對數據庫中發生的改變敏感


ResultSet.CONCUR_READ_ONLY - 只讀結果集,不能用於更新數據庫;

ResultSet.CONCUR_UPDATABLE - 可更新結果集,可以用於更新數據庫;


當使用TYPE_SCROLL_INSENSITIVE或者TYPE_SCROLL_SENSITIVE來創建Statement對象時,可以使用ResultSet 的 first()/last()/beforeFirst()/afterLast()/relative()/absolute()等方法在結果集中隨意前後移動。


提示:即使使用了CONCUR_UPDATABLE參數來創建Statement,得到的記錄集也並非一定是“可更新的”,如果你的記錄集來自於合並查詢,即該查詢的結果來自多個表格,那麽這樣的結果集就可能不是可更新的結果集。可以使用ResuleSet類的getConcurrency()方法來確定是否為可更新的的結果集。

如果結果集是可更新的,那麽可使用ResultSet的updateRow(),insertRow(),moveToCurrentRow(),deleteRow(),cancelRowUpdates() 等方法來對數據庫進行更新。

如果沒有設置可更新結果集 那將抱com.microsoft.sqlserver.jdbc.SQLServerException: 結果集不可更新

【方法】

next(),使遊標向下一條記錄移動。

  previous() ,使遊標上一條記錄移動,前提前面還有記錄。

  absolute(int row),可以使用此方法跳到指定的記錄位置。定位成功返回true,

不成功返回false,返回值為false,則遊標不會移動。

  afterLast() ,遊標跳到最後一條記錄之後。

  beforeFirst() ,遊標跳到第一條記錄之前。(跳到遊標初始位)

  first(),遊標指向第一條記錄。

  last(),遊標指向最後一條記錄。

  relative(int rows) ,相對定位方法,參數值可正可負,參數為正,遊標從當前位置向下移動指定值,參數為負,

遊標從當前位置向上移動指定值。


2.更新

【1】Statement stm = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);

這樣調用executeQuery得到的結果集就是可更新的結果集

【2】插入新行:

ResultSet結果集中,1.先使用moveToInsertRow(),將遊標移到和結果集結構類似的緩沖區中

  2.然後可以使用updateXxx(int column,columnType value)方法來更新指定列數據,

  3.再使用insertRow() 方法插入記錄,

  4.最後將遊標指回原位,moveToCurrentRow() 。

rs.first();

//將光標移動到插入行上

rs.moveToInsertRow();

//構建行數據

rs.updateString(2, "xxxx");

rs.updateInt(3, "x");

//插入一行

rs.insertRow();


五、行集

1)行集是從表格式數據源中檢索出來的一行或多行數據:

與結果集(ResultSet)類似(RowSet接口繼承了ResultSet接口)

但是使用結果集時與數據庫的連接不能斷開,而行集是可以在關閉連接的情況下存在的,行集一般在關閉連接的情況下使用,只有在進行一些特殊操作時需要才建立連接。

2)行集中的數據來源:

使用JDBC驅動程序從數據庫檢索的數據、從其他數據源獲得的數據,如文件數據

行集(Row Set)的優點:

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

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

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

4)可以方便的使數據在行集與JavaBean對象之間進行轉換

行集中的一行數據可以封裝為一個JavaBean對象

JavaBean是一個類,類中有若幹私有屬性,然後有與屬性相關的公有的get和set方法。這樣,我們可以將一行數據存入一個JavaBean對象中

行集相關接口:

javax.sql.RowSet:所有行集的父接口
Java.sql.rowset.CachedRowSet:數據行容器,可以在內存中緩存各行數據,在對數據進行操作時不需要連接到數據源。可以修改CachedRowSet對象中的數據,這些修改隨後可以被更新到數據庫。同時,也是一個JavaBean組件,可滾動,可更新,可序列化。
java.sql.rowset.JDBCRowSet:數據行容器,可滾動,可更新。始終連接到數據庫。
java.sql.rowset.WebRowSet:被緩存的行集,該行集數據可以保存為xml文件。
java.sql.rowset.JoinRowSet:數據行容器,這些數據取自那些形成SQL JOIN關系的RowSet對象,有連接和無連接的RowSet對象都可成為JOIN的一部分。
java.sql.rowset.FilteredRowSet:數據行容器,可以對數據進行條件過濾。

行集的填充:

1)傳統JDBC方式
Class.forName(“com.mysql.jdbc.Driver”);
String connectionUrl = “jdbc:mysql://localhost:3306/test?user=root&password=root ";
Connection connection = DriverManager.getConnection(connectionUrl);
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql);
CachedRowSetImpl rowset = new CachedRowSetImpl();//得到行集
rowset.populate(rs);
rs.close(); statement.close();connection.close();

2)設置行集屬性連接數據庫並檢索數據
CachedRowSetImpl rowset = new CachedRowSetImpl();
rowset.setUrl(“jdbc:mysql://127.0.0.1:3306/test”);
rowset.setUsername(“root”);
rowset.setPassword(“test”);
rowset.setCommand(“select * from student”);
rowset.execute();

-------------------------------------------------------------------------------------------------------------------------------

RowSet的使用:

1、以下行集類使用Java的默認實現類
CachedRowSetImpl rowset = new CachedRowSetImpl();
//CachedRowSetImpl是SUN定義的CachedRow接口默認實現類
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(“select * from table1”);
rowset.populate(rs); //填充行集
rs.close(); statement.close();connection.close(); //關閉連接
//顯示CachedRow數據,使用從結果集繼承的方法
while (rowset.next()) {   
System.out.print(rowset.getString(1) + " : ");   System.out.println(rowset.getString("CompanyName"));   
}

2、更新CachedRow數據(conn.setAutoCommit(false);)
crs.last();
crs.updateShort(3, 58);
crs.updateInt(4, 150000);
crs.updateRow(); //更新行集
crs.acceptChanges(conn); //更新數據庫
註意事項:

使用行集修改數據與行集中的數據填充方式無關。但是要保證acceptChanges()方法執行時有可用的連接對象,如果行集中有可用的連接對象可以調用acceptChanges(),如果行集中沒有可用的連接對象,需要調用acceptChanges(Connection)方法。

使用行集對數據進行添加、修改、刪除時,必須保證事務提交為非自動提交(acceptChanges()方法會調用commit()方法)。

3、添加數據
crs.setTableName(“student”); //添加數據必須指定
crs.moveToInsertRow(); //標識指針的位置
crs.updateInt(“id”,33); //添加數據時主鍵必須指定
crs.updateString(“name", "Shakespeare");
crs.updateShort(“age", 58);
crs.insertRow(); //更新行集
crs.moveToCurrentRow(); //讓指針回到標識的位置
crs.acceptChanges(conn); //更新數據庫

//刪除數據
crs.first();
crs.deleteRow(); //刪除行集數據
crs.acceptChanges(conn); //更新數據庫

4、分頁1:使用結果集填充行集
rs = stm.executeQuery(“select * from student”);
crs.setPageSize(4); //設置每頁行數
crs.populate(rs, 10); //從結果集的第十行開始取4行數據填充行集

crs.nextPage(); //獲得後續頁數據,如果有數據返回true

註意:

此時resultset,statement(preparedstatement),connection不能關閉,否則行集無法取得後續頁數據

5、分頁2:行集建立連接從數據庫讀取數據
CachedRowSetImpl crs= new CachedRowSetImpl();
crs.setUrl(“jdbc:mysql://127.0.0.1:3306/test”);
crs.setUsername(“root”);
crs.setPassword(“root”);
crs.setCommand(“select * from student”);
crs.setPageSize(4); //每頁行數,一定要在execute()方法執行前設置,否則無效
crs.execute();
……
crs.nextPage(); //獲得下一頁的數據,與結果集,連接對象無關

public class JDBC3 {  
    public static void main(String[] args) {  
        Connection conn=null;  
        PreparedStatement pst=null;  
        ResultSet rs=null;  
        DBUtil util=new DBUtil();  
        String sql="select*from score";  
        try {  
            conn=util.getConn();  
            pst=conn.prepareStatement(sql);  
            //必須設置非自動提交  
            conn.setAutoCommit(false);  
            rs=pst.executeQuery();  
            //創建行集實例  
            CachedRowSetImpl rowset=new CachedRowSetImpl();  
            //填充  
            rowset.populate(rs);  
            rs.close();  
            pst.close();  
            rowset.absolute(5);  
            //rowset.updateInt("id", 5);  
            rowset.updateInt("English", 55);  
            //更新  
            rowset.updateRow();  
            //提交  
            rowset.acceptChanges(conn);  
              
            //輸出結果集之前,關閉連接  
            conn.close();  
              
            //輸出行集數據  
            System.out.println("id\tChinese\tEnglish\thistory");  
            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"));  
            }  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
    }  
}


public class JDBC4 {  
    public static void main(String[] args) {  
        try {  
            CachedRowSetImpl rowset=new CachedRowSetImpl();  
            rowset.setUrl("jdbc:mysql://localhost:3308/test");  
            rowset.setUsername("root");  
            rowset.setPassword("123456");  
            rowset.setCommand("select*from score;");  
            rowset.execute();  
            //輸出行集數據  
            System.out.println("id\tChinese\tEnglish\thistory");  
            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"));  
            }  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
    }  
}


public class JDBC6 {  
    public static void main(String[] args) {  
        try {  
            CachedRowSetImpl rowset=new CachedRowSetImpl();  
            rowset.setUrl("jdbc:mysql://localhost:3308/test");  
            rowset.setUsername("root");  
            rowset.setPassword("123456");  
            rowset.setCommand("select*from score;");  
            //設置每頁顯示的數據條數  
            rowset.setPageSize(3);  
            rowset.execute();  
            int i=2;  
            System.out.println("第1頁");  
            System.out.println("id\tChinese\tEnglish\thistory");  
              
            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"));  
            }  
            while(rowset.nextPage()){  
                System.out.println("第"+i+"頁");  
                i++;  
                System.out.println("id\tChinese\tEnglish\thistory");  
                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"));  
                }  
            }  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
          
    }  
}

六、事務和元數據

事務是指一個工作單元,它包含了一組添加,刪除,修改等數據操作命令,這組命令作為一個整體向系統提交執行,要麽都執行成功,要麽全部恢復

在JDBC中使用事務

1)con.setAutoCommit(false),取消自動提交

2)對數據庫執行一個或多個操作(一個或多個SQL語句)

3)con.commit(),提交事務(上面的第二部的多個操作就作為一個整體提交執行)

4)如果某個操作失敗,通過con.rollback()回滾所有操作(撤銷以上的操作,將數據恢復為執行前狀態)

事務處理依賴於底層的數據庫實現,不同的驅動程序對事務處理的支持程度可能不同


【事務特性】

1.事務(Transaction)的四個屬性(ACID)
原子性(Atomic) 對數據的修改要麽全部執行,要麽全部不執行。
一致性(Consistent) 在事務執行前後,數據狀態保持一致性。
隔離性(Isolated) 一個事務的處理不能影響另一個事務的處理。
持續性(Durable) 事務處理結束,其效果在數據庫中持久化。

2.事務並發處理可能引起的問題
臟讀(dirty read) 一個事務讀取了另一個事務尚未提交的數據,
不可重復讀(non-repeatable read) 一個事務的操作導致另一個事務前後兩次讀取到不同的數據
幻讀(phantom read) 一個事務的操作導致另一個事務前後兩次查詢的結果數據量不同。
舉例:
事務A、B並發執行時,
當A事務update後,B事務select讀取到A尚未提交的數據,此時A事務rollback,則B讀到的數據是無效的"臟"數據。
當B事務select讀取數據後,A事務update操作更改B事務select到的數據,此時B事務再次讀去該數據,發現前後兩次的數據不一樣。
當B事務select讀取數據後,A事務insert或delete了一條滿足A事務的select條件的記錄,此時B事務再次select,發現查詢到前次不存在的記錄("幻影"),或者前次的某個記錄不見了。

JDBC的事務支持
JDBC對事務的支持體現在三個方面:
1.自動提交模式(Auto-commit mode)
Connection提供了一個auto-commit的屬性來指定事務何時結束。
a.當auto-commit為true時,當每個獨立SQL操作的執行完畢,事務立即自動提交,也就是說每個SQL操作都是一個事務。
一個獨立SQL操作什麽時候算執行完畢,JDBC規範是這樣規定的:
對數據操作語言(DML,如insert,update,delete)和數據定義語言(如create,drop),語句一執行完就視為執行完畢。
對select語句,當與它關聯的ResultSet對象關閉時,視為執行完畢。
對存儲過程或其他返回多個結果的語句,當與它關聯的所有ResultSet對象全部關閉,所有update count(update,delete等語句操作影響的行數)和output parameter(存儲過程的輸出參數)都已經獲取之後,視為執行完畢。
b. 當auto-commit為false時,每個事務都必須顯示調用commit方法進行提交,或者顯示調用rollback方法進行回滾。auto-commit默認為true。


JDBC提供了5種不同的事務隔離級別,在Connection中進行了定義。

2.事務隔離級別(Transaction Isolation Levels)
JDBC定義了五種事務隔離級別:
TRANSACTION_NONE JDBC驅動不支持事務
TRANSACTION_READ_UNCOMMITTED 允許臟讀、不可重復讀和幻讀。
TRANSACTION_READ_COMMITTED 禁止臟讀,但允許不可重復讀和幻讀。
TRANSACTION_REPEATABLE_READ 禁止臟讀和不可重復讀,單運行幻讀。
TRANSACTION_SERIALIZABLE 禁止臟讀、不可重復讀和幻讀。

3.保存點(SavePoint)
JDBC定義了SavePoint接口,提供在一個更細粒度的事務控制機制。當設置了一個保存點後,可以rollback到該保存點處的狀態,而不是rollback整個事務。
Connection接口的setSavepoint和releaseSavepoint方法可以設置和釋放保存點。

JDBC規範雖然定義了事務的以上支持行為,但是各個JDBC驅動,數據庫廠商對事務的支持程度可能各不相同。如果在程序中任意設置,可能得不到想要的效果。為此,JDBC提供了DatabaseMetaData接口,提供了一系列JDBC特性支持情況的獲取方法。比如,通過DatabaseMetaData.supportsTransactionIsolationLevel方法可以判斷對事務隔離級別的支持情況,通過DatabaseMetaData.supportsSavepoints方法可以判斷對保存點的支持情況。


語法

Connection con = DriverManger.getConnection(urlString);
con.setAutoCommit(false);//取消自動提交
Statement stm = con.createStatement();
stm.executeUpdate(sqlString);
con.transactionEndMethod; //事務方法成功則提交、失敗則回滾con.commit() or con.rollback();

示例:

 try{
   Class.forName(drv).newInstance();//加載驅動
   conn = DriverManager.getConnection(url,user,pwd);
   conn.setAutoCommit(false);//禁止自動提交 
   stmt = conn.createStatement();
   //插入數據操作
   stmt.executeUpdate("insert into(username,password,email) user values(‘admin‘,‘admin‘,[email protected])");
   //更新數據操作
   stmt.executeUpdate("update user set [email protected]");
   //事務提交
   conn.commit(); 
  }catch(Exception ex){
   try{
    //如果失敗則事務回滾
    conn.rollback(); 
   }catch(Exception e){
    e.printStackTrace();
   }
  }finally{
   //關閉連接
   if(conn != null){
    try{
     conn.close();
    }catch(Exception e){
     e.printStackTrace();
    }
   }
  }




JAVA基礎--JDBC