发表于:MyBatis一级缓存引发的问题
一般的,我们在写一个系统的时候,都会分为持久层,服务层,和控制层,各层之间职责分明,对应的Entity,DTO,VO也是很明确知道用在哪里的,但是有时候为了方便,特别是用MyBatis的时候,直接从持久层返回一个vo,然后这个vo又作为方法的参数在系统之间来回调用,那么会引发什么样的问题呢?
假设我们有如下代码:
/**
* 确认订单
*/
public boolean confirmOrder(String orderId){
OrderVo orderVo = orderDao.findByOrderId(orderId);
// 假设计算订单号需要用到一个特殊的利率
BigDecimal annualRate = new BigDecimal(0.12);
// 根据订单号和利率计算实际的价格
String realAmount = calculateRealAmount(orderId, annualRate);
System.out.println(orderVo.getAnnualRate.longValue());
}
/**
* 计算订单实际价格的方法,传入orderVo和实际的利率,用于计算订单价格
*/
private BigDecimal calculateRealAmount(OrderVo orderVo, BigDecimal annualRate){
OrderVo orderVo = orderDao.findByOrderId(orderId);
orderVo.setAnnualRate(annualRate); // 设置新的计算利率
calculateRealAmount(orderVo);
}
/**
* 计算订单实际价格的方法,传入orderVo用于计算
*/
private BigDecimal calculateRealAmount(OrderVo orderVo){
return NumberUtils.mulBigDecimal(orderVo.getAmount, orderVo.getAnnualRate);
}
假设confirmOrder()
是处于一个事务里面,confirmOrder()
方法里面通过calculateRealAmount()
方法计算了实际的订单金额,这两个方法都通过orderDao.findByOrderId(orderId);
查找到了OrderVo,由于MyBatis默认开启了以及缓存,实际上这两个方法查找出来的是同一个OrderVo对象,所以calculateRealAmount()
里面对orderVo的改动,也会影响到confirmOrder()
里面的vo,所以confirmOrder(String orderId)
最后一行输出的值也不是刚查找出来的了,使用不当,会造成潜在的bug。
下面就来总结一下这个问题。
代码规范问题引发脏数据
原因是MyBatis xml文件里面读取出来的是一个VO,某一些方法也使用该VO作为参数,在传递VO参数的时候,对VO进行了重新设置,导致接下来的地方读取vo的属性读到了脏数据。
项目规范:
分清各层数据的职责,防止出现类似情况;
Entity查找出来不要更改,更改后则一定要入库处理,也不要作为方法的参数进行传递;
禁止从数据库里面直接查找出VO。