五、MySQL数据库之事务(1)

1 事务

1.1 事务概述

一组sql语句(insert、update、delete),全部成功整体才算成功,一个失败整体也算失败。

举个例子
a 和 b的账户中都有1000元,a给b转账100元,a 转账完成后会是什么结果?
正常情况下:

update t_account set money = money -100 where name='a';
update t_account set money = money +100 where name='b';

结果: a=900; b=1100

异常情况下:

update t_account set money = money -100 where name='a';

发生异常;

update t_account set money = money +100 where name='b';


结果: a=900; b=1000

事务的出现 解决上面的问题。 特点:要么全成功,要么全失败。


1.2 事务管理过程

在这里插入图片描述

1.3 事务特性ACID

事务是并发控制的基本单元。所谓事务一个sql语句操作序列,这些操作要么都执行,要么都不执行,他是一个不可分割的工作单元。

例如:银行转账工作,从一个帐号扣款并使另一个帐号增款,这个两个操作,要么都执行,要么都不执行。

数据库的事务必须具备ACID特性,ACID是指 Atomic(原子性)、Consistensy(一致性)、Isolation(隔离型)和Durability(持久性)的英文缩写。

1、原子性(Atomicity)

一个事务中所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中如果发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行一样。

2、一致性(Consistency)

一个事务在执行之前和执行之后 数据库都必须处于一致性状态。

如果事务成功的完成,那么数据库的所有变化将生效。

如果事务执行出现错误,那么数据库的所有变化将会被回滚(撤销),返回到原始状态。

3、持久性:

指一个事务一旦被提交,它对数据库的改变将是永久性的,接下来即使数据库发生故障也不会对数据产生影响。

4、隔离性(Isolation)

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

多个事务事件是相互独立的,多个事务事件不能相互干扰。


2 事务隔离问题

如果不考虑事务的隔离型,由于事务的并发,将会出现以下问题:

  1. 脏读 – 最严重,杜绝发生
  2. 不可重复读
  3. 幻读(虚读)

2.1 脏读

指一个事务读取了另外一个事务未提交的数据

在这里插入图片描述


2.2 不可重复读

在一个事务内多次读取表中的数据,多次读取的结果不同
注意
和脏读的区别: 不可重复读是读取的已提交数据
在这里插入图片描述


2.3 幻读(虚读)

一个事务 读取 另一个事务 已经提交的数据,强调的是 记录数 的变化,常有sql类型为 insert和 delete。
在这里插入图片描述
虚读和不可重复读的区别:

虚读 强调的是数据表 记录数 的变化,主要是 insert 和 delete 语句。
不可重复读 强调的是数据表 内容 的变化,主要是 update 语句。

3 数据库的隔离级别

数据库共定义了4种隔离级别(限制由高到低, 性能从低到高):

serializable(串行化):避免 脏读、不可重复读、虚读情况的发生。(—— 最高限制,性能最低)

repeatable read(可重复读):可避免 脏读、不可重复读, 不可避免虚读。mysql采用可重复读。

read committed(读已提交):可避免 脏读,不可避免不可重复读虚读。oracle采用读已提交。

read uncommitted(读未提交):不可避免 脏读不可重复读虚读。(—— 最低限制,性能最高)

mysql 默认选择的可重复读 – repeatable read
oracle 选择 读已提交 – read committed

4 java中的事务管理

4.1 JDBC添加事务

 *  JDBC实现转账,保证数据安全
 *  使用事务技术
 *  需求: tom -1000   , jerry+1000
 *
 *  Connection连接对象方法:
 *    void setAutoCommit(false)设置事务的提交方式,传false阻止自动提交
 *
 *    commit() 提交事务
 *
 *    rollback() 回滚
