javaweb-事務和連接池

一、事務

什麼是事務?

事務,一般是指要做的或所做的事情。在計算機術語中是指訪問並可能更新數據庫中各種數據項的一個程序執行單元。這些單元要麼全都成功,要麼全都不成功。

做一件事情,這個一件事情中有多個組成單元,這個多個組成單元要不同時成功,要不同時失敗。A賬戶轉給B賬戶錢,將A賬戶轉出錢的操作與B賬戶轉入錢的操作綁定到一個事務中,要不這兩個動作同時成功,代表這次轉賬成功,要不就兩個動作同時失敗,代表這次轉賬失敗。

事務在開發中的作用

下面來舉例說明什麼是事務,如下所示:

現實生活中的銀行轉賬業務,張三要給李四轉賬1000元,而在程序員眼中兩條SQL語句就可以搞定,如下:

l 給張三的賬戶減去1000元;

l 給李四的賬戶加上1000元;

如果在轉賬的業務中,成功的將張三的賬戶減去1000元,而在給李四的賬戶加1000元的時候,程序出現了問題,李四的賬戶沒有加上1000元,而張三的賬戶卻減掉了1000元,在現實生活中,這種情況肯定是不允許存在的。當我們將這個轉賬業務放在一個事務中,就不會出現以上情況了。

事務中有多個操作,這些操作要麼全部成功,要麼全部失敗,也就是說給張三的賬戶減去1000元如果成功了,那麼給李四的賬戶加上1000元的操作也必須是成功的,否則給張三減去1000元,以及給李四加上1000元都必須是失敗的

mysql的事務控制

MySQL下如何開啓事務
方式一:
      start  transaction   開啓事務
      rollback    事務回滾(將數據恢復到事務開始時的狀態)
      commit    事務提交(對事務中進行操作,進行確認操作,事務在提交後,數據就不可再進行恢復)
方式二:
     show variables like '%commit%';   可以查看當前autocommit 值
     在MySQL數據庫中它的默認值是 "on" 代表自動事務。
     自動事務的意義就是:執行任意一條SQL語句都會自動提交事務。
     測試:將autocommit的值設置成off
           1. set autocommit=off  
           2. 必須手動commit纔可以將事務提交
           注意:MySQL 默認autocommit=on   oracle默認的autocommit=off
                     如果設置autocommit 爲 off,意味着以後每條SQL 都會處於一個事務中,相當於每條SQL執行前 都執行                      start transaction

mysql默認事務是自動提交的,一條sql是一個事務

當手動開啓事務後,數據庫默認的事務的自動提交暫時失效手動開啓事務:start transaction

提交事務:commit

提交事務到開啓事務之間的所有的sql語句都生效

回滾事務:rollback

從回滾事務到開啓事務之間的所有的sql操作都無效

jdbc的API的事務控制

通過Connection對象可以控制事務

jdbc中想控制事務其實是控制jdbc的更新數據庫的API方法---executeUpdate

開啓事務:connection.setAutoCommit(false);

提交事務:connection.commit();

回滾事務:connection.rollback();

官方的介紹事務特性

事務的四大特性簡稱ACID(Atomicity Consistency Isolation Durability),分別是:

l 原子性:原子性對應的英文是Atomicity,即表示事務中所有操作是不可再分割的原子單位。事務中所有操作要麼全部執行成功,要麼全部執行失敗;

l 一致性:一致性對應的英文是Consistency,事務執行後,數據庫狀態與其它業務規則保持一致。例如轉賬業務,無論事務執行成功與否,參與轉賬的兩個賬號餘額之和應該是不變的;

l 隔離性:隔離性對應的英文是Isolation,是指在併發操作中,不同事務之間應該隔離開來,使每個併發中的事務不會相互干擾;

l 持久性:持久性對應的英文是Durability,指的是一旦事務提交成功,事務中所有的數據操作都必須被持久化到數據庫中,即使提交事務後,數據庫馬上崩潰,在數據庫重啓時,也必須能保證通過某種機制恢復數據。

不同的事務,其一致性的表現形式是不同的,事務的其他三大特性其實都是爲了事務的一致性服務的。

事務的四大特性概括

原子性:數據庫的操作的最小的單位就是事務

