JAVA WEB -事务处理

来源:互联网 发布:三文鱼部位图解知乎 编辑:程序博客网 时间:2024/06/01 18:53

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窗口?–可以继续操作,等待结束。

原创粉丝点击