什么是事务
事务是一个工作单元,该单元内的所有操作是一个整体,要么全部执行成功,要么就全部失败。只要有任一个单元操作失败,则操作全部失败。
注意:事务主要加在对数据库数据发送改变的操作中,例增删改,像查询则不需要事务
为什么需要事务
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 名称; 删除事务保存点
标记: 橙色