jdbc入门(二)
一、事務(wù)
? ? 1.事務(wù)概述
????????事務(wù)(Transaction),一般是指要做的或所做的事情。在計(jì)算機(jī)術(shù)語(yǔ)中是指訪(fǎng)問(wèn)并可能更新數(shù)據(jù)庫(kù)中各種數(shù)據(jù)項(xiàng)的一個(gè)程序執(zhí)行單元(unit)。事務(wù)通常由高級(jí)數(shù)據(jù)庫(kù)操縱語(yǔ)言或編程語(yǔ)言(如SQL,C++或Java)書(shū)寫(xiě)的用戶(hù)程序的執(zhí)行所引起,并用形如begin transaction和end transaction語(yǔ)句(或函數(shù)調(diào)用)來(lái)界定。事務(wù)由事務(wù)開(kāi)始(begin transaction)和事務(wù)結(jié)束(end transaction)之間執(zhí)行的全體操作組成。
????????銀行轉(zhuǎn)賬!張三轉(zhuǎn)10000塊到李四的賬戶(hù),這其實(shí)需要兩條SQL語(yǔ)句:
????????????給張三的賬戶(hù)減去10000元;
????????????給李四的賬戶(hù)加上10000元。
????????如果在第一條SQL語(yǔ)句執(zhí)行成功后,在執(zhí)行第二條SQL語(yǔ)句之前,程序被中斷了(可能是拋出了某個(gè)異常,也可能是其他什么原因),那么李四的賬戶(hù)沒(méi)有加上10000元,而張三卻減去了10000元。這肯定是不行的!
????????事務(wù)中的多個(gè)操作,要么完全成功,要么完全失敗!
? ? 2.事務(wù)的四大特性(ACID)
????????原子性(Atomicity):事務(wù)中所有操作是不可再分割的原子單位。事務(wù)中所有操作要么全部執(zhí)行成功,要么全部執(zhí)行失敗。
????????一致性(Consistency):事務(wù)執(zhí)行后,數(shù)據(jù)庫(kù)狀態(tài)與其它業(yè)務(wù)規(guī)則保持一致。如轉(zhuǎn)賬業(yè)務(wù),無(wú)論事務(wù)執(zhí)行成功與否,參與轉(zhuǎn)賬的兩個(gè)賬號(hào)余額之和應(yīng)該是不變的。
????????隔離性(Isolation):隔離性是指在并發(fā)操作中,不同事務(wù)之間應(yīng)該隔離開(kāi)來(lái),使每個(gè)并發(fā)中的事務(wù)不會(huì)相互干擾。
????????持久性(Durability):一旦事務(wù)提交成功,事務(wù)中所有的數(shù)據(jù)操作都必須被持久化到數(shù)據(jù)庫(kù)中,即使提交事務(wù)后,數(shù)據(jù)庫(kù)馬上崩潰,在數(shù)據(jù)庫(kù)重啟時(shí),也必須能保證通過(guò)某種機(jī)制恢復(fù)數(shù)據(jù)。
? ? 3.MySQL中的事務(wù)
????????在默認(rèn)情況下,MySQL每執(zhí)行一條SQL語(yǔ)句,都是一個(gè)單獨(dú)的事務(wù)。如果需要在一個(gè)事務(wù)中包含多條SQL語(yǔ)句,那么需要開(kāi)啟事務(wù)和結(jié)束事務(wù)。
????????????????開(kāi)啟事務(wù):start transaction;
????????????????結(jié)束事務(wù):commit或rollback。
????????在執(zhí)行SQL語(yǔ)句之前,先執(zhí)行strat transaction,這就開(kāi)啟了一個(gè)事務(wù)(事務(wù)的起點(diǎn)),然后可以去執(zhí)行多條SQL語(yǔ)句,最后要結(jié)束事務(wù),commit表示提交,即事務(wù)中的多條SQL語(yǔ)句所做出的影響會(huì)持久化到數(shù)據(jù)庫(kù)中。或者rollback,表示回滾,即回滾到事務(wù)的起點(diǎn),之前做的所有操作都被撤消了!
? ? 4.JDBC事務(wù)
????????同一事務(wù)中所有的操作,都在使用同一個(gè)Connection對(duì)象!
????????Connection的三個(gè)方法與事務(wù)相關(guān):
????????setAutoCommit(boolean):設(shè)置是否為自動(dòng)提交事務(wù),如果true(默認(rèn)值就是true)表示自動(dòng)提交,也就是每條執(zhí)行的SQL語(yǔ)句都是一個(gè)單獨(dú)的事務(wù),如果設(shè)置false,那么就相當(dāng)于開(kāi)啟了事務(wù)了;con.setAutoCommit(false)表示開(kāi)啟事務(wù)!!!
????????commit():提交結(jié)束事務(wù);con.commit();表示提交事務(wù)
????????rollback():回滾結(jié)束事務(wù)。con.rollback();表示回滾事務(wù)
?
????????jdbc處理事務(wù)的代碼格式:
????????????????try {
????????????????? con.setAutoCommit(false);//開(kāi)啟事務(wù)…
????????????????? ….
????????????????? …
????????????????? con.commit();//try的最后提交事務(wù)
????????????????} catch() {
????????????????? con.rollback();//回滾事務(wù)
????????????????}
代碼演示:
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類(lèi)被加載時(shí)執(zhí)行一次!static {// 給props進(jìn)行初始化,即加載dbconfig.properties文件到props對(duì)象中try {InputStream in = jdbcUtils.class.getClassLoader().getResourceAsStream("dbconfig.properties");props = new Properties();props.load(in);} catch (IOException e) {throw new RuntimeException(e);}// 加載驅(qū)動(dòng)類(lèi)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類(lèi)
@Testpublic void fun1() {zhuanZhang("zs", "ls", 100);}? ? 5.事務(wù)隔離級(jí)別
? ? (1)事務(wù)的并發(fā)讀問(wèn)題
????????臟讀:讀取到另一個(gè)事務(wù)未提交數(shù)據(jù);
????????不可重復(fù)讀:兩次讀取不一致;
????????幻讀(虛讀):讀到另一事務(wù)已提交的數(shù)據(jù)。
????????不可重復(fù)讀和幻讀的區(qū)別:
????????????????不可重復(fù)讀是讀取到了另一事務(wù)的更新;
????????????????幻讀是讀取到了另一事務(wù)的插入(MySQL中無(wú)法測(cè)試到幻讀);
? ? (2)四大隔離級(jí)別
????????4個(gè)等級(jí)的事務(wù)隔離級(jí)別,在相同數(shù)據(jù)環(huán)境下,使用相同的輸入,執(zhí)行相同的工作,根據(jù)不同的隔離級(jí)別,可以導(dǎo)致不同的結(jié)果。不同事務(wù)隔離級(jí)別能夠解決的數(shù)據(jù)并發(fā)問(wèn)題的能力是不同的。
????? ? 1.SERIALIZABLE(串行化)
????????????????不會(huì)出現(xiàn)任何并發(fā)問(wèn)題,因?yàn)樗菍?duì)同一數(shù)據(jù)的訪(fǎng)問(wèn)是串行的,非并發(fā)訪(fǎng)問(wèn)的;
????????????????性能最差;
????????2.REPEATABLE READ(可重復(fù)讀)(MySQL)
????????????????防止臟讀和不可重復(fù)讀,不能處理幻讀問(wèn)題
????????????????性能比SERIALIZABLE好
????????3.READ COMMITTED(讀已提交數(shù)據(jù))(Oracle)
????????????????防止臟讀,沒(méi)有處理不可重復(fù)讀,也沒(méi)有處理幻讀;
????????????????性能比REPEATABLE READ好
????????4.READ UNCOMMITTED(讀未提交數(shù)據(jù))
????????????????可能出現(xiàn)任何事務(wù)并發(fā)問(wèn)題
????????????????性能最好
? ? (3)MySQL隔離級(jí)別
? ? ????MySQL的默認(rèn)隔離級(jí)別為REPEATABLE READ,可以通過(guò)以下語(yǔ)句查看:
select @@tx_isolation????????也可以通過(guò)下面語(yǔ)句來(lái)設(shè)置當(dāng)前連接的隔離級(jí)別:
set transaction isolationlevel [4選1]? ? (4)JDBC設(shè)置隔離級(jí)別
????????con. setTransactionIsolation(int level)
????????????參數(shù)可選值如下:
????????????????Connection.TRANSACTION_READ_UNCOMMITTED;
????????????????Connection.TRANSACTION_READ_COMMITTED;
????????????????Connection.TRANSACTION_REPEATABLE_READ;
????????????????Connection.TRANSACTION_SERIALIZABLE。
二、數(shù)據(jù)庫(kù)連接池
? ? 1.數(shù)據(jù)庫(kù)連接池的概念
????????用池來(lái)管理Connection,這可以重復(fù)使用Connection。有了池,所以我們就不用自己來(lái)創(chuàng)建Connection,而是通過(guò)池來(lái)獲取Connection對(duì)象。當(dāng)使用完Connection后,調(diào)用Connection的close()方法也不會(huì)真的關(guān)閉Connection,而是把Connection“歸還”給池。池就可以再利用這個(gè)Connection對(duì)象了。
? ??
? ? 2.JDBC數(shù)據(jù)連接池接口(DataSource)
????????Java為數(shù)據(jù)庫(kù)連接池提供了公共的接口:javax.sql.DataSource,各個(gè)廠(chǎng)商可以讓自己的連接池實(shí)現(xiàn)這個(gè)接口。這樣應(yīng)用程序可以方便的切換不同廠(chǎng)商的連接池!
? ? 3.DBCP連接池
????? ? (1)什么是DBCP連接池?
????????????????DBCP是Apache提供的一款開(kāi)源免費(fèi)的數(shù)據(jù)庫(kù)連接池!
????? ? (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);//最大連接數(shù)ds.setMaxIdle(10);//最大空閑連接數(shù)ds.setInitialSize(10);//初始化連接數(shù)ds.setMinIdle(2);//最小空閑連接數(shù)ds.setMaxWait(1000);//最大等待毫秒數(shù)Connection con = ds.getConnection();System.out.println(con.getClass().getName());con.close();//關(guān)閉連接只是把連接歸還給池!????? ? (3)DBCP的配置信息
#基本配置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mydb1 username=root password=123#初始化池大小,即一開(kāi)始池中就會(huì)有10個(gè)連接對(duì)象 默認(rèn)值為0 initialSize=0#最大連接數(shù),如果設(shè)置maxActive=50時(shí),池中最多可以有50個(gè)連接,當(dāng)然這50個(gè)連接中包含被使用的和沒(méi)被使用的(空閑) #你是一個(gè)包工頭,你一共有50個(gè)工人,但這50個(gè)工人有的當(dāng)前正在工作,有的正在空閑 #默認(rèn)值為8,如果設(shè)置為非正數(shù),表示沒(méi)有限制!即無(wú)限大 maxActive=8#最大空閑連接 #當(dāng)設(shè)置maxIdle=30時(shí),你是包工頭,你允許最多有20個(gè)工人空閑,如果現(xiàn)在有30個(gè)空閑工人,那么要開(kāi)除10個(gè) #默認(rèn)值為8,如果設(shè)置為負(fù)數(shù),表示沒(méi)有限制!即無(wú)限大 maxIdle=8#最小空閑連接 #如果設(shè)置minIdel=5時(shí),如果你的工人只有3個(gè)空閑,那么你需要再去招2個(gè)回來(lái),保證有5個(gè)空閑工人 #默認(rèn)值為0 minIdle=0#最大等待時(shí)間 #當(dāng)設(shè)置maxWait=5000時(shí),現(xiàn)在你的工作都出去工作了,又來(lái)了一個(gè)工作,需要一個(gè)工人。 #這時(shí)就要等待有工人回來(lái),如果等待5000毫秒還沒(méi)回來(lái),那就拋出異常 #沒(méi)有工人的原因:最多工人數(shù)為50,已經(jīng)有50個(gè)工人了,不能再招了,但50人都出去工作了。 #默認(rèn)值為-1,表示無(wú)限期等待,不會(huì)拋出異常。 maxWait=-1#連接屬性 #就是原來(lái)放在url后面的參數(shù),可以使用connectionProperties來(lái)指定 #如果已經(jīng)在url后面指定了,那么就不用在這里指定了。 #useServerPrepStmts=true,MySQL開(kāi)啟預(yù)編譯功能 #cachePrepStmts=true,MySQL開(kāi)啟緩存PreparedStatement功能, #prepStmtCacheSize=50,緩存PreparedStatement的上限 #prepStmtCacheSqlLimit=300,當(dāng)SQL模板長(zhǎng)度大于300時(shí),就不再緩存它 connectionProperties=useUnicode=true;characterEncoding=UTF8;useServerPrepStmts=true;cachePrepStmts=true;prepStmtCacheSize=50;prepStmtCacheSqlLimit=300#連接的默認(rèn)提交方式 #默認(rèn)值為true defaultAutoCommit=true#連接是否為只讀連接 #Connection有一對(duì)方法:setReadOnly(boolean)和isReadOnly() #如果是只讀連接,那么你只能用這個(gè)連接來(lái)做查詢(xún) #指定連接為只讀是為了優(yōu)化!這個(gè)優(yōu)化與并發(fā)事務(wù)相關(guān)! #如果兩個(gè)并發(fā)事務(wù),對(duì)同一行記錄做增、刪、改操作,是不是一定要隔離它們啊? #如果兩個(gè)并發(fā)事務(wù),對(duì)同一行記錄只做查詢(xún)操作,那么是不是就不用隔離它們了? #如果沒(méi)有指定這個(gè)屬性值,那么是否為只讀連接,這就由驅(qū)動(dòng)自己來(lái)決定了。即Connection的實(shí)現(xiàn)類(lèi)自己來(lái)決定! defaultReadOnly=false#指定事務(wù)的事務(wù)隔離級(jí)別 #可選值:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE #如果沒(méi)有指定,那么由驅(qū)動(dòng)中的Connection實(shí)現(xiàn)類(lèi)自己來(lái)決定 defaultTransactionIsolation=REPEATABLE_READ? ? 4.C3P0連接池
????? ? (1)什么是C3P0連接池?
????????C3P0是一個(gè)開(kāi)源的JDBC連接池,它實(shí)現(xiàn)了數(shù)據(jù)源和JNDI綁定,支持JDBC3規(guī)范和JDBC2的標(biāo)準(zhǔn)擴(kuò)展。目前使用它的開(kāi)源項(xiàng)目有Hibernate,Spring等。
????? ? (2)C3P0的使用
????????????????C3P0中池類(lèi)是: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);//初始化連接數(shù)ds.setMinPoolSize(2);//最少連接數(shù)ds.setMaxPoolSize(50);//最多連接數(shù)Connection con = ds.getConnection();System.out.println(con);con.close();}配置文件要求:
?
????????文件名稱(chēng):必須叫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的配置文件中可以配置多個(gè)連接信息,可以給每個(gè)配置起個(gè)名字,這樣可以方便的通過(guò)配置名稱(chēng)來(lái)切換配置信息。上面文件中默認(rèn)配置為mysql的配置,名為oracle-config的配置也是mysql的配置。
使用默認(rèn)的配置信息
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的作用就是:在服務(wù)器上配置資源,然后通過(guò)統(tǒng)一的方式來(lái)獲取配置的資源。
????????我們這里要配置的資源當(dāng)然是連接池了,這樣項(xiàng)目中就可以通過(guò)統(tǒng)一的方式來(lái)獲取連接池對(duì)象了。
????????下圖是Tomcat文檔提供的:
?
?
配置JNDI資源需要到<Context>元素中配置<Resource>子元素:
????????name:指定資源的名稱(chēng),這個(gè)名稱(chēng)可以隨便給,在獲取資源時(shí)需要這個(gè)名稱(chēng);
????????factory:用來(lái)創(chuàng)建資源的工廠(chǎng),這個(gè)值基本上是固定的,不用修改;
????????type:資源的類(lèi)型,我們要給出的類(lèi)型當(dāng)然是我們連接池的類(lèi)型了
????????bar:表示資源的屬性,如果資源存在名為bar的屬性,那么就配置bar的值。對(duì)于DBCP連接池而言,你需要配置的不是bar,因?yàn)樗鼪](méi)有bar這個(gè)屬性,而是應(yīng)該去配置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.獲取資源
?
????????配置資源的目的當(dāng)然是為了獲取資源了。只要你啟動(dòng)了Tomcat,那么就可以在項(xiàng)目中任何類(lèi)中通過(guò)JNDI獲取資源的方式來(lái)獲取資源了。
????????下圖是Tomcat文檔提供的,與上面Tomcat文檔提供的配置資源是對(duì)應(yīng)的。
?
????????獲取資源:
?
????????????????Context:javax.naming.Context;
????????????????InitialContext:javax.naming.InitialContext;
????????????????lookup(String):獲取資源的方法,其中”java:comp/env”是資源的入口(這是固定的名稱(chēng)),獲取過(guò)來(lái)的還是一個(gè)Context,這說(shuō)明需要在獲取到的Context上進(jìn)一步進(jìn)行獲取?!眀ean/MyBeanFactory”對(duì)應(yīng)<Resource>中配置的name值,這回獲取的就是資源對(duì)象了。
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();上面兩種方式是相同的效果。
總結(jié)
- 上一篇: jdbc入门(一)
- 下一篇: Servlet优化之BaseServle