一致性:一個事務中的多個操作的結果數據是一致的,同時成功和同時失敗

隔離性:多個事務之間的操作互不影響

持久性:當一個事務提交後,更新操作才持久化到磁盤上

事務的隔離級別與問題

不考慮隔離性產生的問題介紹

n 髒讀 一個事務讀取到了另一個事務未提交數據.

n 不可重複讀 一個事務內,兩次讀取到的數據不一致.(update)

n 虛讀(幻讀兩次讀取的數據不一致(insert)

事務的四種隔離級別介紹

數據庫內部定義了四種隔離級別,用於解決三種隔離問題

u 1 Serializable:可避免髒讀、不可重複讀、虛讀情況的發生。(串行化)

u 2 Repeatable read:可避免髒讀、不可重複讀情況的發生。(可重複讀)不可以避免虛讀

u 3 Read committed:可避免髒讀情況發生(讀已提交)

u 4 Read uncommitted:最低級別,以上情況均無法保證。(讀未提交)

mysql數據庫默認的事務隔離級別-----repeatable read級別.

oracle數據默認的事務隔離級別 ----read committed

設置事務隔離級別

n mysql中設置

數據庫默認有事務的隔離級別,mysql 中查看與修改事務的隔離級別

u set session transaction isolation level 設置事務隔離級別

u select @@tx_isolation 查詢當前事務隔離級別

n jdbc中設置事務隔離級別

java.sql.Connection接口中提供

u setTransactionIsolation(int level) ;

參數可以取 Connection 常量之一:

Connection.TRANSACTION_READ_UNCOMMITTED

Connection.TRANSACTION_READ_COMMITTED

Connection.TRANSACTION_REPEATABLE_READ

Connection.TRANSACTION_SERIALIZABLE

(注意,不能使用 Connection.TRANSACTION_NONE,因爲它指定了不受支持的事務。)

 

事務的丟失更新問題(Lost Update)

兩個或多個事務更新同一行,但這些事務彼此之間都不知道其他事務進行的修改,因此第二個更改覆蓋了第一個修改。
如何解決事務的丟失更新問題?
解決事務的丟失更新可以採用兩種方式

方式一:悲觀鎖

       悲觀鎖(假設丟失更新一定會發生)——利用數據庫內部鎖機制,管理事務提供的鎖機制
      (1)共享鎖
                select  * from table lock  in  share mode  (讀鎖、共享鎖)
      (2)排它鎖
                select  * from table for update  (寫鎖、排它鎖)
 悲觀鎖詳解:  
       MySQL鎖機制分爲表級鎖(例如  事務隔離級別中的Serializable)和行級鎖。
共享鎖又稱爲讀鎖,簡稱S鎖,顧名思義,共享鎖就是多個事務對於同一數據可以共享一把鎖,都能訪問到數據,但是隻能讀不能修改。
      排它鎖又稱爲寫鎖,簡稱X鎖,顧名思義,排他鎖就是不能與其他鎖並存,如一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排他鎖,但是獲取排他鎖的事務是可以對數據就行讀取和修改。
  其實共享鎖就是多個事務只能讀數據不能改數據。對於排它鎖而言,當排它鎖鎖住一行數據後 並不是說其他事務不能讀取和修改這行數據,排它鎖指的是一個事務在一行數據上加上排它鎖後,其他事務不能在其上面加任何其他的鎖。MySQL引擎默認的修改數據語句(update、insert、delete)都會自動給涉及到的數據加上排它鎖,select 語句默認不會加任何鎖類型,如果加排它鎖可以使用select ... for update語句,加共享鎖可以使用select ... lock in share mode語句。所以加過排它鎖的數據行在其他事務中是不能修改數據的,也不能通過 for  update 和lock in share mode鎖的方式查詢數據,但是可以直接通過select ...  from ... 查詢數據,因爲普通查詢默認沒有任何鎖機制。

注意:事務控制必須在service層

連接池

  • 連接池:就是創建一個容器,用於裝入多個Connection對象,在使用連接對象時,從容器中獲取一個Connection, 使用完成後,在將這個Connection重新裝入到容器中。這個容器就是連接池。(DataSource)也叫做數據源.
  • 作用:我們可以通過連接池獲取連接對象。

  • 優點:

    • 節省創建連接與釋放連接造成的性能消耗 —- 連接池中連接起到複用的作用 ,提高程序性能
  • 應用程序直接獲取鏈接 

  • 應用程序直接獲取鏈接的缺點

    • 用戶每次請求都需要向數據庫獲得鏈接,而數據庫創建連接通常需要消耗相對較大的資源,創建時間也較長。假設網站一天10萬訪問量,數據庫服務器就需要創建10萬次連接,極大的浪費數據庫的資源,並且極易造成數據庫服務器內存溢出、拓機。

數據庫連接池編寫原理分析

  • 編寫連接池需實現javax.sql.DataSource接口。DataSource接口中定義了兩個重載的getConnection方法:

    • Connection getConnection()
    • Connection getConnection(String username, String password)
  • 實現DataSource接口,並實現連接池功能的步驟:

    • 在DataSource構造函數中批量創建與數據庫的連接,並把創建的連接保存到一個集合對象中
    • 實現getConnection方法,讓getConnection方法每次調用時,從集合對象中取一個Connection返回給用戶。
    • 當用戶使用完Connection,調用Connection.close()方法時,Collection對象應保證將自己返回到連接池的集合對象中,而不要把conn還給數據庫。
  • 原來由jdbcUtil創建連接,現在由dataSource創建連接,爲實現不和具體數據爲綁定,因此datasource也應採用配置文件的方法獲得連接。



自定義連接池

  • 1.創建一個MyDataSource類,在這個類中創建一個LinkedList<Connection>
  • 2.在其構造方法中初始化List集合,並向其中裝入5個Connection對象。
  • 3.創建一個public Connection getConnection();從List集合中獲取一個連接對象返回.
  • 4.創建一個 public void readd(Connection) 這個方法是將使用完成後的Connection對象重新裝入到List集合中.

  • 代碼問題:

  • 1.連接池的創建是有標準的.

    • javax.sql包下定義了一個接口 DataSource
    • 所有的連接池必須實現javax.sql.DataSource接口
  • 2.我們操作時,要使用標準,怎樣可以讓 con.close()它不是銷燬,而是將其重新裝入到連接池.

    • 要解決這個問題,其本質就是將Connection中的close()方法的行爲改變。
    • 怎樣可以改變一個方法的行爲(對方法功能進行增強) 
      • 1.繼承
      • 2.裝飾模式 
        • 1.裝飾類與被裝飾類要實現同一個接口或繼承同一個父類
        • 2.在裝飾類中持有一個被裝飾類引用
        • 3.對方法進行功能增強。
      • 3.動態代理 
        • 可以對行爲增強
        • Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);
    • 結論:Connection對象如果是從連接池中獲取到的,那麼它的close方法的行爲已經改變了,不在是銷燬,而是重新裝入到連接池。

    • 1.連接池必須實現javax.sql.DataSource接口。

    • 2.要通過連接池獲取連接對象 DataSource接口中有一個 getConnection方法.
    • 3.將Connection重新裝入到連接池 使用Connection的close()方法。

