zoukankan      html  css  js  c++  java
  • 事务

    事务概述:

      事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全部失败

      事务作用:保证在一个事务中多次SQL操作要么全部成功,要么全部失败.

    Mysql操作事务

      典型的编程式事务:

    # 创建一个表:账户表.
    create database webdb;
    # 使用数据库
    use webdb;
    # 创建账号表
    create table account(
    id int primary key auto_increment,
    name varchar(20),
    money double
    );
    # 初始化数据
    insert into account values (null,'jack',10000);
    insert into account values (null,'rose',10000);
    insert into account values (null,'tom',10000);

    操作

        MYSQL中可以有两种方式进行事务的管理: 

        自动提交:MySql默认自动提交。及执行一条sql语句提交一次事务。
        手动提交:先开启,再提交

    手动提交:

    start transaction;
    update account set money=money-1000 where name='jack';
    update account set money=money+1000 where name='rose';
    commit;
    #或者
    rollback;

    方式2:自动提交,通过修改mysql全局变量“autocommit”进行控制

    show variables like '%commit%';
    * 设置自动提交的参数为OFF:
    set autocommit = 0; -- 0:OFF 1:ON

     jdbc事务操作

        public void demo01() throws SQLException{
        // 获得连接
            Connection conn = null;
            try {
            //#1 开始事务
                conn.setAutoCommit(false);
            //.... 加钱 ,减钱
             //#2 提交事务
                conn.commit();
            } catch (Exception e) {
            //#3 回滚事务
                conn.rollback();
                Connection对象的方法名 描述
                conn.setAutoCommit(false) 开启事务
                new QueryRunner() 创建核心类,不设置数据源(手动管理连接)
                query(conn , sql , handler, params ) 或
                update(conn, sql , params)
                手动传递连接, 执行SQL语句CRUD
                DbUtils.commitAndCloseQuietly(conn) 提交并关闭连接,不抛异常
                DbUtils.rollbackAndCloseQuietly(conn) 回滚并关闭连接,不抛异常
                1.3 DBUtils事务操作
                        代码演示
                1.4 案例:JDBC事务分层(dao、service)传递Connection
                        分析
            } finally{
            // 释放资源
                conn.close();
            }
        }

    DBUtils事务操作

     public void demo02() throws SQLException{
        // 获得连接
            Connection conn = null;
            try {
            //#1 开始事务
                conn.setAutoCommit(false);
            //.... 加钱 ,减钱
             //#2 提交事务
                DbUtils.ommitAndCloseQuietly(conn);
            } catch (Exception e) {
            //#3 回滚事务
                DbUtils.rollbackAndCloseQuietly(conn);
                e.printStackTrace();
            }
        }

    JDBC事务分层(dao、service)传递Connection

    开发中,常使用分层思想
      不同的层次结构分配不同的解决过程,各个层次间组成严密的封闭系统
      不同层级结构彼此平等
    分层的目的是:
      解耦
      可维护性
      可扩展性
      可重用性
    不同层次,使用不同的包表示
      com.qingmu 公司域名倒写
      com.qingmu.dao dao层
      com.qingmu.service service层
      com.qingmu.domain javabean
      com.qingmu.utils 工具

    工具类C3P0Utils

        public class C3P0Utils {
            //创建一个C3P0的连接池对象(使用c3p0-config.xml中default-config标签中对应的参数)
            public static DataSource ds = new ComboPooledDataSource();
            //从池中获得一个连接
            public static Connection getConnection() throws SQLException {
                return ds.getConnection();
            }
        }

    c3p0-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
    <!-- 使用默认的配置读取连接池对象 -->
    <default-config>
        步骤1:编写入口程序
        service层
        <!-- 连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/webdb</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <!-- 连接池参数 -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">10</property>
        <property name="checkoutTimeout">2000</property>
        <property name="maxIdleTime">1000</property>
    </default-config>
    </c3p0-config>

    步骤1:编写入口程序

        public class App {
            public static void main(String[] args) {
                try {
                    String outUser = "jack";
                    String inUser = "rose";
                    Integer money = 100;
                    //2 转账
                    AccountService accountService = new AccountService();
                    accountService.transfer(outUser, inUser, money);
                    //3 提示
                    System.out.println("转账成功");
                } catch (Exception e) {
                  //3 提示
                    System.out.println("转账失败");
                    e.printStackTrace();
                }
            }
        }

    service层

     public class AccountService {
            /**
             * 事务管理方式:向下传递Connection。有侵入性。使用DBUtils
             * 业务层事务管理转账的方法
             * @param from
             * @param to
             * @param money
             */
            public void transfer(String from, String to, double money) {
                //调用dao层
                AccountDao accountDao = new AccountDao();
            //DBUtils进行事务处理的原理,是在Service层获得连接,以保证事务处理过程中的Connection对象为同一个Connection。
            //因为必须保证连接为同一个连接,所以在业务层获得连接,再将连接传递到持久层,代码具有侵入性。
            //DBUtils使用的方法
                Connection conn = null;
                try {
                //获得连接
                    conn = C3P0Utils.getConnection();
                //设置事务不自动提交
                    conn.setAutoCommit(false);
                //调用持久层
                    accountDao.outMoney(conn,from,money);
                //如果有异常
                //int a = 1 / 0 ;
                    accountDao.inMoney(conn,to,money);
                //提交事务,并安静的关闭连接
                    DbUtils.commitAndCloseQuietly(conn);
                } catch (SQLException e) {
                 //有异常出现时,回滚事务,并安静的关闭连接
                    DbUtils.rollbackAndCloseQuietly(conn);
                    e.printStackTrace();
                }
            }
        }

    dao层

     public class AccountDao {
            /**
             * 付款方法
             * @param conn 连接对象
             * @param from 付款人
             * @param money 金额
             */
            public void outMoney(Connection conn, String from, double money) {
                QueryRunner qr = new QueryRunner();
                try {
                    String sql = "update account set money = money - ? where name = ?";
                    qr.update(conn, sql, money,from);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            /**
             * 收款方法
             * @param conn 连接对象
             * @param to 收款人
             * @param money 金额    public void inMoney(Connection conn, String to, double money) {
                QueryRunner qr = new QueryRunner();
                try {
                    String sql = "update account set money = money + ? where name = ?";
                    qr.update(conn, sql, money,to);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    事务总结:

    事务的四种特性:ACID

    原子性:原子性是指事务是一个不可分割的工作单位,事务中的操作要么都成功,要么都不成功.

    隔离性:事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间的数据要相互隔离.

    持久性:持久性指的是一个事务一旦被提交,他对数据库中数据的改变就是永久性的,接下来及时数据库发生故障也不应该对其有任何影响.

    一致性:事务前后数据的完整性必须保持一致.

     并发访问问题:

    如果不考虑隔离性,事务存在3中并发访问问题.

    1.脏读:一个事务读到了另一个事务未提交的数据

    2.不可重复读:一个事务读到了另一个事务已经提交了的数据,引发另一个事务,在十五中的多次查询不一致.

    3.虚读/幻读:一个事务读到了另一个事务已经提交的数据,导致另一个事务,在事务中多次查询的结果不一致.

     隔离级别:解决问题

    数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。
    1. read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。
    a)存在:3个问题(脏读、不可重复读、虚读)。
    b)解决:0个问题
    2. read committed 读已提交,一个事务读到另一个事务已经提交的数据。
    a)存在:2个问题(不可重复读、虚读)。
    b)解决:1个问题(脏读)
    3. repeatable read:可重复读,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交。
    a)存在:1个问题(虚读)。
    b)解决:2个问题(脏读、不可重复读)
    1. serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。
    a)存在:0个问题。
    b)解决:3个问题(脏读、不可重复读、虚读)
    安全和性能对比
    安全性: serializable > repeatable read > read committed > read uncommitted
    性能 : serializable < repeatable read < read committed < read uncommitted
    常见数据库的默认隔离级别:
    MySql: repeatable read
    Oracle: read committed

    演示演示

    隔离级别演示参考:资料/隔离级别操作过程.doc【增强内容,了解】
    查询数据库的隔离级别

    show variables like '%isolation%';
    或
    select @@tx_isolation;

    设置数据库的隔离级别
    set session transactionisolation level 级别字符串
    级别字符串: readuncommitted 、 read committed 、 repeatable read 、 serializable
    例如: set session transaction isolation level read uncommitted;
    读未提交:readuncommitted
    A窗口设置隔离级别
    AB同时开始事务
    A 查询
    B 更新,但不提交
    A 再查询?-- 查询到了未提交的数据
    B 回滚
    A 再查询?-- 查询到事务开始前数据
    读已提交:read committed

    A窗口设置隔离级别
    AB同时开启事务
    A查询
    B更新、但不提交
    A再查询?--数据不变,解决问题【脏读】
    B提交
    A再查询?--数据改变,存在问题【不可重复读】
    可重复读:repeatable read
    A窗口设置隔离级别
    AB 同时开启事务
    A查询
    B更新, 但不提交
    A再查询?--数据不变,解决问题【脏读】
    B提交
    A再查询?--数据不变,解决问题【不可重复读】
    A提交或回滚
    A再查询?--数据改变,另一个事务
    串行化:serializable
    A窗口设置隔离级别
    AB同时开启事务
    A查询
    B更新?--等待(如果A没有进一步操作,B将等待超时)
    A回滚
    B 窗口?--等待结束,可以进行操作

  • 相关阅读:
    eas之Uuid和BOSUuid 区别
    BOS工具之BOS应用框架
    eas之EAS手工打包及快速部署工具
    S-HR体验中心
    wafII笔记
    eas之MrpUI
    S-HR快速查看shr日志
    S-HR二开基础
    linux安装mysql
    linux安装tomcat
  • 原文地址:https://www.cnblogs.com/qingmuchuanqi48/p/10922896.html
Copyright © 2011-2022 走看看