HHH-15080 Session Refresh can cause StaleObjectStateException when entry in 2LC is reread

This commit is contained in:
William Burns 2022-02-09 14:14:29 -05:00 committed by Steve Ebersole
parent 26e98c0879
commit 6685a7a145
2 changed files with 64 additions and 8 deletions

View File

@ -484,7 +484,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( this.entityInstance );
if ( entry != null && entry.getLockMode().lessThan( lockMode ) ) {
//we only check the version when _upgrading_ lock modes
if ( versionAssembler != null ) {
if ( versionAssembler != null && entry.getLockMode() != LockMode.NONE) {
checkVersion( entry, rowProcessingState );
}
//we need to upgrade the lock mode to the mode requested

View File

@ -6,14 +6,12 @@
*/
package org.hibernate.orm.test.cache;
import static org.junit.Assert.assertEquals;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import org.hibernate.Session;
import org.hibernate.annotations.Cache;
@ -21,13 +19,16 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
/**
* @author Zhenlei Huang
@ -131,6 +132,61 @@ public class RefreshUpdatedDataTest extends BaseCoreFunctionalTestCase {
s.close();
}
@Test
public void testExternalUpdateRefresh() {
// prepare data
Session s = openSession();
s.beginTransaction();
final String BEFORE = "before";
ReadWriteVersionedCacheableItem readWriteVersionedCacheableItem = new ReadWriteVersionedCacheableItem( BEFORE );
readWriteVersionedCacheableItem.getTags().add( "Hibernate" );
readWriteVersionedCacheableItem.getTags().add( "ORM" );
s.persist( readWriteVersionedCacheableItem );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
// Read the entry in to populate 2LC
readWriteVersionedCacheableItem = s.get( ReadWriteVersionedCacheableItem.class, readWriteVersionedCacheableItem.getId() );
// open another session
Session s2 = sessionFactory().openSession();
s2.beginTransaction();
// Read from 2LC
readWriteVersionedCacheableItem = s2.get( ReadWriteVersionedCacheableItem.class, readWriteVersionedCacheableItem.getId() );
assertEquals( BEFORE, readWriteVersionedCacheableItem.getName() );
assertEquals( 2, readWriteVersionedCacheableItem.getTags().size() );
// Change the value externally
int version = readWriteVersionedCacheableItem.version + 1;
long id = readWriteVersionedCacheableItem.id;
s.doWork(connection -> {
try (Statement stmt = connection.createStatement()) {
stmt.executeUpdate("UPDATE ReadWriteVersionedCacheableItem SET version = " + version + " WHERE id = " + id);
}
});
s.getTransaction().commit();
s.close();
s2.refresh(readWriteVersionedCacheableItem);
assertEquals( version, readWriteVersionedCacheableItem.version );
assertEquals( 2, readWriteVersionedCacheableItem.getTags().size() );
s2.delete( readWriteVersionedCacheableItem );
s2.getTransaction().commit();
s2.close();
}
@Entity(name = "ReadWriteCacheableItem")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "item")
public static class ReadWriteCacheableItem {