hql及sql执行顺序导致的问题

转自:https://blog.csdn.net/xiaobing_122613/article/details/54692792


因为Hibernate连表查询比较麻烦,在有些情况下一个业务方法有可能既用到了普通的hibernate查询又用到了hibernate的原生sql查询,如果没有控制好hibernate的执行顺序,有可能会导致程序逻辑错误。拿个例子来说明一下:
{
 StudentModel student=studentDAO.selectById(1);
 student.setClassNum(2);
 student.setStudentName("li");
 studentDAO.updateStudent(student);
 List<StudentModel>students=studentDAO.getStudentByClassNum(2);
 .......
}
上面的一段逻辑是,取出一个Id=1的学生,然后将其所在的班级设置为2,并将马上执行这一操作,接下来是执行一步查询操作(这部操作hibernate用的是原生的sql),将班级为2的同学查出来。结果id为1的同学不在其中,这明显和预期的不一致。如果你把hibernate执行的sql
语句打印出来,发现程序先执行了两条select语句,最后才执行了update语句。之所以这样是和Hibernate的flush处理机制有关,按照insert,update,……,delete的顺序提交所有登记的操作。最后执行的顺序,和代码写的顺序就不一定一致。因此在一些复杂的方法中,有更 新和保存的过程中就要考虑数据库操作顺序的改变以及延时flush是否对程序的结果有影响。如果确实存在着影响,那就可以在需要保持这种操作顺序的位置加 入flush强制,比如:
hibernateTemplate.flush();

Hibernate将缓存中记录的操作flush入数据库,这样看起来也许不太美观,但很有效。


问题起源于代码中对某个对象的处理逻辑,当发现该对象的某些属性变化时,先将原数据删除,再插入一个新对象,由于使用了Spring+Hibernate的组合,很容易写出如下代码:

<code class="hljs go"><span class="hljs-comment"><span class="hljs-comment">//从数据库中读取原数据</span></span>  
Role entity = hibernateTemplate.load(ID);  
<span class="hljs-comment"><span class="hljs-comment">//将数据复制到新对象上</span></span>  
Role newEntity = <span class="hljs-built_in"><span class="hljs-built_in">new</span></span> Role();  
BeanCopier.<span class="hljs-built_in"><span class="hljs-built_in">copy</span></span>(entity, newEntity);  
<span class="hljs-comment"><span class="hljs-comment">//删除原数据</span></span>  
hibernateTemplate.<span class="hljs-built_in"><span class="hljs-built_in">delete</span></span>(entity);  
<span class="hljs-comment"><span class="hljs-comment">//保存新数据</span></span>  
hibernateTemplate.save(entity);</code>  

然而这段代码在执行时却失败了,程序抛出:DataIntegrityViolationException异常,日志显示如下错误:

<code class="hljs groovy">SQL <span class="hljs-string"><span class="hljs-string">Error:</span></span> 
<span class="hljs-number"><span class="hljs-number">1062</span></span>, <span class="hljs-string">
<span class="hljs-string">SQLState:</span></span> <span class="hljs-number"><span class="hljs-number">23000</span></span>  
Duplicate entry <span class="hljs-string"><span class="hljs-string">'demo'</span></span> <span class="hljs-keyword">
<span class="hljs-keyword">for</span></span> key <span class="hljs-string">
<span class="hljs-string">'IDX_ROLE_NAME'</span></span></code>  
即数据插入数据库时失败了,原因是触发了唯一性约束。经反复核实,数据库中数据没有问题,不是其他数据导致的,从代码逻辑上讲,这段代码先删后插也没有任何问题,在一个事务当中也没有什么脏读的问题,  这就搞笑了,问题到底出在哪呢???

打开Hibernate的SQL输出,还原最真实的SQL语句,终于找到了答案,输入日志如下:

<code class="hljs sql">Hibernate:   
    <span class="hljs-comment"><span class="hljs-comment">/* insert com.litt.cidp.system.po.Role  
        */</span></span> <span class="hljs-keyword"><span class="hljs-keyword">insert</span></span>   
        <span class="hljs-keyword"><span class="hljs-keyword">into</span></span>  
            <span class="hljs-keyword"><span class="hljs-keyword">role</span></span>  
            (ROLE_NAME, <span class="hljs-keyword"><span class="hljs-keyword">STATUS</span></span>, REMARK)   
        <span class="hljs-keyword"><span class="hljs-keyword">values</span></span>  
            (?, ?, ?)  
Hibernate:   
    <span class="hljs-comment"><span class="hljs-comment">/* update  
        com.litt.cidp.system.po.Role */</span></span> <span class="hljs-keyword"><span class="hljs-keyword">update</span></span>  
            <span class="hljs-keyword"><span class="hljs-keyword">role</span></span>   
        <span class="hljs-keyword"><span class="hljs-keyword">set</span></span>  
            ROLE_NAME=?,  
            <span class="hljs-keyword"><span class="hljs-keyword">STATUS</span></span>=?,  
            REMARK=?   
        <span class="hljs-keyword"><span class="hljs-keyword">where</span></span>  
            ROLE_ID=?  
Hibernate:   
    <span class="hljs-comment"><span class="hljs-comment">/* delete com.litt.cidp.system.po.Role */</span></span> <span class="hljs-keyword"><span class="hljs-keyword">delete</span></span>   
        <span class="hljs-keyword"><span class="hljs-keyword">from</span></span>  
            <span class="hljs-keyword"><span class="hljs-keyword">role</span></span>   
        <span class="hljs-keyword"><span class="hljs-keyword">where</span></span>  
            ROLE_ID=?</code>  

Hibernate在最终执行SQL语句时,居然是按INSERT, UPDATE, DELETE的顺序执行的,而非按照代码顺序执行!What the F!查阅了相关资料,说是Hibernate为了性能优化,对象操作都是放在缓存里而没有立即执行数据库操作直到commit操作,为的是利用batch操作提高数据库操作性能;老天,那业务逻辑怎么办?同理,在一个事务中混用Hibernate和JDBC操作,将导致数据不一致问题

解决方案:在需要同步的地方(即按照INSERT, UPDATE, DELETE顺序有可能产生问题的时候;混用JDBC操作的时候),执行session.flush()方案,该方法将缓存中的数据请求立即转换为数据库操作并执行。


扫描二维码关注公众号,回复: 3480521 查看本文章

eg:如下示例




猜你喜欢

转载自blog.csdn.net/XiaoXiao_RenHe/article/details/80648812