jdbc入门(二)
一、事務
? ? 1.事務概述
????????事務(Transaction),一般是指要做的或所做的事情。在計算機術語中是指訪問并可能更新數據庫中各種數據項的一個程序執行單元(unit)。事務通常由高級數據庫操縱語言或編程語言(如SQL,C++或Java)書寫的用戶程序的執行所引起,并用形如begin transaction和end transaction語句(或函數調用)來界定。事務由事務開始(begin transaction)和事務結束(end transaction)之間執行的全體操作組成。
????????銀行轉賬!張三轉10000塊到李四的賬戶,這其實需要兩條SQL語句:
????????????給張三的賬戶減去10000元;
????????????給李四的賬戶加上10000元。
????????如果在第一條SQL語句執行成功后,在執行第二條SQL語句之前,程序被中斷了(可能是拋出了某個異常,也可能是其他什么原因),那么李四的賬戶沒有加上10000元,而張三卻減去了10000元。這肯定是不行的!
????????事務中的多個操作,要么完全成功,要么完全失敗!
? ? 2.事務的四大特性(ACID)
????????原子性(Atomicity):事務中所有操作是不可再分割的原子單位。事務中所有操作要么全部執行成功,要么全部執行失敗。
????????一致性(Consistency):事務執行后,數據庫狀態與其它業務規則保持一致。如轉賬業務,無論事務執行成功與否,參與轉賬的兩個賬號余額之和應該是不變的。
????????隔離性(Isolation):隔離性是指在并發操作中,不同事務之間應該隔離開來,使每個并發中的事務不會相互干擾。
????????持久性(Durability):一旦事務提交成功,事務中所有的數據操作都必須被持久化到數據庫中,即使提交事務后,數據庫馬上崩潰,在數據庫重啟時,也必須能保證通過某種機制恢復數據。
? ? 3.MySQL中的事務
????????在默認情況下,MySQL每執行一條SQL語句,都是一個單獨的事務。如果需要在一個事務中包含多條SQL語句,那么需要開啟事務和結束事務。
????????????????開啟事務:start transaction;
????????????????結束事務:commit或rollback。
????????在執行SQL語句之前,先執行strat transaction,這就開啟了一個事務(事務的起點),然后可以去執行多條SQL語句,最后要結束事務,commit表示提交,即事務中的多條SQL語句所做出的影響會持久化到數據庫中。或者rollback,表示回滾,即回滾到事務的起點,之前做的所有操作都被撤消了!
? ? 4.JDBC事務
????????同一事務中所有的操作,都在使用同一個Connection對象!
????????Connection的三個方法與事務相關:
????????setAutoCommit(boolean):設置是否為自動提交事務,如果true(默認值就是true)表示自動提交,也就是每條執行的SQL語句都是一個單獨的事務,如果設置false,那么就相當于開啟了事務了;con.setAutoCommit(false)表示開啟事務!!!
????????commit():提交結束事務;con.commit();表示提交事務
????????rollback():回滾結束事務。con.rollback();表示回滾事務
?
????????jdbc處理事務的代碼格式:
????????????????try {
????????????????? con.setAutoCommit(false);//開啟事務…
????????????????? ….
????????????????? …
????????????????? con.commit();//try的最后提交事務
????????????????} catch() {
????????????????? con.rollback();//回滾事務
????????????????}
代碼演示:
AccountDao.java
public class AccountDao {public void updateBalance(Connection con, String name, double balance) {try {String sql = "update account set balance=balance+? where name=?";PreparedStatement psmt = con.prepareStatement(sql);psmt.setDouble(1, balance);psmt.setString(2, name);psmt.executeUpdate();} catch (SQLException e) {e.printStackTrace();}} }jdbcUtils.java
public class jdbcUtils {private static Properties props = null;// 只在JdbcUtils類被加載時執行一次!static {// 給props進行初始化,即加載dbconfig.properties文件到props對象中try {InputStream in = jdbcUtils.class.getClassLoader().getResourceAsStream("dbconfig.properties");props = new Properties();props.load(in);} catch (IOException e) {throw new RuntimeException(e);}// 加載驅動類try {Class.forName(props.getProperty("driverClassName"));} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}// 獲取連接!public static Connection getConnection() throws SQLException {// 得到Connectionreturn DriverManager.getConnection(props.getProperty("url"),props.getProperty("username"), props.getProperty("password"));} }dbcconfig.properties
driverClassName=com.mysql.jdbc.Driver url=jdbc\:mysql\://localhost\:3306/mydbc?rewriteBatchedStatements\=true username=root password=rootService.java
public void zhuanZhang(String from, String to, double money) {Connection con = null;try {con = jdbcUtils.getConnection();con.setAutoCommit(false);AccountDao dao = new AccountDao();dao.updateBalance(con, from, -money);dao.updateBalance(con, to, money);con.commit();con.close();} catch (Exception e) {try {con.rollback();con.close();} catch (SQLException e1) {e1.printStackTrace();}}}Test類
@Testpublic void fun1() {zhuanZhang("zs", "ls", 100);}? ? 5.事務隔離級別
? ? (1)事務的并發讀問題
????????臟讀:讀取到另一個事務未提交數據;
????????不可重復讀:兩次讀取不一致;
????????幻讀(虛讀):讀到另一事務已提交的數據。
????????不可重復讀和幻讀的區別:
????????????????不可重復讀是讀取到了另一事務的更新;
????????????????幻讀是讀取到了另一事務的插入(MySQL中無法測試到幻讀);
? ? (2)四大隔離級別
????????4個等級的事務隔離級別,在相同數據環境下,使用相同的輸入,執行相同的工作,根據不同的隔離級別,可以導致不同的結果。不同事務隔離級別能夠解決的數據并發問題的能力是不同的。
????? ? 1.SERIALIZABLE(串行化)
????????????????不會出現任何并發問題,因為它是對同一數據的訪問是串行的,非并發訪問的;
????????????????性能最差;
????????2.REPEATABLE READ(可重復讀)(MySQL)
????????????????防止臟讀和不可重復讀,不能處理幻讀問題
????????????????性能比SERIALIZABLE好
????????3.READ COMMITTED(讀已提交數據)(Oracle)
????????????????防止臟讀,沒有處理不可重復讀,也沒有處理幻讀;
????????????????性能比REPEATABLE READ好
????????4.READ UNCOMMITTED(讀未提交數據)
????????????????可能出現任何事務并發問題
????????????????性能最好
? ? (3)MySQL隔離級別
? ? ????MySQL的默認隔離級別為REPEATABLE READ,可以通過以下語句查看:
select @@tx_isolation????????也可以通過下面語句來設置當前連接的隔離級別:
set transaction isolationlevel [4選1]? ? (4)JDBC設置隔離級別
????????con. setTransactionIsolation(int level)
????????????參數可選值如下:
????????????????Connection.TRANSACTION_READ_UNCOMMITTED;
????????????????Connection.TRANSACTION_READ_COMMITTED;
????????????????Connection.TRANSACTION_REPEATABLE_READ;
????????????????Connection.TRANSACTION_SERIALIZABLE。
二、數據庫連接池
? ? 1.數據庫連接池的概念
????????用池來管理Connection,這可以重復使用Connection。有了池,所以我們就不用自己來創建Connection,而是通過池來獲取Connection對象。當使用完Connection后,調用Connection的close()方法也不會真的關閉Connection,而是把Connection“歸還”給池。池就可以再利用這個Connection對象了。
? ??
? ? 2.JDBC數據連接池接口(DataSource)
????????Java為數據庫連接池提供了公共的接口:javax.sql.DataSource,各個廠商可以讓自己的連接池實現這個接口。這樣應用程序可以方便的切換不同廠商的連接池!
? ? 3.DBCP連接池
????? ? (1)什么是DBCP連接池?
????????????????DBCP是Apache提供的一款開源免費的數據庫連接池!
????? ? (2)DBCP的使用
public void fun1() throws SQLException {BasicDataSource ds = new BasicDataSource();ds.setUsername("root");ds.setPassword("123");ds.setUrl("jdbc:mysql://localhost:3306/mydb1");ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setMaxActive(20);//最大連接數ds.setMaxIdle(10);//最大空閑連接數ds.setInitialSize(10);//初始化連接數ds.setMinIdle(2);//最小空閑連接數ds.setMaxWait(1000);//最大等待毫秒數Connection con = ds.getConnection();System.out.println(con.getClass().getName());con.close();//關閉連接只是把連接歸還給池!????? ? (3)DBCP的配置信息
#基本配置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mydb1 username=root password=123#初始化池大小,即一開始池中就會有10個連接對象 默認值為0 initialSize=0#最大連接數,如果設置maxActive=50時,池中最多可以有50個連接,當然這50個連接中包含被使用的和沒被使用的(空閑) #你是一個包工頭,你一共有50個工人,但這50個工人有的當前正在工作,有的正在空閑 #默認值為8,如果設置為非正數,表示沒有限制!即無限大 maxActive=8#最大空閑連接 #當設置maxIdle=30時,你是包工頭,你允許最多有20個工人空閑,如果現在有30個空閑工人,那么要開除10個 #默認值為8,如果設置為負數,表示沒有限制!即無限大 maxIdle=8#最小空閑連接 #如果設置minIdel=5時,如果你的工人只有3個空閑,那么你需要再去招2個回來,保證有5個空閑工人 #默認值為0 minIdle=0#最大等待時間 #當設置maxWait=5000時,現在你的工作都出去工作了,又來了一個工作,需要一個工人。 #這時就要等待有工人回來,如果等待5000毫秒還沒回來,那就拋出異常 #沒有工人的原因:最多工人數為50,已經有50個工人了,不能再招了,但50人都出去工作了。 #默認值為-1,表示無限期等待,不會拋出異常。 maxWait=-1#連接屬性 #就是原來放在url后面的參數,可以使用connectionProperties來指定 #如果已經在url后面指定了,那么就不用在這里指定了。 #useServerPrepStmts=true,MySQL開啟預編譯功能 #cachePrepStmts=true,MySQL開啟緩存PreparedStatement功能, #prepStmtCacheSize=50,緩存PreparedStatement的上限 #prepStmtCacheSqlLimit=300,當SQL模板長度大于300時,就不再緩存它 connectionProperties=useUnicode=true;characterEncoding=UTF8;useServerPrepStmts=true;cachePrepStmts=true;prepStmtCacheSize=50;prepStmtCacheSqlLimit=300#連接的默認提交方式 #默認值為true defaultAutoCommit=true#連接是否為只讀連接 #Connection有一對方法:setReadOnly(boolean)和isReadOnly() #如果是只讀連接,那么你只能用這個連接來做查詢 #指定連接為只讀是為了優化!這個優化與并發事務相關! #如果兩個并發事務,對同一行記錄做增、刪、改操作,是不是一定要隔離它們啊? #如果兩個并發事務,對同一行記錄只做查詢操作,那么是不是就不用隔離它們了? #如果沒有指定這個屬性值,那么是否為只讀連接,這就由驅動自己來決定了。即Connection的實現類自己來決定! defaultReadOnly=false#指定事務的事務隔離級別 #可選值:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE #如果沒有指定,那么由驅動中的Connection實現類自己來決定 defaultTransactionIsolation=REPEATABLE_READ? ? 4.C3P0連接池
????? ? (1)什么是C3P0連接池?
????????C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規范和JDBC2的標準擴展。目前使用它的開源項目有Hibernate,Spring等。
????? ? (2)C3P0的使用
????????????????C3P0中池類是:ComboPooledDataSource。
public void fun1() throws PropertyVetoException, SQLException {ComboPooledDataSource ds = new ComboPooledDataSource();ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb1");ds.setUser("root");ds.setPassword("123");ds.setDriverClass("com.mysql.jdbc.Driver");ds.setAcquireIncrement(5);//每次的增量為5ds.setInitialPoolSize(20);//初始化連接數ds.setMinPoolSize(2);//最少連接數ds.setMaxPoolSize(50);//最多連接數Connection con = ds.getConnection();System.out.println(con);con.close();}配置文件要求:
?
????????文件名稱:必須叫c3p0-config.xml
????????文件位置:必須在src下
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config><default-config><property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property><property name="driverClass">com.mysql.jdbc.Driver</property><property name="user">root</property><property name="password">123</property><property name="acquireIncrement">3</property><property name="initialPoolSize">10</property><property name="minPoolSize">2</property><property name="maxPoolSize">10</property></default-config><named-config name="oracle-config"><property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property><property name="driverClass">com.mysql.jdbc.Driver</property><property name="user">root</property><property name="password">123</property><property name="acquireIncrement">3</property><property name="initialPoolSize">10</property><property name="minPoolSize">2</property><property name="maxPoolSize">10</property></named-config> </c3p0-config>????????c3p0的配置文件中可以配置多個連接信息,可以給每個配置起個名字,這樣可以方便的通過配置名稱來切換配置信息。上面文件中默認配置為mysql的配置,名為oracle-config的配置也是mysql的配置。
使用默認的配置信息
public void fun2() throws PropertyVetoException, SQLException {ComboPooledDataSource ds = new ComboPooledDataSource();Connection con = ds.getConnection();System.out.println(con);con.close();}使用名為oracle-config的配置信息
public void fun2() throws PropertyVetoException, SQLException {ComboPooledDataSource ds = new ComboPooledDataSource("orcale-config");Connection con = ds.getConnection();System.out.println(con);con.close();}三、Tomcat配置連接池
? ? 1.Tomcat配置JNDI資源
?
????????JNDI(Java Naming and Directory Interface),Java命名和目錄接口。JNDI的作用就是:在服務器上配置資源,然后通過統一的方式來獲取配置的資源。
????????我們這里要配置的資源當然是連接池了,這樣項目中就可以通過統一的方式來獲取連接池對象了。
????????下圖是Tomcat文檔提供的:
?
?
配置JNDI資源需要到<Context>元素中配置<Resource>子元素:
????????name:指定資源的名稱,這個名稱可以隨便給,在獲取資源時需要這個名稱;
????????factory:用來創建資源的工廠,這個值基本上是固定的,不用修改;
????????type:資源的類型,我們要給出的類型當然是我們連接池的類型了
????????bar:表示資源的屬性,如果資源存在名為bar的屬性,那么就配置bar的值。對于DBCP連接池而言,你需要配置的不是bar,因為它沒有bar這個屬性,而是應該去配置url、username等屬性。
<Context> <Resource name="mydbcp" type="org.apache.tomcat.dbcp.dbcp.BasicDataSource"factory="org.apache.naming.factory.BeanFactory"username="root" password="123" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1/mydb1"maxIdle="3"maxWait="5000"maxActive="5"initialSize="3"/> </Context>? ? 2.獲取資源
?
????????配置資源的目的當然是為了獲取資源了。只要你啟動了Tomcat,那么就可以在項目中任何類中通過JNDI獲取資源的方式來獲取資源了。
????????下圖是Tomcat文檔提供的,與上面Tomcat文檔提供的配置資源是對應的。
?
????????獲取資源:
?
????????????????Context:javax.naming.Context;
????????????????InitialContext:javax.naming.InitialContext;
????????????????lookup(String):獲取資源的方法,其中”java:comp/env”是資源的入口(這是固定的名稱),獲取過來的還是一個Context,這說明需要在獲取到的Context上進一步進行獲取。”bean/MyBeanFactory”對應<Resource>中配置的name值,這回獲取的就是資源對象了。
Context cxt = new InitialContext(); DataSource ds = (DataSource)cxt.lookup("java:/comp/env/mydbcp");Connection con = ds.getConnection();System.out.println(con);con.close(); Context cxt = new InitialContext(); Context envCxt = (Context)cxt.lookup("java:/comp/env");DataSource ds = (DataSource)env.lookup("mydbcp");Connection con = ds.getConnection();System.out.println(con);con.close();上面兩種方式是相同的效果。
總結
- 上一篇: jdbc入门(一)
- 下一篇: Servlet优化之BaseServle