開源連接池

  • 現在很多WEB服務器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的實現,即連接池的實現。通常我們把DataSource的實現,按其英文含義稱之爲數據源,數據源中都包含了數據庫連接池的實現。
  • 也有一些開源組織提供了數據源的獨立實現: 
    • DBCP 數據庫連接池
    • C3P0 數據庫連接池
    • Apache Tomcat內置的連接池(apache dbcp)
  • 實際應用時不需要編寫連接數據庫代碼,直接從數據源獲得數據庫的連接。程序員編程時也應儘量使用這些數據源的實現,以提升程序的數據庫訪問性能。

DBCP數據源(瞭解)

  • DBCP 是 Apache 軟件基金組織下的開源連接池實現
  • 使用DBCP數據源,應用程序應在系統中增加如下兩個 jar 文件: 
    • Commons-dbcp-1.4.jar:連接池的實現
    • Commons-pool-1.5.6.jar:連接池實現的依賴庫
  • Tomcat 的連接池正是採用該連接池來實現的。該數據庫連接池既可以與應用服務器整合使用,也可由應用程序獨立使用。

C3P0數據源(必會)

  • C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規範和JDBC2的標準擴展。
  • 目前使用它的開源項目有Hibernate,Spring等。

  • c3p0與dbcp區別

    • dbcp沒有自動回收空閒連接的功能
    • c3p0有自動回收空閒連接功能
  • 1.導包

    • c3p0-0.9.1.2.jar
  • 1.手動使用

ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql:///day18");
cpds.setUser("root");
cpds.setPassword("abc");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 2.自動(使用配置文件) 
    • c3p0的配置文件可以是properties也可以是xml。
    • c3p0的配置文件如果名稱叫做 c3p0.properties or c3p0-config.xml 並且放置在classpath路徑下(對於web應用就是classes目錄)那麼c3p0會自動查找。
    • 注意:我們其時只需要將配置文件放置在src下就可以。
    • 使用: 
      • ComboPooledDataSource cpds = new ComboPooledDataSource();
      • 它會在指定的目錄下查找指定名稱的配置文件,並將其中內容加載。
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///day18</property>
        <property name="user">root</property>
        <property name="password">abc</property>
    </default-config>

