HHH-11186 - Add examples for all Hibernate annotations
Document DynamicUpdate annotation
This commit is contained in:
parent
020414e1aa
commit
3ee36aa420
|
@ -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]
|
||||||
====
|
====
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
UPDATE
|
||||||
|
Product
|
||||||
|
SET
|
||||||
|
price_cents = ?
|
||||||
|
WHERE
|
||||||
|
id = ?
|
||||||
|
|
||||||
|
-- binding parameter [1] as [INTEGER] - [2499]
|
||||||
|
-- binding parameter [2] as [BIGINT] - [1]
|
|
@ -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]
|
|
@ -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[]
|
||||||
|
}
|
|
@ -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[]
|
||||||
|
}
|
Loading…
Reference in New Issue