• Spring Data JPA使用@DynamicInsert和@DynamicUpdate注解进行动态插入和更新


    当您保留一个新实体或使用 Spring Data JPA 更新现有实体时,您可能已经意识到您始终在执行相同的 SQL 语句,该语句设置该实体映射的所有列。如果您只更新其属性之一,情况也是如此。

    这是Hibernate提供的性能优化,Hibernate是Spring Data JPA默认使用的JPA实现。Hibernate试图避免检查实体的哪些属性已更改并为它们生成特定的SQL语句。相反,它在启动时为每个实体类生成 1 个 SQL UPDATE 和 1 个 SQL INSERT 语句,并在每次插入或更新操作中重用它。

    一遍又一遍地重用相同的语句可以改进 Hibernate 的工作。但它也会产生一些副作用。如果仅更改大型实体类的 1 个属性,则这些语句会产生开销。如果需要审核对数据库表执行的所有更改,它们也会导致问题。在这些情况下,最好让 Hibernate 为每个操作生成一个新的 SQL INSERT 或 UPDATE 语句。

    标准行为

    但在我向您展示如何执行此操作之前,让我们快速浏览一下默认行为。在这里,您可以看到一个简单的ChessPlayer实体,该实体存储每个玩家的名字、姓氏出生日期id属性映射主键,其值由数据库序列生成。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    @Entity

    public class ChessPlayer {

        @Id

        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "player_seq")

        @SequenceGenerator(name = "player_seq", sequenceName = "player_sequence")

        private Long id;

        private String firstName;

         

        private String lastName;

        private LocalDate birthDate;

        ...

    }

    我准备了一个标准存储库,它只扩展了Spring Data JPA的JpaRepository,并且没有添加任何自定义查询或其他功能。

    1

    public interface ChessPlayerRepository extends JpaRepository { }

    我准备了一个测试用例,它可以在不设置他的出生日期属性的情况下保留一个新的ChessPlayer。在下一步中,我将修复名字中的拼写错误。这将触发一个额外的 SQL 更新语句。

    1

    2

    3

    4

    5

    6

    ChessPlayer p = new ChessPlayer();

    p.setFirstName("Torben");

    p.setLastName("Janssen");

    chessPlayerRepository.save(p);

    p.setFirstName("Thorben");

    正如您在日志输出中看到的,Hibernate执行了一个SQL INSERT和一个UPDATE语句来设置Chess_Player表的所有列。这包括设置为 null 的birth_date列。

    1

    2

    3

    4

    11:33:15.505 DEBUG 19836 - – [           main] org.hibernate.SQL                        : select nextval ('player_sequence')

    11:33:15.514 DEBUG 19836 - – [           main] org.hibernate.SQL                        : select nextval ('player_sequence')

    11:33:15.547 DEBUG 19836 - – [           main] org.hibernate.SQL                        : insert into chess_player (birth_date, first_name, last_name, id) values (?, ?, ?, ?)

    11:33:15.557 DEBUG 19836 - – [           main] org.hibernate.SQL                        : update chess_player set birth_date=?, first_name=?, last_name=? where id=?

    冬眠@DynamicInsert

    Spring Data JPA 充当 Hibernate 之上的一层。因此,您可以在实体类上使用Hibernate的所有专有映射注释。

    如果要在持久化新实体对象时动态生成 SQL INSERT 语句,则需要使用 Hibernate 专有@DynamicInsert注释来批注实体类。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    @Entity

    @DynamicInsert

    public class ChessPlayer {

        @Id

        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "player_seq")

        @SequenceGenerator(name = "player_seq", sequenceName = "player_sequence")

        private Long id;

        private String firstName;

         

        private String lastName;

        private LocalDate birthDate;

         

        ...

    }

    然后,当您执行与以前相同的测试用例时,您将在日志输出中看到 Hibernate 仅使用在新实体对象上设置的属性动态生成 SQL INSERT 语句。

    1

    2

    3

    4

    5

    6

    ChessPlayer p = new ChessPlayer();

    p.setFirstName("Torben");

    p.setLastName("Janssen");

    chessPlayerRepository.save(p);

    p.setFirstName("Thorben");

    Hibernate只设置SQL INSERT语句中的idfirst_namelast_name列,而不设置birth_date列。Hibernate排除了该列,因为我在Spring Data JPA的存储库上调用save方法之前没有在测试用例中设置它。

    1

    2

    3

    4

    11:37:20.374 DEBUG 7448 - – [           main] org.hibernate.SQL                        : select nextval ('player_sequence')

    11:37:20.386 DEBUG 7448 - – [           main] org.hibernate.SQL                        : select nextval ('player_sequence')

    11:37:20.427 DEBUG 7448 - – [           main] org.hibernate.SQL                        : insert into chess_player (first_name, last_name, id) values (?, ?, ?)

    11:37:20.435 DEBUG 7448 - – [           main] org.hibernate.SQL                        : update chess_player set birth_date=?, first_name=?, last_name=? where id=?

    但是 SQL UPDATE 语句仍会更新ChessPlayer实体类映射的所有列。如果要更改它,还需要使用 @DynamicUpdate 注释实体类。

    冬眠@DynamicUpdate

    与上一节中描述的@DynamicInsert注解一样,@DynamicUpdate注解告诉Hibernate为每个更新操作生成特定的SQL UPDATE语句。执行此操作时,Hibernate会检测哪些属性已更改,并且仅将这些属性包含在SQL语句中。

    在下面的示例中,我使用 Hibernate 的@DynamicInsert@DynamicUpdate注释注释了ChessPlayer实体。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    @Entity

    @DynamicInsert

    @DynamicUpdate

    public class ChessPlayer {

        @Id

        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "player_seq")

        @SequenceGenerator(name = "player_seq", sequenceName = "player_sequence")

        private Long id;

        private String firstName;

         

        private String lastName;

        private LocalDate birthDate;

         

        ...

    }

    让我们执行与前面示例中相同的测试用例。

    1

    2

    3

    4

    5

    6

    ChessPlayer p = new ChessPlayer();

    p.setFirstName("Torben");

    p.setLastName("Janssen");

    chessPlayerRepository.save(p);

    p.setFirstName("Thorben");

    正如您在日志输出中看到的,Hibernate现在生成了特定的SQL INSERT和UPDATE语句。

    1

    2

    3

    4

    11:39:45.177 DEBUG 13832 - – [           main] org.hibernate.SQL                        : select nextval ('player_sequence')

    11:39:45.185 DEBUG 13832 - – [           main] org.hibernate.SQL                        : select nextval ('player_sequence')

    11:39:45.214 DEBUG 13832 - – [           main] org.hibernate.SQL                        : insert into chess_player (first_name, last_name, id) values (?, ?, ?)

    11:39:45.224 DEBUG 13832 - – [           main] org.hibernate.SQL                        : update chess_player set first_name=? where id=?

    我们已经在上一节中讨论了 INSERT 语句,因此让我们专注于更新操作。

    在测试用例中,我只更改了firstName属性的值。Hibernate在对该实体对象执行脏检查时认识到了这一点。基于此,Hibernate随后生成了一个SQL UPDATE语句,该语句仅更改first_name列中的值。

    结论

    Spring Data JPA充当JPA实现之上的一层。在大多数情况下,这就是休眠。当您持久化或更新实体对象时,Spring Data JPA 将该操作委托给 JPA 实现。因此,所有写入操作的处理和 SQL 语句的生成取决于您的 JPA 实现及其功能。

    默认情况下,Hibernate不会为每个实体对象生成特定的SQL INSERT或UPDATE语句。相反,它在应用程序启动时为每个实体类生成 1 个 INSERT 和 1 个 UPDATE 语句,并在所有插入或更新操作中重用它们。这减少了这些操作的开销,但也更改了数据库中的太多列。

    如果这是一个问题,您可以使用 @DynamicInsert 和 @DynamicUpdate 来批注实体类。这些专有注释告诉Hibernate为每个实体对象动态生成SQL INSERT或UPDATE语句。执行此操作时,请记住,您不会免费获得此功能,也无法针对特定用例激活或停用它。要生成特定的 UPDATE 或 INSERT 语句,Hibernate 需要检测哪些属性发生了变化,并根据此信息生成新的 SQL 语句。这会减慢此实体类对象的所有插入或更新操作。

  • 相关阅读:
    是的,诺基亚还“活着”,并推出了新款平板电脑!
    web前端-html自定义列表
    Go 原子操作有哪些?
    QT -- 多线程 —— moveToThread
    JavaSE中级之Java常用的类
    [ROS]虚拟机ubuntu18.04系统里面运行usb_cam
    Oracle连接和使用
    JS本地存储 sessionStorage和localStorage
    隐藏滚动条样式
    OpenAI-Sora学习手册
  • 原文地址:https://blog.csdn.net/allway2/article/details/127673462