您好,登錄后才能下訂單哦!
一、JDBC
一、JDBC概述
JDBC和ODBC都基于一個思想:根據API編寫的程序都可以與驅動管理器進行通信,而驅動管理器則通過驅動程序和實際的數據庫進行通信。
在三層應用模式中,客戶端不直接調用數據庫,而是調用服務器上的中間件層,最后由中間層完成對數據庫的操作
連接到數據庫:
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語句
創建Statement對象 Statement st = conn.createStatement();
調用Statement接口executeUpdate方法st.executeUpdate(sql);
1】此方法返回受SQL命令影響的行數,或者如果沒影響即返回0.
2】此方法可以執行INSERT、UPDATE、DELETE、CREATE、TABLE、DROP.但是不可以執行SELECT查詢
execute方法可以執行任意的SQL語句。
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對象最多只能有一個打開的結果集。
三、執行查詢操作
預備語句
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','admin@123.com')"); //更新數據操作 stmt.executeUpdate("update user set email='admin@163.com'"); //事務提交 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(); } } }
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。