Hibernate flush

247 阅读3分钟

问题

先看伪代码:

事务方法(){
查询sql1
插入sql2
查询sql3
}

插入sql2执行的时候,并没有真正的插入数据到数据库——而是在缓存。

什么缓存?hibernate的缓存。

说白了就是,虽然执行了插入sql2这行代码,但是数据并没有真正的插入到数据库——本质是并没有在数据库执行插入sql2。这个时候,数据只是在缓存。

本质,其实就是,插入sql,在这个时候,还没有在数据库执行——也就是说,这个时候插入sql只是在hibernate的缓存。


那何时在数据库真正执行呢?

执行下一个查询的时候,即执行查询sql3的时候,触发了插入sql2在数据库真正的执行。


那这样会有什么问题呢?

问题在于异常的时候,具体来说是,插入sql2异常(比如,违反唯一约束,或者数据长度太大)的时候,正常情况下,应该是回滚的。

但是,如果代码是这个样子:

事务方法(){
查询sql1
插入sql2


try{
  查询sql3
}catch(){
  记录日志
}


}

那么,执行查询sql3的时候,会触发在数据库真正执行插入sql2——但是,这个时候,插入sql2异常了。

问题来了,这个时候,插入异常会被捕获,只是记录日志,导致事务没有回滚。

也就是说,异常被catch住了,没有抛出,导致事务没有回滚。

原因

插入异常被catch,导致事务没有回滚。

从而,引发业务问题。

解决方法

在插入sql2的后面,立即写到数据库。

具体解决方法?flush。即hibernate的flush方法。

flush,会把缓存里的数据刷到数据库。说白了,本质其实就是让插入sql,在数据库真正执行。

现在的伪代码如下:

事务方法(){
查询sql1
插入sql2
flush();


try{
  查询sql3
}catch(){
  记录日志
}


}

加了flush之后,就会立即执行在数据库执行插入sql2,如果异常,就直接抛出异常了,事务也会回滚——也就不会引发后续的业务问题。

插入sql2,何时在数据库真正执行?

情况1-提交事务

如果上面的例子,没有查询sql3,那么就是提交事务的时候,真正在数据库执行插入sql2。

情况2-下一个查询语句

如果后面还有查询sql3,那么执行查询sql3的时候,就会触发插入sql2真正的在数据库执行。

也就是说,如果后面还有查询,那么就是下一个查询语句会触发插入语句在数据库真正的执行。为什么会这样呢?因为查询语句需要从数据库查询数据,前面的插入语句如果不插入数据到数据库,后面的查询语句就查询不出来数据了。所以,在后面的查询语句执行的时候,会先触发之前的插入语句在数据库真正执行,然后才从数据库查询数据得到查询结果。