事务

事务概述:

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

  事务作用:保证在一个事务中多次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 窗口?--等待结束,可以进行操作

posted @ 2019-05-25 16:39  King-DA  阅读(203)  评论(0编辑  收藏  举报