关于@DynamicUpdate的误解

8,608 阅读3分钟

先看证据


二话不说,先把@DynamicUpdate的官方api-doc呈出来(如下图),参见https://docs.jboss.org/hibernate/stable/orm/javadocs/

翻译成白话文:

  • 对于更新,此实体是否应在使用动态sql生成时,仅在预处理sql语句中引用已更改的列。
  • 请注意,对于重新附加分离的实体,如果未启用select-before-update,则无法执行此操作。

举栗子


先说明一下@DynamicUpdate注解用在实体类上

有数据库表tbl_foo和对应的实体类Foo,如下:

id name col3 col4 col5
1 毕加索 1770 奥地利
class Foo{
    private Integer id;
    private String name;
    private String col3;
    private String col4;
    private String col5;
    getter...
    setter...
}

在Service中更新id=1记录的name属性,代码如下:

Foo foo = fooDao.findById(1);
foo.setName("贝多芬");
fooDao.save(foo);

两种情况:

  1. 在entity类中未使用@DynamicUpdate注解或使用了@DynamicUpdate(false),那么Hibernate底层执行的sql如:
update tbl_foo set name=?, col3=?, col4=?, col5=? where id=?
  1. 在entity类中使用了@DynamicUpdate注解(或@DynamicUpdate(true)),Hibernate底层执行的sql如:
update tbl_foo set name=? where id=?

以上两种情况对数据库更新的结果是等效的,但是使用@DynamicUpdate性能会好一些。因为不使用@DynamicUpdate时,即使没有改变的字段也会被更新。如果进行频繁的更新操作,并且每次只更新少数字段,那么@DynamicUpdate对性能的优化效果还是很好的。

误解


我们常有一种需求,web层用对象接收前端要修改的属性值(不修改的值为空),我们希望直接调用dao的update方法进行选择更新。

web层使用对象接收前端要修改的属性值,可等效看做执行如下代码:

Foo foo = new Foo();
foo.setId(1);
foo.setName("贝多芬");

这时服务层直接使用这个对象进行更新

fooDao.update(foo);

我们期待的结果:

id name col3 col4 col5
1 贝多芬 1770 奥地利

看到网上有些文章说在实体类加上@DynamicUpdate,就可以满足我们的以上需求,但是很遗憾,不然!

反而得到这样的结果是:

id name col3 col4 col5
1 贝多芬

只要完全理解了api-doc中对@DynamicUpdat的说明,就很容易知道得到这个结果的原因了。@DynamicUpdate的动态更新的含义是,比较更新要使用的实体类中的字段值与从数据库中查询出来的字段值,判断其是否有修改。看这个例子,数据库中id=1的记录所有字段都是非空的,但是实体类中只有name有值,也就是所有字段都变了,只是其他字段被更新为了新的空值。

闲言碎语(一些废话可以忽略)


  • 学习一个注解(或者类)的用法,不管是开源的还是商业的,最简单、最靠谱、最直接的途径就是一手的官方api文档。

  • 有些小哥哥在使用Spring Data JPA时用到了@DynamicUpdate,就理算当然的认为它和Spring是一家,其实不然。其实从引用注解时的包名就能看出来,很明显Hibernate才是它的原配。原因当然就是Spring Data JPA使用的ORM实现是Hibernate。所以也就解释了上图中的文档是Hibernate的api文档,而不是Spring或者Java的api文档。

以上全部为个人观点,希望对你有帮助。本人水平有限,难免有不妥或者谬误之处,望大家指出。有其他问题也欢迎和大家交流!