JAVA WEB -事务处理

1.事务处理

1.1 事务处理类型

mysql
JDBC
DBUtils

1.2 Mysql事务处理

首先,打开小海豚,创建表,并输入数据。

CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(50),
    money DOUBLE
);

INSERT INTO account(id,NAME,money) VALUES(NULL,'jack',10000);
INSERT INTO account(id,NAME,money) VALUES(NULL,'rose',20000);

打开CMD,进行操作

Start transaction;
commit;
roll back;
三个步骤,如果打开Start transaction,并且Update数据库中的数据,在小海豚刷新是没有任何变化的,执行commit后,刷新小海豚数据才会更新。’

1.3 mysql autocommit

每次在CMD中操作mysql语句,都是默认autocommit。
可以通过语句进行改变:show variables like ‘xxcommit’; —-》
–》 set autocommit = 0/1;
***Oracle 数据库不自动commit;

1.4 JDBC事务操作

conn.setAutoCommit(false) 开启事务
conn.commit() 提交事务
conn.rollbalck() 回滚事务

@Test
    public void demo0() throws Exception{
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = (Connection) JDBCUtils.getConnection();
            connection.setAutoCommit(false);

            String sql = "update account set money = money + ? where name = ?";

            preparedStatement = connection.prepareStatement(sql);

            preparedStatement.setInt(1,-100);
            preparedStatement.setString(2, "zhilong");
            preparedStatement.executeUpdate();

            preparedStatement.setInt(1,100);
            preparedStatement.setString(2, "tommy");
            preparedStatement.executeUpdate();

            connection.commit();

            int r = preparedStatement.executeUpdate();
            System.out.println(r);

        } catch (Exception e) {
            // TODO: handle exception
            try {
                connection.rollback();
                throw new RuntimeException("program error",e);
            } catch (Exception e2) {
                // TODO: handle exception
            }
        }finally {
            if (connection != null) {
                connection.close();
            }
        }
    }
1.5 DBUtils事务操作

*conn.setAutoCommit(false) 开启事务
*new QueryRunner() 创建核心类,不设置数据源,手动管理连接。
*Query()、Update() 手动传递连接
*Dbutils.commitAndClose(conn) 或 DbUtils.rollbackandClose(conn) 关闭连接。

        Connection connection = null;
        QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource());
        try {

         connection = C3P0Utils.getConnection();
         connection.setAutoCommit(false);

         String sql = "update account set money= money+? where name = ?";
         Object[] params = {-100,"jack"};
         int r = queryRunner.update(connection, sql, params);

         Object[] params2 = {100,"rose"};
         int r2 = queryRunner.update(connection, sql, params2);


//       DbUtils.commitAndClose(connection);
         DbUtils.commitAndCloseQuietly(connection);
        } catch (Exception e) {

            // TODO: handle exception

            DbUtils.rollbackAndClose(connection);
        }

1.6 分层

dao 数据库操作
Service 业务代码加上事务进行操作
domain Bean层
AccountDao:

public class AccountDao {

    public void out(Connection conn,String outUser,Double money) throws Exception{

        QueryRunner queryRunner = new QueryRunner();
        String sql = "update account set money = money - ? where name = ?";
        Object[] objects = {money,outUser};

        int r =  queryRunner.update(conn,sql, objects);
        System.out.println(r);

    }

    public void in(Connection connection,String inUser,Double money) throws Exception{

        QueryRunner queryRunner = new QueryRunner();
        String sql = "update account set money = money - ? where name = ?";
        Object[] objects = {money,inUser};
        queryRunner.update(connection,sql,objects);

    }
}

AccountService:

public class AccountService {