public class JdbcTransaction {
    public static void main(String[] args)  {
        Connection con = null;
        PreparedStatement stmt = null;
        try {
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取连接对象
            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "root", "root");

            //阻止事务自动提交,开启事务
            con.setAutoCommit(false);

            //拼写tom-1000的SQL语句
            String sqlOut = "update account set money = money - 1000 where name = 'tom'";
			//获取SQL语句执行对象
			stmt = con.prepareStatement(sqlOut);
            //执行SQL
            stmt.executeUpdate();
            //int a = 1 / 0;
            //拼写jerry+1000的SQL语句
            String sqlIn = "update account set money = money + 1000 where name = 'jerry'";
            stmt = con.prepareStatement(sqlIn);
            stmt .executeUpdate();

            //提交事务
            con.commit();
        }catch (Exception ex){
            //程序执行catch,出现异常,SQL语句执行失败,回滚
            ex.printStackTrace();
            try {
                //回滚
                con.rollback();
            }catch (Exception e){e.printStackTrace();}
        }finally {
            //释放资源
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

4.2 DbUtils事务操作

Connection对象的方法名描述
conn.setAutoCommit(false)开启事务
new QueryRunner()创建核心类,不设置数据源(手动管理连接)
query(conn , sql , handler, params ) 或
update(conn, sql , params)
手动传递连接, 执行SQL语句CRUD
DbUtils.commitAndCloseQuietly(conn)提交并关闭连接,不抛异常
DbUtils.rollbackAndCloseQuietly(conn)回滚并关闭连接,不抛异常

注意:
使用无参的QueryRunner的构造方法,只有获取了连接对象才可以对事务进行管理和控制,我们自己控制Connection连接。

案例介绍:
dao层:访问数据库

/**
 *  对数据表account的操作
 */
public class AccountDao {

    /**
     * 定义方法,实现转账,更新账户的数据
     * 传递参数,用户名,余额
     */
    public void updateAccount(String name, double money , Connection con) throws SQLException {
        //事务,不能传递连接池
        QueryRunner qr = new QueryRunner();
        //拼写更新的SQL
        String sql = "update account set money = ? where name = ?";
        qr.update(con,sql,money,name);
    }

    /**
     * 定义方法: 查账户信息
     * 传递参数,账户名,用户输入的
     * 查询后结果
     * BeanHandler封装, 结果集就是Account对象
     */
    public Account queryAccount(String name, Connection con)throws SQLException{
        //事务,不能传递连接池
        QueryRunner qr = new QueryRunner();
        //拼写查询账户的SQL
        String sql =  "select * from account where name = ?";
        //执行SQL语句,封装为Account对象
        Account account = qr.query(con,sql,new BeanHandler<Account>(Account.class),name);
        return account;
    }
}

service层,对数据进行运算

public class AccountService {
    /**
     * 创建方法,实现转账
     * 参数,付款人,收款人,金额
     */
    public void transfer(String fukuan, String shoukuan,double money){
        //调用dao层,先查询账户的余额
        AccountDao accountDao  = new AccountDao();
        Connection con = null;
        try {
            //业务层获取数据库连接对象
            con = DruidUtils.getConnection();
            //修改隔离级别con.setTransactionIsolation(Connection接口静态成员变量);
            //开启事务,阻止自动提交
            con.setAutoCommit(false);

            //dao层方法,查询账户,付款人
            Account accountFukuan = accountDao.queryAccount(fukuan, con);
            //dao层方法,查询账户,收款人
            Account accountShoukuan = accountDao.queryAccount(shoukuan, con);

            System.out.println(accountFukuan);//Account(id=1, name=tom, money=10000.0)
            System.out.println(accountShoukuan);//Account(id=2, name=jerry, money=10000.0)

            //accountFukuan的余额 - 1000
            accountFukuan.setMoney(  accountFukuan.getMoney() - money);
            //accountShoukuan的余额 + 1000
            accountShoukuan.setMoney( accountShoukuan.getMoney() + money );
            //调用dao层方法update转账
            accountDao.updateAccount(fukuan, accountFukuan.getMoney(),con );
            //int a = 1/0;
            accountDao.updateAccount(shoukuan,accountShoukuan.getMoney(),con);

            //SQL语句执行成功,提交事务
            //DBUtils工具类,提交事务并释放资源
            DbUtils.commitAndCloseQuietly(con);
        }catch (SQLException ex){
            ex.printStackTrace();
            //SQL执行失败,回滚
            // DBUtils工具类,回滚事务并释放资源
            DbUtils.rollbackAndCloseQuietly(con);

        }finally {
            // DBUtils工具类,释放资源
            DbUtils.closeQuietly(con);
        }

    }
}

5 Java经典三层架构

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值