From 3ee36aa4204573493595e5e404cfdc453b88d38e Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Thu, 20 Apr 2017 18:31:00 +0300 Subject: [PATCH] HHH-11186 - Add examples for all Hibernate annotations Document DynamicUpdate annotation --- .../userguide/appendices/Annotations.adoc | 2 +- .../chapters/pc/PersistenceContext.adoc | 77 +++++++++++ ...c-managed-state-dynamic-update-example.sql | 9 ++ .../pc-managed-state-update-example.sql | 15 +++ .../userguide/pc/DynamicUpdateTest.java | 125 ++++++++++++++++++ .../userguide/pc/NoDynamicUpdateTest.java | 122 +++++++++++++++++ 6 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-managed-state-dynamic-update-example.sql create mode 100644 documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-managed-state-update-example.sql create mode 100644 documentation/src/test/java/org/hibernate/userguide/pc/DynamicUpdateTest.java create mode 100644 documentation/src/test/java/org/hibernate/userguide/pc/NoDynamicUpdateTest.java diff --git a/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc b/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc index 54d8440bd4..37a1f7b535 100644 --- a/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc +++ b/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc @@ -734,7 +734,7 @@ The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibern By default, Hibernate uses a cached `UPDATE` statement that sets all table columns. When the entity is annotated with the `@DynamicUpdate` annotation, the `PreparedStatement` is going to include only the columns whose values have been changed. -See the <> section for more info on how `@DynamicUpdate` works. +See the <> section for more info. [NOTE] ==== diff --git a/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc b/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc index dc26017b8b..cec28f8bea 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc @@ -251,6 +251,83 @@ include::{sourcedir}/PersistenceContextTest.java[tags=pc-managed-state-native-ex ---- ==== +By default, when you modify an entity, all columns but the identifier are being set during update. + +Therefore, considering you have the following `Product` entity mapping: + +[[pc-managed-state-update-mapping-example]] +.`Product` entity mapping +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/NoDynamicUpdateTest.java[tags=pc-managed-state-update-mapping-example] +---- +==== + +If you persist the following `Product` entity: + +[[pc-managed-state-update-persist-example]] +.Persisting a `Product` entity +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/NoDynamicUpdateTest.java[tags=pc-managed-state-update-persist-example] +---- +==== + +When you modify the `Product` entity, Hibernate generates the following SQL UPDATE statement: + +[[pc-managed-state-update-example]] +.Modifying the `Product` entity +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/NoDynamicUpdateTest.java[tags=pc-managed-state-update-example] +---- + +[source, SQL, indent=0] +---- +include::{extrasdir}/pc-managed-state-update-example.sql[] +---- +==== + +The default UPDATE statement containing all columns has two advantages: + +- it allows you to better benefit from JDBC Statement caching. +- it allows you to enable batch updates even if multiple entities modify different properties. + +However, there is also one downside to including all columns in the SQL UPDATE statement. +If you have multiple indexes, the database might update those redundantly even if you don't actually modify all column values. + +To fix this issue, you can use dynamic updates. + +[[pc-managed-state-dynamic-update]] +==== Dynamic updates + +To enable dynamic updates, you need to annotate the entity with the `@DynamicUpdate` annotation: + +[[pc-managed-state-dynamic-update-mapping-example]] +.`Product` entity mapping +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/DynamicUpdateTest.java[tags=pc-managed-state-dynamic-update-mapping-example] +---- +==== + +This time, when reruning the previous test case, Hibernate generates the following SQL UPDATE statement: + +[[pc-managed-state-dynamic-update-example]] +.Modifying the `Product` entity with a dynamic update +==== +[source, SQL, indent=0] +---- +include::{extrasdir}/pc-managed-state-dynamic-update-example.sql[] +---- +==== + +The dynamic update allows you to set just the columns sthat were modified in the associated entity. + [[pc-refresh]] === Refresh entity state diff --git a/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-managed-state-dynamic-update-example.sql b/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-managed-state-dynamic-update-example.sql new file mode 100644 index 0000000000..b77fc3a8d8 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-managed-state-dynamic-update-example.sql @@ -0,0 +1,9 @@ +UPDATE + Product +SET + price_cents = ? +WHERE + id = ? + +-- binding parameter [1] as [INTEGER] - [2499] +-- binding parameter [2] as [BIGINT] - [1] \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-managed-state-update-example.sql b/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-managed-state-update-example.sql new file mode 100644 index 0000000000..6773243a66 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-managed-state-update-example.sql @@ -0,0 +1,15 @@ +UPDATE + Product +SET + description = ?, + name = ?, + price_cents = ?, + quantity = ? +WHERE + id = ? + +-- binding parameter [1] as [VARCHAR] - [Get the most out of your persistence layer] +-- binding parameter [2] as [VARCHAR] - [High-Performance Java Persistence] +-- binding parameter [3] as [INTEGER] - [2499] +-- binding parameter [4] as [INTEGER] - [10000] +-- binding parameter [5] as [BIGINT] - [1] \ No newline at end of file diff --git a/documentation/src/test/java/org/hibernate/userguide/pc/DynamicUpdateTest.java b/documentation/src/test/java/org/hibernate/userguide/pc/DynamicUpdateTest.java new file mode 100644 index 0000000000..d17521b62b --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/pc/DynamicUpdateTest.java @@ -0,0 +1,125 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.userguide.pc; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.Target; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.test.annotations.id.entities.Shoe; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +public class DynamicUpdateTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Product.class, + }; + } + + @Test + public void testLifecycle() { + doInJPA( this::entityManagerFactory, entityManager -> { + + Product book = new Product(); + book.setId( 1L ); + book.setName( "High-Performance Java Persistence" ); + book.setDescription( "get the most out of your persistence layer" ); + book.setPriceCents( 29_99 ); + book.setQuantity( 10_000 ); + + entityManager.persist( book ); + } ); + + doInJPA( this::entityManagerFactory, entityManager -> { + + Product book = entityManager.find( Product.class, 1L ); + book.setPriceCents( 24_99 ); + } ); + } + + //tag::pc-managed-state-dynamic-update-mapping-example[] + @Entity(name = "Product") + @DynamicUpdate + public static class Product { + + @Id + private Long id; + + @Column + private String name; + + @Column + private String description; + + @Column(name = "price_cents") + private Integer priceCents; + + @Column + private Integer quantity; + + //Getters and setters are omitted for brevity + + //end::pc-managed-state-dynamic-update-mapping-example[] + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getPriceCents() { + return priceCents; + } + + public void setPriceCents(Integer priceCents) { + this.priceCents = priceCents; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + //tag::pc-managed-state-dynamic-update-mapping-example[] + } + //end::pc-managed-state-dynamic-update-mapping-example[] +} diff --git a/documentation/src/test/java/org/hibernate/userguide/pc/NoDynamicUpdateTest.java b/documentation/src/test/java/org/hibernate/userguide/pc/NoDynamicUpdateTest.java new file mode 100644 index 0000000000..0802b8d629 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/pc/NoDynamicUpdateTest.java @@ -0,0 +1,122 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.userguide.pc; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Vlad Mihalcea + */ +public class NoDynamicUpdateTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Product.class, + }; + } + + @Test + public void testLifecycle() { + doInJPA( this::entityManagerFactory, entityManager -> { + + //tag::pc-managed-state-update-persist-example[] + Product book = new Product(); + book.setId( 1L ); + book.setName( "High-Performance Java Persistence" ); + book.setDescription( "Get the most out of your persistence layer" ); + book.setPriceCents( 29_99 ); + book.setQuantity( 10_000 ); + + entityManager.persist( book ); + //end::pc-managed-state-update-persist-example[] + } ); + + + //tag::pc-managed-state-update-example[] + doInJPA( this::entityManagerFactory, entityManager -> { + Product book = entityManager.find( Product.class, 1L ); + book.setPriceCents( 24_99 ); + } ); + //end::pc-managed-state-update-example[] + } + + //tag::pc-managed-state-update-mapping-example[] + @Entity(name = "Product") + public static class Product { + + @Id + private Long id; + + @Column + private String name; + + @Column + private String description; + + @Column(name = "price_cents") + private Integer priceCents; + + @Column + private Integer quantity; + + //Getters and setters are omitted for brevity + + //end::pc-managed-state-update-mapping-example[] + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getPriceCents() { + return priceCents; + } + + public void setPriceCents(Integer priceCents) { + this.priceCents = priceCents; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + //tag::pc-managed-state-update-mapping-example[] + } + //end::pc-managed-state-update-mapping-example[] +}