    public void transfer(String outUser,String inUser,double money) throws Exception{
//      1,获得连接
        Connection connection =  C3P0Utils.getConnection();
        try {

//       2开启事务
         connection.setAutoCommit(false);

//       业务代码
        AccountDao accountDao = new AccountDao();
        accountDao.out(connection,outUser, money);


        accountDao.in(connection,inUser, money);
//       3提交事务
         DbUtils.commitAndCloseQuietly(connection);

        } catch (Exception e) {
            // TODO: handle exception
//          如果有异常就回滚
            DbUtils.rollbackAndCloseQuietly(connection);

            throw new RuntimeException(e);
        }
    }
}

Test:

public class test {
    public static void main(String[] args) throws Exception {

        String outUser = "jack";
        String inUser = "rose";
        double money = 100d;
        try {
            AccountService accountService = new AccountService();
            accountService.transfer(outUser, inUser, money);
            System.out.println("transfer successfully");
        } catch (Exception e) {
            // TODO: handle exception
            System.out.println("transfer failed");
        }
    }
}

####1.7 ThreadLocal
用途:除了事务以外,JDK允许此类可以在一个线程中共享数据。
Threadlocal:底层就是一个Map,key存放的是当前线程,Value存放的是共享数据。
实现:C3P0Utils.getConnection() 内部使用Threadlocal,用于本地线程缓存连接,1).从ThreadLocal获得连接
2).如果没有,从连接池获得连接,并保存到ThreadLocalhost中。
3).获得连接,返回即可。
代码:

**C3P0Utils:**
public class C3P0Utils {
    private static DataSource dataSource = new ComboPooledDataSource("itheima");

    private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();

    public static ComboPooledDataSource getDataSource(){

        return (ComboPooledDataSource) dataSource;
    }

    public static Connection getConnection() throws SQLException{
//      1.从本地线程变量获得
        Connection connection = local.get();

//      2.如果没有,从连接池获得,并添加到ThreadLocal中
        if (connection == null) {
            connection =  dataSource.getConnection();
            local.set(connection);
        }

        return connection;

    }
}

AccountService

public class AccountService {

    public void transfer(String outUser,String inUser,double money) throws Exception{
        java.sql.Connection connection = null;
        try {
//          获得连接
            connection = C3P0Utils.getConnection();
//          开启事务
            connection.setAutoCommit(false);

//          业务操作
            AccountDao accountDao = new AccountDao();
            accountDao.out(outUser, money);
            accountDao.in(inUser, money);
//          提交事务
            DbUtils.commitAndCloseQuietly(connection);
        } catch (Exception e) {
            // TODO: handle exception
            DbUtils.rollbackAndCloseQuietly(connection);
            throw new RuntimeException(e);
        }


    }

}

AccountDao

public void out(String outUser,Double money) throws Exception{

        QueryRunner queryRunner = new QueryRunner();
        String sql = "update account set money = money - ? where name = ?";
        Object[] objects = {money,outUser};
        int r =  queryRunner.update(C3P0Utils.getConnection(), sql,objects);
        System.out.println(r);

    }

    public void in(String inUser,Double money) throws Exception{

        QueryRunner queryRunner = new QueryRunner();
        String sql = "update account set money = money - ? where name = ?";
        Object[] objects = {money,inUser};
        queryRunner.update(C3P0Utils.getConnection(),sql,objects);

    }
}

Test

