zoukankan      html  css  js  c++  java
  • 事务处理与使用连接池管理连接

    事务的概念和MySQL事务支持:

     事务是由一步或者多步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。

     事务具备4个特性:原子性(Atomicity),一致性(Consistency),隔离性(Isolation)和持续性(Durabilty)。这4个特性也简称为ACID性。

      原子性:事务是应用中最小的执行单位

      一致性:事务执行的结果,必须是数据库从一个一致性状态,变成另一个一致性状态。当数据库只包含事务成功提交的结果时,数据库处于一致性状态。如果系统运行中断,某个事务尚未完成而被中断,而该未完成的事务对数据库所做的修改已被写入数据库,此时,数据库就处于一种不正确的状态。比如说银行转账,A向B转账1000元,数据库从A账户减少了1000元,给B账户增加了1000元,如果全部执行成功,数据库处于一致性状态;如果仅仅执行完A账户金额的修改,而没有增加B账户的金额,则数据就处于不一致性状态:因此,一致性时通过原子性来保证的。

      隔离性:各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务都时隔离的。也就是说,并发执行的事务之间不能看到对象的中间状态,并发执行的事务之间不能互相影响。

      持续性:持续性也成为持久性,指事务一旦提交,对数据所做的任何改变都要记录到永久存储器中,通常就是保存进物理数据库。

     数据库的事务由下列语句组成:

      一组DML语句,经过这组DML语句修改后的数据将保持较号的一致性。

      一条DDL语句

      一条DCL语句

      DDL语句和DCL语句只有一条,因为DDL和DCL语句都会导致事务立即提交。

     当事务所包含的全部数据库操作都成功执行后,应该提交事务(commit),使这些事务修改永久生效。事务的提交一共有两种方式:显示提交与自动提交:

      显示提交:使用commit

      自动提交:执行DDL或者DCL语句,或者程序正常退出。

     当事务所包含的全部数据库操作执行失败后,应该回滚(rollback)事务,是该事务中所做的修改全部失效。事务回滚有两种方式:显示回滚与自动回滚:

      显示回滚:使用rollback

      自动回滚:数据库操作执行失败,系统错误或者强行退出

     MYSQL的事务:

      在默认的情况下,MYSQL是关闭事务的,即开启自动提交,在默认的情况下,当用户在MYSQL控制台输入一条DML语句时,这叫DML语句将会立即保存到数据库里(持续性)。如果要开启MYSQL的事务支持,可以显示调用如下命令:

    set autocommit=0 #开启事务
    set autocommit=1 #自动提交

      自动提交和开启事务恰好相反,如果开启自动提交,就是关闭事务;如果开启事务就是关闭自动提交

      一旦在MYSQL的命令行窗口中输入了set autocommit=0 开启了事务,该命令行窗口里的所有DML语句都不会立即生效,上一个事务结束后第一条DML语句将开始一个新的事务,而后续执行的所有SQL语句都在该事务中,除非显式使用commit来提交事务,或者正常退出,或者运行DDL,DCL语句导致事务隐式提交。当然也可以使用rollback来回滚结束事务,使用rollback结束事务将导致本次事务中的DML语句所做的修改全部失效。

      PS:这里要注意一点的是,在命令行窗口的开启事务是对该命令行窗口有效,其他窗口还是处于mysql的默认状态,即自动提交。

      除此之外,若不想关闭整个命令行窗口的自动提交(即关闭事务),而只是想临时性地开始事务,则可以使用MySQL提供的start transaction或begin两个命令,它们都表示临时性地开始一次事务,处于start transaction或者begin后的DML语句不会立即生效,除非使用commit显示提交事务,或者执行DDL,DCL语句的隐式提交事务才会立即生,如下面的SQL语句:

    #临时开始事务
    begin:
        #向student_table表中插入3条记录
        insert into tb_test values(null,'张三''1');
        insert into tb_test values(null,'李四''2');
        insert into tb_test values(null,'赵五''3');
        
        #查询表中的记录 (1select * from tb_test;
    
        #回滚事务
        rollback;
    
        #查询表中的记录  (2)
        select * from tb_test;

      执行上面SQL语句中的第一个查询语句将会看到刚刚插入的3条记录,其他窗口进行查询是看不到该3条记录的,因为该3条记录未提交,也就是还没有写入数据库中。当回滚事务后,执行上面SQL语句的第二个查询语句,该窗口也和其他窗口一样,也看不到那3条记录,因为回滚事务后,对事务中的所有DML语句全都失效,因此该窗口再也查询不出那三条记录。

      MYSQL还提供了savepoint来设置事务的中间点,通过使用设置savepoint设置事务的中间点可以让事务回滚到指定中间点,而不是回滚全部事务。如下SQL语句:

    savepoint a #设置中间点
    rollback to a; 使用rollback回滚到指定中间点

    JDBC的事务支持:

     JDBC连接也提供了事务支持,JDBC连接的事务支持由Connection提供,Connection默认是自动提交,即关闭事务(一次性只能执行一条DML语句),在这种情况下,每条SQL语句一旦执行,便会立即提交到数据库,永久生效,无法对其进行回滚操作。

       可以使用Connection 的setAutoCommit(false)方法来关闭自动提交,开启事务。

       可以使用Connection 的setAutoCommit(true)方法来关闭事务,开启自动提交,这也是Connection的默认状态。

     一旦开启了事务之后,程序可以向平常一样创建Statement对象,创建了Statement对象之后,可以执行任意多条DML语句,尽管程序都执行了多条DML语句,但这些DML语句所做的修改都不会生效,因为没有提交事务。因此当编写完DML语句后要加上Connection.commit()方法来提交事务,让DML语句生效。如多条DML语句中执行失败,则应该用Connection的rollback()方法来回滚事务。实际上,当Connection遇到一个未处理的SQLExcepition异常时,系统将会非正常退出,即事务隐式回滚;若程序捕获了改异常,则需要在异常处理块中显式地回滚事务。

     下面程序示范了JDBC的事务支持:

    public class JDBC {
        public void insertInTransaction(String[] sqls){
            try{
                conn.setAutoCommit(false);
                Statement statement = conn.createStatement();
                for(String sql : sqls){
                    statement.execute(sql);
                    System.out.println("SQL语句执行正常");
                }
                conn.commit(); //提交事务
            }catch(Exception e){
                if(conn != null){
                    try {
                        conn.rollback();
                        System.out.println("事务回滚");
                    } catch (SQLException e1) {e1.printStackTrace();}
                }
            }
        }
        
        Connection conn = null;
        Statement statement = null;
        public JDBC(String paramFile){
            try{
                Properties props = new Properties();
                props.load(new FileInputStream(paramFile));
                driver = props.getProperty("driver");
                url = props.getProperty("Url");
                user = props.getProperty("user");
                password = props.getProperty("password");
                conn =DriverManager.getConnection(url,user,password);
                statement =conn.createStatement();
                
            }catch(Exception e){}
        }
        
        public static void main(String[] args) throws SQLException {
            String[] sqls = new String[]{
                "insert into tb_test values(null,'AA',1)",
                "insert into tb_test values(null,'BB',2)",
                "insert into tb_test values(null,'CC',3)",
                "insert into tb_test values(null,'00','00')",  //错误的,因为第三个字段的数据类型为int,不能传入字符串进去
                "insert into tb_test values(null,'DD',4')",
            };
            jdbc_2.insertInTransaction(sqls);
        }
    }

     上述代码中,一共提交了5条DML语句,其中第四条是错误的,但第四条DML语句执行时,因为是错误的,会出现SQLException异常,程序将这个异常捕获,并执行了事务回滚,去查看对应数据库表,可以发现前三条DML语句在程序中尽管执行了,但还未写入到数据库中。保证了数据库的一致性。运行效果如下:

    SQL语句执行正常
    SQL语句执行正常
    SQL语句执行正常
    SQL语句执行正常
    事务回滚

    使用连接池管理连接:

     数据库连接的建立以及关闭都是极其耗费系统资源的操作,在多层结构的应用环境中,这种资源的耗费对系统性能影响尤其明显。当获得的数据库连接,一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完后立即关闭连接。频繁地打开,关闭连接造成系统性能低下。

     为了解决这个问题,Java提供了数据库连接池,当应用程序启动时,系统会主动建立足够的数据库连接,并将这些连接组成一个连接池。每次应用程序请求数据库连接时,无需重新打开连接,而是从连接池中取出已有的连接使用,当应用程序使用完毕后,该连接不用完毕,而是直接将连接归回连接池。通过使用连接池,将大大提高程序的运行效率。

     数据库连接池是Connection对象的工厂,数据库连接池的常用参数如下:

      1. 数据库的初始连接数

      2. 连接池的最大连接数。

      3. 连接池的最小连接数

      4. 连接池每次增加的容量

     JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由商用服务器(如WebLogic,WebSphere)等提供实现,也有一些开源组织提供实现(如DBCP和C3P0)。这里推荐大家都使用C3P0。

     DBCP数据源:

      DBCP数据源是由Apache软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:common-pool。如果要使用DBCP连接池,需要将如下的两个jar文件添加到系统或者开发软件中:

       commons-dbcp.jar:连接池的实现 

       commons-pool.jar:连接池实现的依赖库

      Tomcat的连接池正是采用该连接池实现的。数据库连接池既可以与应用服务器整合使用,也可以由应用程序独立使用。下面的代码示范了使用DBCP来获取数据库连接方式:

    BasicDataSource ds = new BasicDataSource(); //创建数据源对象
    ds.setDriverClassName("com.mysql.jdbc.Driver"); //设置连接池所需的驱动
    ds.setUrl("jdbc://mysql://localhost:8080/test"); //设置连接数据库的URL
    ds.setUsername("root"); //设置连接数据库的用户名
    ds.setPassword("root");  //设置连接数据库的密码
    ds.setInitialSize(5); //设置连接池的初始连接数
    ds.setMaxActive(20); //设置连接池最多可有20个活动连接数
    ds.setMinIdle(2); //设置连接池最少由2个空闲的连接

      数据源与数据库连接不同,数据源无须创建多个,它是产生数据库连接的工厂,因此整个应用只需有一个数据源便可,建议是将数据源设置成类成员变量,并且在应用开始时立即初始化数据源对象。

    Connection conn = ds.getConnection(); //从数据库连接池中获取连接
    conn.close() //关闭连接,并将该连接还给数据库连接池

     

     C3P0数据源:

      相比之下,C3P0数据源性能更胜一筹,Hibernate就推荐使用该连接池。C3P0连接池不仅可以自动清理不在使用的Connection,还可以自动清理Statement和ResultSet。若要使用C3P0,需要将c3p0-0.9.1.2.jar (有不同的版本)的jar文件添加到系统或者开发软件中。

      下面代码通过c3p0连接池获得数据库连接:

    ComboPooledDataSource ds = new ComboPooledDataSource(); //创建连接池实例
                ds.setDriverClass("com.mysql.jdbc.Driver"); //设置连接池连接数据库所需的驱动
                ds.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test"); //设置连接数据库的URL
                ds.setUser("root");  //设置连接数据库的用户名
                ds.setPassword("root"); //设置连接数据库的密码
                ds.setMaxPoolSize(40); //设置连接池的最大连接数
                ds.setMinPoolSize(10); //设置连接池的最小连接数
                ds.setInitialPoolSize(10); //设置连接池的初始连接数
                ds.setMaxStatements(180); //设置连接池的缓存Statement的最大数

     C3P0获取数据库的连接操作代码与DBCP源获取数据库的连接是一样的;关闭连接的操作也是一样的:

    Connection conn = ds.getConnection(); //从数据库连接池中获取连接
    conn.close() //关闭连接,并将该连接还给数据库连接池
  • 相关阅读:
    关系/比较运算符
    字符串连接符
    算数运算符
    基本数据类型之间的转换
    常用的进制
    数据类型的分类
    变量
    java目录结构
    C语言获取系统时间及time.h函数使用指南
    链表:单向链表的理解,创建及基本操作
  • 原文地址:https://www.cnblogs.com/hjlin/p/11441224.html
Copyright © 2011-2022 走看看