HHH-11186 - Add examples for all Hibernate annotations

Document DynamicUpdate annotation
This commit is contained in:
Vlad Mihalcea 2017-04-20 18:31:00 +03:00
parent 020414e1aa
commit 3ee36aa420
6 changed files with 349 additions and 1 deletions

View File

@ -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. 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. 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 <<chapters/domain/entity.adoc#locking-optimistic-lock-type-dirty-example, `OptimisticLockType.DIRTY` mapping>> section for more info on how `@DynamicUpdate` works. See the <<chapters/pc/PersistenceContext.adoc#pc-managed-state-dynamic-update,`@DynamicUpdate`>> section for more info.
[NOTE] [NOTE]
==== ====

View File

@ -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]] [[pc-refresh]]
=== Refresh entity state === Refresh entity state

View File

@ -0,0 +1,9 @@
UPDATE
Product
SET
price_cents = ?
WHERE
id = ?
-- binding parameter [1] as [INTEGER] - [2499]
-- binding parameter [2] as [BIGINT] - [1]

View File

@ -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]

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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[]
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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[]
}