</c3p0-config>


    @Test
    public void test2() throws PropertyVetoException, SQLException {
        ComboPooledDataSource cpds = new ComboPooledDataSource();

        // 得到一個Connection
        Connection con = cpds.getConnection();

        ResultSet rs = con.createStatement().executeQuery(
                "select * from account");

        while (rs.next()) {

            System.out.println(rs.getInt("id") + "  " + rs.getString("name"));
        }

        rs.close();
        con.close(); // 將Connection對象重新裝入到連接池.

        // String path = this.getClass().getResource("/").getPath();
        // System.out.println(path);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33


配置Tomcat數據源

  • tomcat服務器內置連接池 (使用Apache DBCP)
  • 配置tomcat 內置連接池,通過JNDI方式 去訪問tomcat的內置連接池
JNDI技術簡介
  • JNDI(Java Naming and Directory Interface),Java**命名和目錄接口,它對應於**J2SE中的javax.naming包。是JavaEE一項技術,允許將一個Java對象綁定到一個JNDI容器(tomcat)中,並且爲對象指定一個名稱,通過javax.naming 包 Context 對JNDI 容器中綁定的對象進行查找,通過指定名稱找到綁定Java對象。

  • 這套API的主要作用在於:它可以把Java對象放在一個容器中(支持JNDI容器 Tomcat),併爲容器中的java對象取一個名稱,以後程序想獲得Java對象,只需通過名稱檢索即可。

  • 其核心API爲Context,它代表JNDI容器,其lookup方法爲檢索容器中對應名稱的對象。
配置操作步驟
  • 1、配置使用tomcat 內置連接池 配置<context> 元素
  • context元素有三種常見配置位置 
    • 1) tomcat/conf/context.xml 所有虛擬主機,所有工程都可以訪問該連接池
    • 2) tomcat/conf/Catalina/localhost/context.xml 當前虛擬主機(localhost)下所有工程都可以使用該連接池
    • 3) 當前工程/META-INF/context.xml 只有當前工程可以訪問該連接池
<Context>
  <Resource name="jdbc/EmployeeDB" auth="Container"
            type="javax.sql.DataSource" username="root" password="abc"
            driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day14"
            maxActive="8" maxIdle="4"/>
</Context>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 必須先將mysql驅動jar包 複製tomcat/lib下
  • 在tomcat啓動服務器時,創建連接池對象,綁定 jdbc/EmployeeDB 指定名稱上

  • 2、通過運行在JNDI容器內部的程序(Servlet/JSP)去訪問tomcat內置連接池

public class DataSourceServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        try {
            Context context = new InitialContext();
            Context envCtx = (Context) context.lookup("java:comp/env"); // 固定路徑
            DataSource datasource = (DataSource) envCtx
                    .lookup("jdbc/EmployeeDB");//自己定義的數據庫名字

            Connection con = datasource.getConnection();
            ResultSet rs = con.createStatement().executeQuery(
                    "select * from account");

            while (rs.next()) {
                System.out.println(rs.getInt("id") + "  "
                        + rs.getString("name"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章