HHH-11186 - Add examples for all Hibernate annotations

Document @OptimisticLock annotation
This commit is contained in:
Vlad Mihalcea 2017-05-25 18:37:22 +03:00
parent 378009d5e1
commit 6f35871266
4 changed files with 184 additions and 1 deletions

View File

@ -1054,7 +1054,7 @@ See the <<chapters/pc/PersistenceContext.adoc#pc-cascade-on-delete, `@OnDelete`
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/OptimisticLock.html[`@OptimisticLock`] annotation is used to specify if the current annotated attribute will trigger an entity version increment upon being modified.
//TODO: Add example
See the <<chapters/locking/Locking.adoc#locking-optimistic-exclude-attribute, Excluding attributes>> section for more info.
[[annotations-hibernate-optimisticlocking]]
==== `@OptimisticLocking`

View File

@ -142,6 +142,51 @@ include::{extrasdir}/locking-optimistic-version-timestamp-source-persist-example
----
====
[[locking-optimistic-exclude-attribute]]
===== Excluding attributes
By default, every entity attribute modification is going to trigger a version incrementation.
If there is an entity property which should not bump up the entity version,
then you need to annotate it with the Hibernate https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/OptimisticLock.html[`@OptimisticLock`] annotation,
as illustrated in the following example.
[[locking-optimistic-exclude-attribute-mapping-example]]
.@OptimisticLock mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/OptimisticLockTest.java[tags=locking-optimistic-exclude-attribute-mapping-example]
----
====
This way, if one tread modifies the `Phone` number while a second thread increments the `callCount` attribute,
the two concurrent transactions are not going to conflict as illustrated by the following example.
[[locking-optimistic-exclude-attribute-example]]
.@OptimisticLock exlude attribute example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/OptimisticLockTest.java[tags=locking-optimistic-exclude-attribute-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/locking-optimistic-exclude-attribute-example.sql[]
----
====
When Bob changes the `Phone` entity `callCount`, the entity version is not bumped up.
That's why Alice's UPDATE succeeds since the entity version is still 0, even if Bob has changed the record
since Alice loaded it.
[WARNING]
====
Although there is no conflict between Bob and Alice, Alice's UPDATE overrides Bob's change to the `callCount` attribute.
For this reason, you should only use this feature if you can accommodate lost updates on the excluded entity properties.
====
[[locking-optimistic-versionless]]
===== Versionless optimistic locking

View File

@ -0,0 +1,23 @@
-- Bob changes the Phone call count
update
Phone
set
callCount = 1,
"number" = '123-456-7890',
version = 0
where
id = 1
and version = 0
-- Alice changes the Phone number
update
Phone
set
callCount = 0,
"number" = '+123-456-7890',
version = 1
where
id = 1
and version = 0

View File

@ -0,0 +1,115 @@
/*
* 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.locking;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Version;
import org.hibernate.annotations.OptimisticLock;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class OptimisticLockTest extends BaseEntityManagerFunctionalTestCase {
private static final Logger log = Logger.getLogger( OptimisticLockTest.class );
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Phone.class
};
}
@Test
public void test() {
doInJPA( this::entityManagerFactory, entityManager -> {
Phone phone = new Phone();
phone.setId( 1L );
phone.setNumber( "123-456-7890" );
entityManager.persist( phone );
return phone;
} );
//tag::locking-optimistic-exclude-attribute-example[]
doInJPA( this::entityManagerFactory, entityManager -> {
Phone phone = entityManager.find( Phone.class, 1L );
phone.setNumber( "+123-456-7890" );
doInJPA( this::entityManagerFactory, _entityManager -> {
Phone _phone = _entityManager.find( Phone.class, 1L );
_phone.incrementCallCount();
log.info( "Bob changes the Phone call count" );
} );
log.info( "Alice changes the Phone number" );
} );
//end::locking-optimistic-exclude-attribute-example[]
}
//tag::locking-optimistic-exclude-attribute-mapping-example[]
@Entity(name = "Phone")
public static class Phone {
@Id
private Long id;
@Column(name = "`number`")
private String number;
@OptimisticLock( excluded = true )
private long callCount;
@Version
private Long version;
//Getters and setters are omitted for brevity
//end::locking-optimistic-exclude-attribute-mapping-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Long getVersion() {
return version;
}
public long getCallCount() {
return callCount;
}
//tag::locking-optimistic-exclude-attribute-mapping-example[]
public void incrementCallCount() {
this.callCount++;
}
}
//end::locking-optimistic-exclude-attribute-mapping-example[]
}