1
背景
最近小伙伴要上线一个小特性——审阅状态的变更。在上线的过程中,发现一个非常奇怪的问题。
基本上保证了逻辑没问题 ,多次检查也没问题。但是事实就是发生了!
小伙伴有点怀疑人生了:)。
所以我们临时的处理方式是增加日志打印,逐步验证我们的逻辑。看看我们是如何排查问题的。
使用的技术框架:springboot+jpa
2
问题排查
1)原逻辑代码
ALog aLog = aLogRepository.findOne(logId);
if (aLog.getStatus == UNKNOWN) {
aLog.setStatus(INIT); // ①
} else {
// ②
aLog.setStatus(RUNNING);
aLogRepository.save(aLog);
return;
}
// ③发送短信
smsAdapter.sendSms("手机号", "状态变更...");
return true;
上面是经过抽离的主要逻辑,存在的问题:
逻辑不会进入到②,只会进入到①和③,也就是当前的记录状态无需更新,并且需要发送短信。但是数据库的aLog记录状态就被更新了,到底是为什么?又没有调用save方法进行更新记录,怎么就会把数据给改了?
由于多次检查和增加日志逻辑验证,我们最终是确保逻辑是没问题的。
此时,我想到了唯一一个问题,就是跟Hibernate的对象状态有关!那么到底是不是呢,我们就得继续排查确认了。
2)原来是这样的
smsDao.save(sms);
smsHandle.sendSms();
查看了发送短信的逻辑,原来这里会将短信记录保存到数据库,再调用短信接口发送对应的短信内容。
那么,很有可能就是这里的smsDao.save帮忙把aLog顺便一起update了?事实确实如此。小伙伴就把发送短信的逻辑代码注释掉,重新测试调用接口,这时aLog数据库记录就不会被更新了。
所以罪魁祸首就是“它”。
这个时候就确定了是Hibernate包含了三种状态,此时aLog查询出来,数据库有此记录,session也有此对象,说明当前aLog是处于持久化状态,如果在事务提交的时候,会判断当前对象记录是否发生改变,是的话就会触发update操作。
所以,这也就是为什么aLog并没有主动调用save操作,就帮忙把它自动更新了。
重新理了一下逻辑,也调整了下代码,因为①aLog.setStatus(INIT)并不需要,多此一举了。所以把它去掉即可。
3)了解Hibernate的状态
Hibernate存在transient(瞬时状态),persistent(持久化状态)以及detached(离线状态)。
瞬时状态就是刚new出来一个对象,还没有被保存到数据库中,持久化状态就是已经被保存到数据库中,离线状态就是数据库中有,但是session中不存在该对象。
①对于刚创建的一个对象,如果session中和数据库中都不存在该对象,那么该对象就是瞬时对象(Transient)。
②瞬时对象调用save方法,或者离线对象调用update方法可以使该对象变成持久化对象,如果对象是持久化对象时,那么对该对象的任何修改,都会在提交事务时才会与之进行比较,如果不同,则发送一条update语句,否则就不会发送语句。
③离线对象就是,数据库存在该对象,但是该对象又没有被session所托管。
3
总结
虽然这个问题并不难(或者这不算问题),但是为了避免其他人或者你可能以后也会遇到的这种问题,可以很好的帮助(你)避免重蹈覆辙。
1)对于查询出来的对象,如果无需更新就不要set属性值,有时可以创建新变量来存储当前的值;
2)使用JPA(Hibernate)要注意自动会“帮你”更新数据库数据;
3)记录每次遇到的不小不大的问题,可以帮助少踩坑。
https://www.cnblogs.com/xiaoluo501395377/p/3380270.html
http://www.luyixian.cn/news_show_268613.aspx
3、Kafka面试题!掌握它才说明你真正懂Kafka
4、Netty 5.0为啥被舍弃?原因竟然是...
5、中台之上——业务架构系列【汇总】
-关注搬运工来架构,与优秀的你一同进步-
如果喜欢这篇文章或支持可以点在看哦↘
本文仅供学习!所有权归属原作者。侵删!文章来源: 搬运工来架构
文章评论