Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@
*/
public class EntityIncrementVersionProcess implements BeforeTransactionCompletionProcess {
private final Object object;
private final EntityEntry entry;

/**
* Constructs an EntityIncrementVersionProcess for the given entity.
*
* @param object The entity instance
* @param entry The entity's EntityEntry reference
*/
public EntityIncrementVersionProcess(Object object, EntityEntry entry) {
public EntityIncrementVersionProcess(Object object) {
this.object = object;
this.entry = entry;
}

/**
Expand All @@ -39,6 +36,12 @@ public EntityIncrementVersionProcess(Object object, EntityEntry entry) {
*/
@Override
public void doBeforeTransactionCompletion(SessionImplementor session) {
final EntityEntry entry = session.getPersistenceContext().getEntry( object );
// Don't increment version for an entity that is not in the PersistenceContext;
if ( entry == null ) {
return;
}

final EntityPersister persister = entry.getPersister();
final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );
entry.forceLocked( object, nextVersion );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,25 @@
*/
public class EntityVerifyVersionProcess implements BeforeTransactionCompletionProcess {
private final Object object;
private final EntityEntry entry;

/**
* Constructs an EntityVerifyVersionProcess
*
* @param object The entity instance
* @param entry The entity's referenced EntityEntry
*/
public EntityVerifyVersionProcess(Object object, EntityEntry entry) {
public EntityVerifyVersionProcess(Object object) {
this.object = object;
this.entry = entry;
}

@Override
public void doBeforeTransactionCompletion(SessionImplementor session) {
final EntityPersister persister = entry.getPersister();

if ( !entry.isExistsInDatabase() ) {
// HHH-9419: We cannot check for a version of an entry we ourselves deleted
final EntityEntry entry = session.getPersistenceContext().getEntry( object );
// Don't check version for an entity that is not in the PersistenceContext;
if ( entry == null ) {
return;
}

final EntityPersister persister = entry.getPersister();
final Object latestVersion = persister.getCurrentVersion( entry.getId(), session );
if ( !entry.getVersion().equals( latestVersion ) ) {
throw new OptimisticLockException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void lock(Serializable id, Object version, Object object, int timeout, Sh
}
final EntityEntry entry = session.getPersistenceContext().getEntry( object );
// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.
( (EventSource) session ).getActionQueue().registerProcess( new EntityIncrementVersionProcess( object, entry ) );
( (EventSource) session ).getActionQueue().registerProcess( new EntityIncrementVersionProcess( object ) );
}

protected LockMode getLockMode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.hibernate.LockMode;
import org.hibernate.OptimisticLockException;
import org.hibernate.action.internal.EntityVerifyVersionProcess;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.persister.entity.Lockable;
Expand Down Expand Up @@ -49,9 +48,8 @@ public void lock(Serializable id, Object version, Object object, int timeout, Sh
if ( !lockable.isVersioned() ) {
throw new OptimisticLockException( object, "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
}
final EntityEntry entry = session.getPersistenceContext().getEntry( object );
// Register the EntityVerifyVersionProcess action to run just prior to transaction commit.
( (EventSource) session ).getActionQueue().registerProcess( new EntityVerifyVersionProcess( object, entry ) );
( (EventSource) session ).getActionQueue().registerProcess( new EntityVerifyVersionProcess( object ) );
}

protected LockMode getLockMode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ public void onPostLoad(PostLoadEvent event) {
entry.forceLocked( entity, nextVersion );
}
else if ( LockMode.OPTIMISTIC_FORCE_INCREMENT.equals( lockMode ) ) {
final EntityIncrementVersionProcess incrementVersion = new EntityIncrementVersionProcess( entity, entry );
final EntityIncrementVersionProcess incrementVersion = new EntityIncrementVersionProcess( entity );
event.getSession().getActionQueue().registerProcess( incrementVersion );
}
else if ( LockMode.OPTIMISTIC.equals( lockMode ) ) {
final EntityVerifyVersionProcess verifyVersion = new EntityVerifyVersionProcess( entity, entry );
final EntityVerifyVersionProcess verifyVersion = new EntityVerifyVersionProcess( entity );
event.getSession().getActionQueue().registerProcess( verifyVersion );
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* 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.test.locking;

import java.util.Arrays;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.LockModeType;
import javax.persistence.Version;

import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.testing.junit4.CustomParameterized;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

/**
* @author Gail Badner
*/

@TestForIssue(jiraKey = "HHH-13492")
@RunWith(CustomParameterized.class)
public class LockRefreshTest extends BaseNonConfigCoreFunctionalTestCase {
private final LockModeType lockModeType;

@Parameterized.Parameters(name = "JpaComplianceCachingSetting={0}")
public static Iterable<Object[]> parameters() {
return Arrays.asList(
new Object[][] {
{ LockModeType.OPTIMISTIC },
{ LockModeType.OPTIMISTIC_FORCE_INCREMENT }
}
);
}

public LockRefreshTest(LockModeType lockModeType) {
this.lockModeType = lockModeType;
}

@Test
public void testLockRefreshUpdate() {
doInHibernate(
this::sessionFactory, session -> {
final Employee employee = session.get( Employee.class, "Jane" );
session.lock( employee, lockModeType );
session.refresh( employee );
employee.department = "Finance";
}
);

doInHibernate(
this::sessionFactory, session -> {
final Employee employee = session.get( Employee.class, "Jane" );
assertEquals( "Finance", employee.department );
}
);
}

@Test
public void testLockRefreshMerge() {
doInHibernate(
this::sessionFactory, session -> {
final Employee employee = session.get( Employee.class, "Jane" );
session.lock( employee, lockModeType );
session.refresh( employee );
employee.department = "Finance";
session.merge( employee );
}
);

doInHibernate(
this::sessionFactory, session -> {
final Employee employee = session.get( Employee.class, "Jane" );
assertEquals( "Finance", employee.department );
}
);
}

@Test
public void testLockRefreshDelete() {
doInHibernate(
this::sessionFactory, session -> {
final Employee employee = session.get( Employee.class, "Jane" );
session.lock( employee, lockModeType );
session.refresh( employee );
session.delete( employee );
}
);

doInHibernate(
this::sessionFactory, session -> {
assertNull( session.get( Employee.class, "Jane" ) );
}
);
}

@Test
public void testLockRefreshEvict() {
doInHibernate(
this::sessionFactory, session -> {
final Employee employee = session.get( Employee.class, "Jane" );
session.lock( employee, lockModeType );
session.refresh( employee );
employee.department = "Finance";
session.evict( employee );
}
);

doInHibernate(
this::sessionFactory, session -> {
final Employee employee = session.get( Employee.class, "Jane" );
assertEquals( "Software Engineering", employee.department );
}
);
}

@Override
public void prepareTest() {
final Employee employee = new Employee();
employee.name = "Jane";
employee.department = "Software Engineering";

doInHibernate(
this::sessionFactory, session -> {
session.persist( employee );
}
);
}

protected boolean isCleanupTestDataRequired() {
return true;
}

@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Employee.class };
}

@Entity(name = "Employee")
public static class Employee {
@Id
private String name;

private String department;

@Version
@Column(name = "ver")
private int version;
}
}