public class test {
    public static void main(String[] args) {

        String outUser = "zhilong";
        String inUser = "tommy";
        double money = 100d;
        try {

            AccountService accountService = new AccountService();
            accountService.transfer(outUser, inUser, money);


        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}
1.7 事务总结

*1.7.1 事务特性:ACID

原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作,要么都发生,要么都不发生。
一致性(Consistency)事务前后数据的完整性必须保持一致。
隔离型(Isolation)事务的隔离性是指多个用户并发访问数据时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。
持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中的数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
*1.7.2并发访问问题
如果不考虑隔离性,事务存在3种并发访问问题。
脏读:一个事务读到另一个事务未提交的数据。
不可重复读:一个事务读到另一个事务已经提交的(Update)数据,引发另一个事务,在事务中多次查询结果不一致。
虚读和幻读:一个事务读到另一个事务已经提交的(insert)数据,导致另一个事务,在事务中多次查询结果不一致。
*1.7.3隔离级别:解决问题
Read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。

存放,3个问题(脏读,不可重复读,虚读)。
解决,0个问题。
Read committed 读已提交,一个事务读到另一个事务已提交的数据。
存放:2个问题(不可重复读,虚读)
解决:1个问题(脏读)。
repeatable Read:可重复读,在一个事务中读到数据始终保持一致,无论另一个事务是否提交。
存放:1个问题(虚读)。
解决:2个问题(脏读,不可重复读)。
serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。
存放:0个问题。
解决:1个问题(脏读,不可重复读,虚读)。
安全和性能对比
安全性:serializable>repeatable read>read committed>read uncommitted.
性能:serializable

A: 
mysql> select * from accoun
+----+---------+-------+
| id | name    | money |
+----+---------+-------+
|  1 | jack    | 25400 |
|  2 | rose    | 20000 |
|  3 | zhilong |  9300 |
|  4 | Tommy   |  5100 |
|  5 | Tommy   |  5000 |
|  6 | hans    |  4600 |
+----+---------+-------+
6 rows in set (0.00 sec)

mysql> select * from accoun
+----+---------+-------+
| id | name    | money |
+----+---------+-------+
|  1 | jack    | 25300 |
|  2 | rose    | 20000 |
|  3 | zhilong |  9300 |
|  4 | Tommy   |  5100 |
|  5 | Tommy   |  5000 |
|  6 | hans    |  4600 |
+----+---------+-------+
6 rows in set (0.00 sec)

mysql> select * from accoun
+----+---------+-------+
| id | name    | money |
+----+---------+-------+
|  1 | jack    | 25400 |
|  2 | rose    | 20000 |
|  3 | zhilong |  9300 |
|  4 | Tommy   |  5100 |
|  5 | Tommy   |  5000 |
|  6 | hans    |  4600 |
+----+---------+-------+
6 rows in set (0.00 sec)

B:

mysql> select * from accoun
+----+---------+-------+
| id | name    | money |
+----+---------+-------+
|  1 | jack    | 25400 |
|  2 | rose    | 20000 |
|  3 | zhilong |  9300 |
|  4 | Tommy   |  5100 |
|  5 | Tommy   |  5000 |
|  6 | hans    |  4600 |
+----+---------+-------+
6 rows in set (0.00 sec)

mysql> select * from accoun
+----+---------+-------+
| id | name    | money |
+----+---------+-------+
|  1 | jack    | 25300 |
|  2 | rose    | 20000 |
|  3 | zhilong |  9300 |
|  4 | Tommy   |  5100 |
|  5 | Tommy   |  5000 |
|  6 | hans    |  4600 |
+----+---------+-------+
6 rows in set (0.00 sec)

mysql> select * from accoun
+----+---------+-------+
| id | name    | money |
+----+---------+-------+
|  1 | jack    | 25400 |
|  2 | rose    | 20000 |
|  3 | zhilong |  9300 |
|  4 | Tommy   |  5100 |
|  5 | Tommy   |  5000 |
|  6 | hans    |  4600 |
+----+---------+-------+
6 rows in set (0.00 sec)

>读已提交

A窗口设置隔离级别
AB同时开启事务
A查询
B更新、但不提交
A在查询?–数据不变,解决问题【脏读】。
B提交。
A在查询?–数据改变,存在问题【不可重复读】。
>可重复读:repeatable Read
A窗口设置隔离级别
AB同时开启事务
A查询
B更新,但不提交
A在查询
B提交
A在查询?—数据不变,解决【脏读】
B提交
A在查询? —数据不变,解决【不可重复读】
A提交或者回滚
A在查询 ?—数据改变,另一个事务【可重复读】

>串行化:serializable

A窗口设置隔离级别
AB同时开启事务
A查询
B跟新?–等待更新,如果A没有进一步操作,B将等待超时
A回滚
B窗口?–可以继续操作,等待结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值