一、什么是事务? 要求MySQL的表类型为Innodb才支持事务。使用事务时,要求数据库引擎必须是 InnoDB 引擎 在数据库操作中,一项事务是由一条或多条对数据库更新的sql语句,所组成的一个不可分割的工作单元 只有当事务中的所有操作都正常完成了,整个事务才能被提交到数据库,如果有一项操作没有完成,就必须撤消整个事务。 例如: 在银行的转帐事务中,假定张三从自己的帐号上把1000元转到李四的帐号上,相关的sql语句如下: update account set monery=monery-1000 where name=‘zhangsan’ ; update account set monery=monery+1000 where name=‘lisi’ ; 这个两条语句必须作为一个完成的事务来处理。只有当两条都成功执行了,才能提交这个事务。如果有一句失败,整个事务必须撤消。
二、事务的特点: 原子性、一致性、隔离性和持久性。(一句话:一个业务要么全部成功,要么全部失败。) 原子性: 表示事务执行过程中,用户定义的操作序列要么全部执行成功,要么全部执行失败。 一致性: 表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态,这称为事务回滚。 隔离性: 表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见。 持久性: 表示事务完成之后,对系统的影响是永久性的。如果已提交的数据在事务执行失败时,数据的状态都应该正确。
三、JDBC实现事务方式: 1、保证一个业务的所有更新操作中。所使用的连接对象是同一个连接对象 2、将连接对象的提交方式设置为手动提交。 connection.setAutoCommit(false); 通过 connection.commit();提交事务 如果有异常发送时,可以通过connection.rollback();回滚事务 注意:控制事务的connnection必须是同一个
四、事务的并发问题 当两个或两个以上的线程,同时访问同一条记录时,就存在事务并发问题,可能造成数据混乱。 1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的就是脏数据。 2、不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。 3、幻读:事务A对数据库的数据进行批量操作。事务B完成记录的添加,这时新加的记录可能就没有进行事务A的批量操作。这就是幻读。 解决事务并发问题,需要采用事务隔离级别来进行
五、代码实现事务的提交和回滚详细过程 Transaction示例:
// 示例 public class TransactionRollBackController extends ServicePluginAdapter{ public static final Logger logger = LoggerFactory.getLogger(TransactionRollBackController.class);
@Override
public Object executeProcessService(ESPServerContext espContext, Object responseObject) throws Exception {
JParamObject po = espContext.getParamObject();
JResponseObject RO = espContext.getResponseObject();
try {
// 获取数据
String ybDctId = po.GetValueByParamName("ybDctId", "ATEST2");
String mbDctId = po.GetValueByParamName("mbDctId", "ATEST1");
if (StringUtils.isBlank(mbDctId)) {
return null;
} else {
// 根据字典表,先进行删除数据(先删除mbDctId,在根据ybDctId数据更新mbDctId数据)
operateDbData(espContext, ybDctId, mbDctId);
return RO;
}
} catch (Exception e) {
// 日志中心能接受到日志
logger.error(e.getMessage());
// 平台封装的异常类
ServiceException se = new ServiceException(e.getMessage());
se.setErrorMessage(e.getMessage());
// -1:异常;0:正常
se.setErrorCode(-1);
// 主要为了前台出现提示框
throw se;
}
}
/**
* 功能描述:
* 进行数据操作
*
* @param espContext
* @param ybDctId 源表名
* @param mbDctId 目标表名
*/
private void operateDbData(ESPServerContext espContext, String ybDctId, String mbDctId) throws SQLException {
JConnection connection = null;
PreparedStatement pSt = null;
// 设置回滚位置
Savepoint savepoint = null;
try {
// 获取连接
connection = espContext.getConnection();
// 关闭自动提交事务
connection.setAutoCommit(false);
// 组织删除数据
StringBuilder deleteSql = new StringBuilder();
deleteSql.append("DELETE FROM ").append(mbDctId).append(" WHERE F_BH = '1'");
// 编译
pSt = connection.prepareStatement(deleteSql.toString());
// 删除数据
pSt.executeUpdate();
// ============================================================
//设置回滚的点 失败只会回滚users信息
//savepoint = connection.setSavepoint("检查点");
// ============================================================
// 根据源表添加数据到目标表
StringBuilder upDateSql = new StringBuilder();
// INSERT INTO 目标表 SELECT * FROM 来源表 ;
upDateSql.append("INSERT INTO ").append(mbDctId).append(" SELECT * FROM ").append(ybDctId);
// 编译
pSt = connection.prepareStatement(upDateSql.toString());
// 添加数据
pSt.executeUpdate();
// =============================================================
//成功: 提交事务。
connection.commit();
// // 批量提交时 // // 1、积攒sql // preparedStatement.addBatch(); // // 进行批量处理 // if (i % 600 == 0 && i != 0) { // // 2、攒够之后提交一次,与数据库交互一次 // preparedStatement.executeBatch(); // // 清空缓存数据 // preparedStatement.clearBatch(); // } // } // preparedStatement.executeBatch(); // // 提交 // conn.commit(); // preparedStatement.clearBatch(); } catch (SQLException e) { e.printStackTrace(); try { //事变: 回滚事务 connection.rollback(); // 回滚到指定位置 // savepoint = conn.setSavepoint("检查点"); } catch (Exception e1) { e1.printStackTrace(); } } finally { //关闭连接 JConnection.BackStatement(pSt, null); } } }
添加主键冲突,报错后会进行回滚