数据库事务

1、事务的概念

多个数据库操作称为一个事务。并且事务中的多个操作,必须全部成功,不然需要回滚,不存在部分成功的情况。比方说,从ATM机取钱,先提交取款申请,ATM接受申请,做出处理,出钱,并且扣款。如果扣款成功,但是没有出钱,是不行的。上述的操作,可以称为一个事务。事务的存在就是避免上述系统中严重的数据一致性问题。

事务特性:

ACID
A:原子性 (事务具有原子性,所有操作看成一个,要么全部成功,要么都不成功)

C:一致性 ( 数据库操作前后,整体状态是一致的。比方说,A和B用户之间,转账前和转账后,账户总和是一样的)

I: 隔离性 ( 事务之间的执行时相互的独立的,彼此之间不受影响 )

D:持久性 ( 对数据库进行事务操作后,对数据库的修改是永久保存的 )

事务的ACID特性是由关系型数据库管理系统来实现的,其采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库回退到执行事务前的初始状态。
数据库管理系统采用锁机制(表锁、行锁、页锁、乐观锁、悲观锁)来实现事务的隔离性,当多个事务同时更新数据库中相同的数据时,只允许持有锁的事务能够更新数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新此数据。

2、JDBC如何处理事务

public void JdbcTransfer() { 
java.sql.Connection conn = null;
 try{ 
    conn = conn       =DriverManager.getConnection("jdbc:oracle:thin:@host:1521:SID","username","userpwd");
     // 将自动提交设置为 false,
     //若设置为 true 则数据库将会把每一次数据更新认定为一个事务并自动提交
     conn.setAutoCommit(false);

     stmt = conn.createStatement(); 
     // 将 A 账户中的金额减少 500 
     stmt.execute("\
     update t_account set amount = amount - 500 where account_id = 'A'");
     // 将 B 账户中的金额增加 500 
     stmt.execute("\
     update t_account set amount = amount + 500 where account_id = 'B'");

     // 提交事务
     conn.commit();
     // 事务提交:转账的两步操作同时成功
 } catch(SQLException sqle){            
     try{ 
         // 发生异常,回滚在本事务中的操做
        conn.rollback();
         // 事务回滚:转账的两步操作完全撤销
         stmt.close(); 
         conn.close(); 
     }catch(Exception ignore){ 

     } 
     sqle.printStackTrace(); 
 } 
}

上述代码实现了一个简单的转账功能,通过事务,要么都成功,要么都回滚。

3、Mybatis框架如何处理事务

配置文件的配置:

<transactionManager type="JDBC" />

在配置文件中配置JDBC事务。默认不自动提交。

事务的创建:
//使用SqlSession来处理事务。

SqlSession sqlSession = // 
使用SqlSessionFactoryBuider 创建Session

事务的提交:
在更新操作中,Insert 、Update、Delete 操作中需要提交事务,不然即使执行了数据库操作了,但是对数据库仍然不产生影响。
sqlSession.commit() ;

事务的回滚:
在数据库操作的异常处理中,对事务进行回滚操作。
sqlSession.rollback();

事务的关闭:
任何数据库操作,最后都得对事务进行关闭。
sqlSession.close() ; 在finally中

4、Spring事务

5、分布式场景中的事务

JDBC事务不能夸多个数据库,所以不支持分布式场景下存在多个数据库的情况。
JTA(Java Transaction API ) 解决分布式事务的方案之一。
在日常开发中,如果使用JDBC就能够解决数据库事务问题,那么说明此项目是一个小项目。在电商系统中,一般把一个电商网站拆分成商品模块、订单模块、购物车模块、消息模块、支付模块等。然后我们把不同的模块部署到不同的机器上,各个模块之间通过远程服务调用(RPC)(dubbo框架)等方式进行通信,以一个分布式的系统对外提供服务。
举例:在一个支付流程中,要和多个模块进行交互,每个模块都部署在不同的机器中,并且每个模块的数据库都不一致,这时候无法使用JDBC来管理事务。

/* 支付订单处理 */
@Transactional(rollbackFor = Exception.class)
public void completeOrder() {
orderDao.update(); // 订单服务本地更新订单状态
accountService.update(); // 调用资金账户服务给资金帐户加款
pointService.update(); // 调用积分服务给积分帐户增加积分
accountingService.insert(); // 调用会计服务向会计系统写入会计原始凭证
merchantNotifyService.notify(); // 调用商户通知服务向商户发送支付结果通知
}

参考文章:
http://www.cnblogs.com/chengpeng15/p/5802930.html

6、事务死锁
事务A和事务B,分别访问数据资源1 和资源2,如果事务A占有资源1,请求资源2。事务B占有资源2,请求资源1。那么就会出现死锁的情况。称为事务死锁。

7、事务的提交和回滚
对数据库操作执行完成后,需要对事务进行提交,才对数据库产生了影响。提交完成后,事务也就结束。如果事务执行过程中,出现了异常,那么会进行事务的回滚,对数据库不产生影响,回到初始状态。

8、事务超时

事务执行会单独的占用数据库资源,如果一个事务长时间占用数据库,必然会影响其他事务的执行,所以给事务之设定了超时时间,如果在超过一定的时间内,事务没有执行完成,那么就进行回滚,回滚结束后,事务自动结束。

PS: 事务结束的机制是执行Commit 或者 Rollback操作。

异常现象:
一次操作中,创建了事务,但是事务没有提交。这时候执行另外一个数据库操作,出现异常。提示,数据库已经被锁定。
在一个SqlSession还没有提交前,执行另外SqlSession不能执行。这和事务相关吗?还是数据库访问锁表的问题?
事务的隔离性,通过锁机制来实现,应该是事务A并没有提交,还持有锁,执行到其他事务B的时候,等待A释放锁,但是A一直不释放,等待一段时间后,出现事务超时,则执行失败,抛出异常????

项目中的现状:
@Transactional 事务使用在类上。其作用域是什么?
@Transactional 事务使用在某方法上,但是并没有异常触发条件。
@Transactional(isolation =Isolation.SERIALIZABLE) 事务注解中括号后面的是Isolation.SERIALIZABLE这个是什么?
在mqcp的消费者类中存在事务不生效的情况。
@Transactional(rollbackFor = { Exception.class }) 注意存在使用了事务,但是不生效的情况。 这个跟某些方法不能实施Spring AOP事务有关。
@Transactional事务的粒度,不能过大。不然效率很低。

@AspectJ 这个注解的作用。在项目中

@Slf4j
@Aspect
@Component
public class WebLogAspect {

@Pointcut("execution(public * com.pa.schedule.web..*.*(..))")
public void webLog(){}

@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable{

    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();
    //HttpServletResponse  response = attributes.getResponse();
     // 记录下请求内容
    log.info("请求url : " + request.getRequestURL().toString());
    log.info("类名class_method : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
    log.info("入参 : " + Arrays.toString(joinPoint.getArgs()));
}

@AfterReturning(returning = "ret" , pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable{
    log.info("返回结果 : " + ret);
}
}

每个模块都存在这个类,主要的作用就是实现对Controller层请求的拦截 ,在请求前和请求后加上相应的日志打印。

//未完成待整理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值