事务(包含Java程序)

什么是事务

事务是一个工作单元,该单元内的所有操作是一个整体,要么全部执行成功,要么就全部失败。只要有任一个单元操作失败,则操作全部失败。

注意:事务主要加在对数据库数据发送改变的操作中,例增删改,像查询则不需要事务

为什么需要事务

SQL语句只能执行一条,而事务可以把多个SQL语句组成一个事务,同时执行

事务的四大特性(ACID)

标记: 面试题

1、原子性(Atomicity):事务中的所有操作是不可再分割的原子单位,要么全部成功,要么全部失败
标记: 红色

2、一致性(Consistency):事务执行后,数据库状态与其他业务规则保持一致。
标记: 红色

如:
转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的。

3、隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离起来,使每个并发中的事务不会互相干扰
标记: 红色

4、持久性(Durability):一旦事务提交成功,事务中的所有操作都必须被持久化到数据库中,即使提交事务后,数据库马上奔溃,在数据库重启时,也必须能保证通过某种机制恢复数据
标记: 红色

MySQL中的事务

标记: 红色

例:
#胡一刀给胡一菲转账500元
START TRANSACTION;
UPDATE bank SET cmoney=cmoney+500 WHERE cname='胡一菲';
UPDATE bank SET cmoney=cmoney-500 WHERE cname='胡一刀';
COMMIT;

1、在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务

2、Mysql的事务默认是 “自动提交”,而oracle默认是 “不自动提交”

1、start transaction; 开启事务(关闭自动提交)
标记: 红色

2、show variables like “%commit%”; 查看当前mysql的事务状态
标记: 蓝色

3、set atuocommit=0; 设置自动提交关闭,数值为1则是开启自动提交。
标记: 蓝色

4、commit; 提交事务(及事务中的多条SQL语句所作出的影响会持久化到数据库中)
标记: 红色

5、rollback; 回滚事务(即回滚到事务的起点,之前做的所有操作都被撤销)
标记: 红色, 已经提交的事务是 不能回滚的

Java程序的事务

Java程序中的事务管理是通过Connection连接数据库对象来操作的

方法:

1、commit():void 使所有上一次提交/回滚后进行的更改成为持久更改,并释放Connection对象当前持有的所有数据库锁。
标记: 红色, 例://提交 conn.commit();

2、rollback():void 取消在当前事务中进行的所有更改,并释放Connection对象当前持有的所有数据库锁。
标记: 红色, 例://回滚 conn.rollback();

3、setAutoCommit(boolean autoCommit):void 将此链接的自动提交模式设置为给定状态
标记: 红色

例:
//设置conn连接对象 不自动提交
conn.setAutoCommit(false);

4、rollback(Savepoint savepoint):void 取消所有设置给定Savepoint对象之后进行的更改

使用注意:

1、在使用JDBC进行事务控制时,要在业务层中完成,即在业务层中使用同一个数据连接Connection对象

2、不能在dao层实现类中直接创建数据库连接对象

例:在dao层的方法,要使用事务的需要把业务层的Connection带过去

//例如转账事务的操作,涉及转出和转入的,两个方法要自带数据库连接对象
    /**转出*/
    int transactionOut(Connection conn, Integer Id, BigDecimal money) throws Exception;

    /**转入*/
    int transactionIn(Connection conn, Integer Id, BigDecimal money) throws Exception;

3、应该在业务层实现类中去创建并引入数据库连接对象connection

例://在业务层中的银行交易实现类(AccountServiceImpl)中
public class AccountServiceImpl implements IAccountService {
    IAccountDao accDao = (AccountDaoImpl)DaoUrlUtils.getDao("AccountDaoImpl");

    @Override
    public String transferAccount(Integer souId, Integer targetId, BigDecimal money) throws Exception {
        //获取同一个的连接对象(事务的所有操作必须是同一个conn)
        Connection conn = JdbcUtils.getConn();
		。。。。
		}
完整(service层)使用示例:
例://在银行交易Service中,转账业务的实现
@Override
    public String transferAccount(Integer souId, Integer targetId, BigDecimal money) throws Exception {
        //获取同一个的连接对象(事务的所有操作必须是同一个conn)
        Connection conn = JdbcUtils.getConn();
        //设置conn连接对象 不自动提交
        conn.setAutoCommit(false);
        //获取转出账户的余额
        BigDecimal souMoney = accDao.selAccountById(souId).getMoney();
        //比较余额和转出金额的大小
        boolean istransfer = souMoney.compareTo(money)!=-1;
        try {
            //用户余额大于等于转出金额
            if(istransfer) {
                //开始转账的转出
                int i = accDao.transactionOut(conn,souId,money);
                //开始转账的转入
                int j = accDao.transactionIn(conn,targetId,money);
                if(i>0 && j>0) {
                    //提交
                    conn.commit();
                    return "success";
                }else {
                    return "fail";
                }
            }else {
                //用户余额小于转出金额,不可以转账
                return "fail";
            }
        } catch (Exception e) {
            //出现错误就回滚事务
            conn.rollback();
            e.printStackTrace();
            return "error";
        }finally {
            //关闭conn连接
            conn.close();
        }
    }

线程获取Connection

从线程中取数据库连接对象

//得到本地线程对象(线程中装有数据库的连接对象)     
private static final ThreadLocal<Connection> tl = new ThreadLocal<Connection>();       
//取到数据源对象     
public static DataSource getDataSource() {         
		return dataSource;     
	}       

	//从线程中获取数据源     
	public static Connection getConn(){         
		try {             
			conn = tl.get();  //有可能线程中不存在Connection数据库连接对象             
			if(conn==null){                 
			conn = dataSource.getConnection();  //若线程中不存在,则由数据源得到连接对象                 
			tl.set(conn);  //把得到的Connection数据库连接对象立即装入到线程中             
			}         
		} catch (SQLException e) {             
			e.printStackTrace();         
		}         
		return conn;     
}

1、事物依然加在业务层,不能加在dao层。
2、线程相当于内存中的一个盒子,里面装有Connection数据库连接对象(最多只有一个) 只要没有关闭此线程或者没有关闭此线程中的Connection数据库连接对象,则线程中的Connection数据库连 接对象永远都是同一个对象。
3、hibernate框架处理事物就是通过从线程中取

扩展:事务保存点

案例:
创建事务,给bank表插入2条数据,然后设置保存点,然后回滚到保存点。
select * from bank;
start transaction;
insert into bank values ('胡一刀',200);
savepoint X;
insert into bank values ('胡一菲',300);
savepoint y;
insert into bank values ('胡一飞',400);
rollback to savepoint X; -- 回滚到事务保存点
-- 事务保存点以上的数据不回滚,直接保存到硬盘
-- 事务保存点以下的数据全部回滚
rollback; -- 全部回滚
release savepoint X; -- 删除事务保存点

在使用事务时,当用户回滚事务时将取消所有操作。在一些大型事务中,有时并不需要取消全部操作。

通过在事务内部设置事务保存点,如果将事务回滚到事务保存点,则该事务保存点之后的所有操作将被取消,而事务保存点之前的则保存

savepoint 名称; 创建事务保存点
标记: 橙色

rollback to savepoint 名称; 回滚到事务保存点
标记: 橙色

release savepoint 名称; 删除事务保存点
标记: 橙色

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值