HHH-11186 - Add examples for all Hibernate annotations
Document @OptimisticLock annotation
This commit is contained in:
parent
378009d5e1
commit
6f35871266
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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[]
|
||||
}
|
Loading…
Reference in New Issue