HHH-10649 - When 2LC enabled, flush session and then refresh entity cause dirty read in another session / transaction
This commit is contained in:
parent
a7694b5225
commit
52719397b3
|
@ -13,18 +13,22 @@ import java.util.Map;
|
|||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.PersistentObjectException;
|
||||
import org.hibernate.UnresolvableObjectException;
|
||||
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
|
||||
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.access.SoftLock;
|
||||
import org.hibernate.engine.internal.Cascade;
|
||||
import org.hibernate.engine.internal.CascadePoint;
|
||||
import org.hibernate.engine.spi.CascadingActions;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.RefreshEvent;
|
||||
import org.hibernate.event.spi.RefreshEventListener;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.type.CollectionType;
|
||||
|
@ -136,17 +140,31 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
|||
}
|
||||
|
||||
if ( persister.hasCache() ) {
|
||||
Object previousVersion = null;
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
// we need to grab the version value from the entity, otherwise
|
||||
// we have issues with generated-version entities that may have
|
||||
// multiple actions queued during the same flush
|
||||
previousVersion = persister.getVersion( object );
|
||||
}
|
||||
final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
|
||||
Object ck = cache.generateCacheKey(
|
||||
final Object ck = cache.generateCacheKey(
|
||||
id,
|
||||
persister,
|
||||
source.getFactory(),
|
||||
source.getTenantIdentifier()
|
||||
);
|
||||
cache.evict( ck );
|
||||
final SoftLock lock = cache.lockItem( source, ck, previousVersion );
|
||||
source.getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
|
||||
@Override
|
||||
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
||||
cache.unlockItem( session, ck, lock );
|
||||
}
|
||||
} );
|
||||
cache.remove( source, ck );
|
||||
}
|
||||
|
||||
evictCachedCollections( persister, id, source.getFactory() );
|
||||
evictCachedCollections( persister, id, source );
|
||||
|
||||
String previousFetchProfile = source.getLoadQueryInfluencers().getInternalFetchProfile();
|
||||
source.getLoadQueryInfluencers().setInternalFetchProfile( "refresh" );
|
||||
|
@ -168,19 +186,36 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
|||
|
||||
}
|
||||
|
||||
private void evictCachedCollections(EntityPersister persister, Serializable id, SessionFactoryImplementor factory) {
|
||||
evictCachedCollections( persister.getPropertyTypes(), id, factory );
|
||||
private void evictCachedCollections(EntityPersister persister, Serializable id, EventSource source) {
|
||||
evictCachedCollections( persister.getPropertyTypes(), id, source );
|
||||
}
|
||||
|
||||
private void evictCachedCollections(Type[] types, Serializable id, SessionFactoryImplementor factory)
|
||||
private void evictCachedCollections(Type[] types, Serializable id, EventSource source)
|
||||
throws HibernateException {
|
||||
for ( Type type : types ) {
|
||||
if ( type.isCollectionType() ) {
|
||||
factory.getCache().evictCollection( ( (CollectionType) type ).getRole(), id );
|
||||
CollectionPersister collectionPersister = source.getFactory().getCollectionPersister( ( (CollectionType) type ).getRole() );
|
||||
if ( collectionPersister.hasCache() ) {
|
||||
final CollectionRegionAccessStrategy cache = collectionPersister.getCacheAccessStrategy();
|
||||
final Object ck = cache.generateCacheKey(
|
||||
id,
|
||||
collectionPersister,
|
||||
source.getFactory(),
|
||||
source.getTenantIdentifier()
|
||||
);
|
||||
final SoftLock lock = cache.lockItem( source, ck, null );
|
||||
source.getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
|
||||
@Override
|
||||
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
||||
cache.unlockItem( session, ck, lock );
|
||||
}
|
||||
} );
|
||||
cache.remove( source, ck );
|
||||
}
|
||||
}
|
||||
else if ( type.isComponentType() ) {
|
||||
CompositeType actype = (CompositeType) type;
|
||||
evictCachedCollections( actype.getSubtypes(), id, factory );
|
||||
evictCachedCollections( actype.getSubtypes(), id, source );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,14 @@ import org.junit.Test;
|
|||
import org.hibernate.Session;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.persister.entity.Lockable;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for handling of data just inserted during a transaction being read from the database
|
||||
|
@ -164,7 +168,9 @@ public class InsertedDataTest extends BaseCoreFunctionalTestCase {
|
|||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 0, cacheMap.size() );
|
||||
assertEquals( 1, cacheMap.size() );
|
||||
Object lock = cacheMap.values().iterator().next();
|
||||
assertEquals( "org.hibernate.testing.cache.AbstractReadWriteAccessStrategy$Lock", lock.getClass().getName() );
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
|
|
345
hibernate-core/src/test/java/org/hibernate/test/cache/RefreshUpdatedDataTest.java
vendored
Normal file
345
hibernate-core/src/test/java/org/hibernate/test/cache/RefreshUpdatedDataTest.java
vendored
Normal file
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* 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.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.Cache;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Zhenlei Huang
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-10649")
|
||||
@RequiresDialect(value = {H2Dialect.class})
|
||||
public class RefreshUpdatedDataTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
ReadWriteCacheableItem.class,
|
||||
ReadWriteVersionedCacheableItem.class,
|
||||
NonStrictReadWriteCacheableItem.class,
|
||||
NonStrictReadWriteVersionedCacheableItem.class,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration cfg) {
|
||||
super.configure( cfg );
|
||||
Properties properties = Environment.getProperties();
|
||||
if ( H2Dialect.class.getName().equals( properties.get( Environment.DIALECT ) ) ) {
|
||||
cfg.setProperty( Environment.URL, "jdbc:h2:mem:db-mvcc;MVCC=true" );
|
||||
}
|
||||
cfg.setProperty( Environment.CACHE_REGION_PREFIX, "" );
|
||||
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" );
|
||||
cfg.setProperty( Environment.CACHE_PROVIDER_CONFIG, "true" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAndFlushThenRefresh() {
|
||||
// prepare data
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
final String BEFORE = "before";
|
||||
|
||||
ReadWriteCacheableItem readWriteCacheableItem = new ReadWriteCacheableItem( BEFORE );
|
||||
readWriteCacheableItem.getTags().add( "Hibernate" );
|
||||
readWriteCacheableItem.getTags().add( "ORM" );
|
||||
s.persist( readWriteCacheableItem );
|
||||
|
||||
ReadWriteVersionedCacheableItem readWriteVersionedCacheableItem = new ReadWriteVersionedCacheableItem( BEFORE );
|
||||
readWriteVersionedCacheableItem.getTags().add( "Hibernate" );
|
||||
readWriteVersionedCacheableItem.getTags().add( "ORM" );
|
||||
s.persist( readWriteVersionedCacheableItem );
|
||||
|
||||
NonStrictReadWriteCacheableItem nonStrictReadWriteCacheableItem = new NonStrictReadWriteCacheableItem( BEFORE );
|
||||
nonStrictReadWriteCacheableItem.getTags().add( "Hibernate" );
|
||||
nonStrictReadWriteCacheableItem.getTags().add( "ORM" );
|
||||
s.persist( nonStrictReadWriteCacheableItem );
|
||||
|
||||
NonStrictReadWriteVersionedCacheableItem nonStrictReadWriteVersionedCacheableItem = new NonStrictReadWriteVersionedCacheableItem( BEFORE );
|
||||
nonStrictReadWriteVersionedCacheableItem.getTags().add( "Hibernate" );
|
||||
nonStrictReadWriteVersionedCacheableItem.getTags().add( "ORM" );
|
||||
s.persist( nonStrictReadWriteVersionedCacheableItem );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
Session s1 = openSession();
|
||||
s1.beginTransaction();
|
||||
|
||||
final String AFTER = "after";
|
||||
|
||||
ReadWriteCacheableItem readWriteCacheableItem1 = s1.get( ReadWriteCacheableItem.class, readWriteCacheableItem.getId() );
|
||||
readWriteCacheableItem1.setName( AFTER );
|
||||
readWriteCacheableItem1.getTags().remove("ORM");
|
||||
|
||||
ReadWriteVersionedCacheableItem readWriteVersionedCacheableItem1 = s1.get( ReadWriteVersionedCacheableItem.class, readWriteVersionedCacheableItem.getId() );
|
||||
readWriteVersionedCacheableItem1.setName( AFTER );
|
||||
readWriteVersionedCacheableItem1.getTags().remove("ORM");
|
||||
|
||||
NonStrictReadWriteCacheableItem nonStrictReadWriteCacheableItem1 = s1.get( NonStrictReadWriteCacheableItem.class, nonStrictReadWriteCacheableItem.getId() );
|
||||
nonStrictReadWriteCacheableItem1.setName( AFTER );
|
||||
nonStrictReadWriteCacheableItem1.getTags().remove("ORM");
|
||||
|
||||
NonStrictReadWriteVersionedCacheableItem nonStrictReadWriteVersionedCacheableItem1 = s1.get( NonStrictReadWriteVersionedCacheableItem.class, nonStrictReadWriteVersionedCacheableItem.getId() );
|
||||
nonStrictReadWriteVersionedCacheableItem1.setName( AFTER );
|
||||
nonStrictReadWriteVersionedCacheableItem1.getTags().remove("ORM");
|
||||
|
||||
s1.flush();
|
||||
s1.refresh( readWriteCacheableItem1 );
|
||||
s1.refresh( readWriteVersionedCacheableItem1 );
|
||||
s1.refresh( nonStrictReadWriteCacheableItem1 );
|
||||
s1.refresh( nonStrictReadWriteVersionedCacheableItem1 );
|
||||
|
||||
assertEquals( AFTER, readWriteCacheableItem1.getName() );
|
||||
assertEquals( 1, readWriteCacheableItem1.getTags().size() );
|
||||
assertEquals( AFTER, readWriteVersionedCacheableItem1.getName() );
|
||||
assertEquals( 1, readWriteVersionedCacheableItem1.getTags().size() );
|
||||
assertEquals( AFTER, nonStrictReadWriteCacheableItem1.getName() );
|
||||
assertEquals( 1, nonStrictReadWriteCacheableItem1.getTags().size() );
|
||||
assertEquals( AFTER, nonStrictReadWriteVersionedCacheableItem1.getName() );
|
||||
assertEquals( 1, nonStrictReadWriteVersionedCacheableItem1.getTags().size() );
|
||||
|
||||
// open another session
|
||||
Session s2 = sessionFactory().openSession();
|
||||
try {
|
||||
s2.beginTransaction();
|
||||
ReadWriteCacheableItem readWriteCacheableItem2 = s2.get( ReadWriteCacheableItem.class, readWriteCacheableItem.getId() );
|
||||
ReadWriteVersionedCacheableItem readWriteVersionedCacheableItem2 = s2.get( ReadWriteVersionedCacheableItem.class, readWriteVersionedCacheableItem.getId() );
|
||||
NonStrictReadWriteCacheableItem nonStrictReadWriteCacheableItem2 = s2.get( NonStrictReadWriteCacheableItem.class, nonStrictReadWriteCacheableItem.getId() );
|
||||
NonStrictReadWriteVersionedCacheableItem nonStrictReadWriteVersionedCacheableItem2 = s2.get( NonStrictReadWriteVersionedCacheableItem.class, nonStrictReadWriteVersionedCacheableItem.getId() );
|
||||
|
||||
assertEquals( BEFORE, readWriteCacheableItem2.getName() );
|
||||
assertEquals( 2, readWriteCacheableItem2.getTags().size() );
|
||||
assertEquals( BEFORE, readWriteVersionedCacheableItem2.getName() );
|
||||
assertEquals( 2, readWriteVersionedCacheableItem2.getTags().size() );
|
||||
|
||||
//READ_UNCOMMITTED because there is no locking to prevent collections from being cached in the first Session
|
||||
|
||||
assertEquals( BEFORE, nonStrictReadWriteCacheableItem2.getName() );
|
||||
assertEquals( 1, nonStrictReadWriteCacheableItem2.getTags().size());
|
||||
assertEquals( BEFORE, nonStrictReadWriteVersionedCacheableItem2.getName() );
|
||||
assertEquals( 1, nonStrictReadWriteVersionedCacheableItem2.getTags().size() );
|
||||
|
||||
s2.getTransaction().commit();
|
||||
}
|
||||
finally {
|
||||
if ( s2.getTransaction().getStatus().canRollback() ) {
|
||||
s2.getTransaction().rollback();
|
||||
}
|
||||
s2.close();
|
||||
}
|
||||
|
||||
s1.getTransaction().rollback();
|
||||
s1.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.delete( readWriteCacheableItem );
|
||||
s.delete( readWriteVersionedCacheableItem );
|
||||
s.delete( nonStrictReadWriteCacheableItem );
|
||||
s.delete( nonStrictReadWriteVersionedCacheableItem );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Entity(name = "ReadWriteCacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "item")
|
||||
public static class ReadWriteCacheableItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||
@ElementCollection
|
||||
private List<String> tags = new ArrayList<String>();
|
||||
|
||||
public ReadWriteCacheableItem() {
|
||||
}
|
||||
|
||||
public ReadWriteCacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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 List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "ReadWriteVersionedCacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "item")
|
||||
public static class ReadWriteVersionedCacheableItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||
@ElementCollection
|
||||
private List<String> tags = new ArrayList<String>();
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public ReadWriteVersionedCacheableItem() {
|
||||
}
|
||||
|
||||
public ReadWriteVersionedCacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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 List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "NonStrictReadWriteCacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "item")
|
||||
public static class NonStrictReadWriteCacheableItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
@ElementCollection
|
||||
private List<String> tags = new ArrayList<String>();
|
||||
|
||||
public NonStrictReadWriteCacheableItem() {
|
||||
}
|
||||
|
||||
public NonStrictReadWriteCacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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 List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "NonStrictReadWriteVersionedCacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "item")
|
||||
public static class NonStrictReadWriteVersionedCacheableItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
@ElementCollection
|
||||
private List<String> tags = new ArrayList<String>();
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public NonStrictReadWriteVersionedCacheableItem() {
|
||||
}
|
||||
|
||||
public NonStrictReadWriteVersionedCacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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 List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
}
|
277
hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/functional/InsertedDataTest.java
vendored
Normal file
277
hibernate-ehcache/src/test/java/org/hibernate/test/cache/ehcache/functional/InsertedDataTest.java
vendored
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* 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.cache.ehcache.functional;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* Tests for handling of data just inserted during a transaction being read from the database
|
||||
* and placed into cache. Initially these cases went through putFromRead which causes problems because it
|
||||
* loses the context of that data having just been read.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class InsertedDataTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {CacheableItem.class};
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void addSettings(Map settings) {
|
||||
super.addSettings( settings );
|
||||
settings.put( AvailableSettings.CACHE_REGION_PREFIX, "" );
|
||||
settings.put( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.configure( "hibernate-config/hibernate.cfg.xml" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsert() {
|
||||
sessionFactory().getCache().evictEntityRegions();
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
CacheableItem item = new CacheableItem( "data" );
|
||||
s.save( item );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 1, cacheMap.size() );
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete CacheableItem" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithRollback() {
|
||||
sessionFactory().getCache().evictEntityRegions();
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
CacheableItem item = new CacheableItem( "data" );
|
||||
s.save( item );
|
||||
s.flush();
|
||||
s.getTransaction().rollback();
|
||||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 0, cacheMap.size() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertThenUpdate() {
|
||||
sessionFactory().getCache().evictEntityRegions();
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
CacheableItem item = new CacheableItem( "data" );
|
||||
s.save( item );
|
||||
s.flush();
|
||||
item.setName( "new data" );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 1, cacheMap.size() );
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete CacheableItem" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertThenUpdateThenRollback() {
|
||||
sessionFactory().getCache().evictEntityRegions();
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
CacheableItem item = new CacheableItem( "data" );
|
||||
s.save( item );
|
||||
s.flush();
|
||||
item.setName( "new data" );
|
||||
s.getTransaction().rollback();
|
||||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 0, cacheMap.size() );
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete CacheableItem" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithRefresh() {
|
||||
sessionFactory().getCache().evictEntityRegions();
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
CacheableItem item = new CacheableItem( "data" );
|
||||
s.save( item );
|
||||
s.flush();
|
||||
s.refresh( item );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 1, cacheMap.size() );
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete CacheableItem" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithRefreshThenRollback() {
|
||||
sessionFactory().getCache().evictEntityRegions();
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
CacheableItem item = new CacheableItem( "data" );
|
||||
s.save( item );
|
||||
s.flush();
|
||||
s.refresh( item );
|
||||
s.getTransaction().rollback();
|
||||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 1, cacheMap.size() );
|
||||
Object lock = cacheMap.values().iterator().next();
|
||||
assertEquals( "org.hibernate.cache.ehcache.internal.strategy.AbstractReadWriteEhcacheAccessStrategy$Lock", lock.getClass().getName() );
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
item = (CacheableItem) s.get( CacheableItem.class, item.getId() );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
assertNull( "it should be null", item );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithClear() {
|
||||
sessionFactory().getCache().evictEntityRegions();
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
CacheableItem item = new CacheableItem( "data" );
|
||||
s.save( item );
|
||||
s.flush();
|
||||
s.clear();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 1, cacheMap.size() );
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete CacheableItem" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithClearThenRollback() {
|
||||
sessionFactory().getCache().evictEntityRegions();
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
CacheableItem item = new CacheableItem( "data" );
|
||||
s.save( item );
|
||||
s.flush();
|
||||
s.clear();
|
||||
item = (CacheableItem) s.get( CacheableItem.class, item.getId() );
|
||||
s.getTransaction().rollback();
|
||||
s.close();
|
||||
|
||||
Map cacheMap = sessionFactory().getStatistics().getSecondLevelCacheStatistics( "item" ).getEntries();
|
||||
assertEquals( 0, cacheMap.size() );
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
item = (CacheableItem) s.get( CacheableItem.class, item.getId() );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
assertNull( "it should be null", item );
|
||||
}
|
||||
|
||||
@Entity(name = "CacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "item")
|
||||
public static class CacheableItem {
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
public CacheableItem() {
|
||||
}
|
||||
|
||||
public CacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue(generator = "increment")
|
||||
@GenericGenerator(name = "increment", strategy = "increment")
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* 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.cache.ehcache.functional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Zhenlei Huang
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-10649")
|
||||
public class RefreshUpdatedDataTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
ReadWriteCacheableItem.class,
|
||||
ReadWriteVersionedCacheableItem.class,
|
||||
NonStrictReadWriteCacheableItem.class,
|
||||
NonStrictReadWriteVersionedCacheableItem.class,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void addSettings(Map settings) {
|
||||
super.addSettings( settings );
|
||||
settings.put( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.configure( "hibernate-config/hibernate.cfg.xml" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAndFlushThenRefresh() {
|
||||
// prepare data
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
final String BEFORE = "before";
|
||||
|
||||
ReadWriteCacheableItem readWriteCacheableItem = new ReadWriteCacheableItem( BEFORE );
|
||||
readWriteCacheableItem.getTags().add( "Hibernate" );
|
||||
readWriteCacheableItem.getTags().add( "ORM" );
|
||||
s.persist( readWriteCacheableItem );
|
||||
|
||||
ReadWriteVersionedCacheableItem readWriteVersionedCacheableItem = new ReadWriteVersionedCacheableItem( BEFORE );
|
||||
readWriteVersionedCacheableItem.getTags().add( "Hibernate" );
|
||||
readWriteVersionedCacheableItem.getTags().add( "ORM" );
|
||||
s.persist( readWriteVersionedCacheableItem );
|
||||
|
||||
NonStrictReadWriteCacheableItem nonStrictReadWriteCacheableItem = new NonStrictReadWriteCacheableItem( BEFORE );
|
||||
nonStrictReadWriteCacheableItem.getTags().add( "Hibernate" );
|
||||
nonStrictReadWriteCacheableItem.getTags().add( "ORM" );
|
||||
s.persist( nonStrictReadWriteCacheableItem );
|
||||
|
||||
NonStrictReadWriteVersionedCacheableItem nonStrictReadWriteVersionedCacheableItem = new NonStrictReadWriteVersionedCacheableItem( BEFORE );
|
||||
nonStrictReadWriteVersionedCacheableItem.getTags().add( "Hibernate" );
|
||||
nonStrictReadWriteVersionedCacheableItem.getTags().add( "ORM" );
|
||||
s.persist( nonStrictReadWriteVersionedCacheableItem );
|
||||
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
Session s1 = openSession();
|
||||
s1.beginTransaction();
|
||||
|
||||
final String AFTER = "after";
|
||||
|
||||
ReadWriteCacheableItem readWriteCacheableItem1 = s1.get( ReadWriteCacheableItem.class, readWriteCacheableItem.getId() );
|
||||
readWriteCacheableItem1.setName( AFTER );
|
||||
readWriteCacheableItem1.getTags().remove("ORM");
|
||||
|
||||
ReadWriteVersionedCacheableItem readWriteVersionedCacheableItem1 = s1.get( ReadWriteVersionedCacheableItem.class, readWriteVersionedCacheableItem.getId() );
|
||||
readWriteVersionedCacheableItem1.setName( AFTER );
|
||||
readWriteVersionedCacheableItem1.getTags().remove("ORM");
|
||||
|
||||
NonStrictReadWriteCacheableItem nonStrictReadWriteCacheableItem1 = s1.get( NonStrictReadWriteCacheableItem.class, nonStrictReadWriteCacheableItem.getId() );
|
||||
nonStrictReadWriteCacheableItem1.setName( AFTER );
|
||||
nonStrictReadWriteCacheableItem1.getTags().remove("ORM");
|
||||
|
||||
NonStrictReadWriteVersionedCacheableItem nonStrictReadWriteVersionedCacheableItem1 = s1.get( NonStrictReadWriteVersionedCacheableItem.class, nonStrictReadWriteVersionedCacheableItem.getId() );
|
||||
nonStrictReadWriteVersionedCacheableItem1.setName( AFTER );
|
||||
nonStrictReadWriteVersionedCacheableItem1.getTags().remove("ORM");
|
||||
|
||||
s1.flush();
|
||||
s1.refresh( readWriteCacheableItem1 );
|
||||
s1.refresh( readWriteVersionedCacheableItem1 );
|
||||
s1.refresh( nonStrictReadWriteCacheableItem1 );
|
||||
s1.refresh( nonStrictReadWriteVersionedCacheableItem1 );
|
||||
|
||||
assertEquals( AFTER, readWriteCacheableItem1.getName() );
|
||||
assertEquals( 1, readWriteCacheableItem1.getTags().size() );
|
||||
assertEquals( AFTER, readWriteVersionedCacheableItem1.getName() );
|
||||
assertEquals( 1, readWriteVersionedCacheableItem1.getTags().size() );
|
||||
assertEquals( AFTER, nonStrictReadWriteCacheableItem1.getName() );
|
||||
assertEquals( 1, nonStrictReadWriteCacheableItem1.getTags().size() );
|
||||
assertEquals( AFTER, nonStrictReadWriteVersionedCacheableItem1.getName() );
|
||||
assertEquals( 1, nonStrictReadWriteVersionedCacheableItem1.getTags().size() );
|
||||
|
||||
// open another session
|
||||
Session s2 = sessionFactory().openSession();
|
||||
try {
|
||||
s2.beginTransaction();
|
||||
ReadWriteCacheableItem readWriteCacheableItem2 = s2.get( ReadWriteCacheableItem.class, readWriteCacheableItem.getId() );
|
||||
ReadWriteVersionedCacheableItem readWriteVersionedCacheableItem2 = s2.get( ReadWriteVersionedCacheableItem.class, readWriteVersionedCacheableItem.getId() );
|
||||
NonStrictReadWriteCacheableItem nonStrictReadWriteCacheableItem2 = s2.get( NonStrictReadWriteCacheableItem.class, nonStrictReadWriteCacheableItem.getId() );
|
||||
NonStrictReadWriteVersionedCacheableItem nonStrictReadWriteVersionedCacheableItem2 = s2.get( NonStrictReadWriteVersionedCacheableItem.class, nonStrictReadWriteVersionedCacheableItem.getId() );
|
||||
|
||||
assertEquals( BEFORE, readWriteCacheableItem2.getName() );
|
||||
assertEquals( 2, readWriteCacheableItem2.getTags().size() );
|
||||
assertEquals( BEFORE, readWriteVersionedCacheableItem2.getName() );
|
||||
assertEquals( 2, readWriteVersionedCacheableItem2.getTags().size() );
|
||||
|
||||
//READ_UNCOMMITTED because there is no locking to prevent collections from being cached in the first Session
|
||||
|
||||
assertEquals( BEFORE, nonStrictReadWriteCacheableItem2.getName() );
|
||||
assertEquals( 1, nonStrictReadWriteCacheableItem2.getTags().size());
|
||||
assertEquals( BEFORE, nonStrictReadWriteVersionedCacheableItem2.getName() );
|
||||
assertEquals( 1, nonStrictReadWriteVersionedCacheableItem2.getTags().size() );
|
||||
|
||||
s2.getTransaction().commit();
|
||||
}
|
||||
finally {
|
||||
if ( s2.getTransaction().getStatus().canRollback() ) {
|
||||
s2.getTransaction().rollback();
|
||||
}
|
||||
s2.close();
|
||||
}
|
||||
|
||||
s1.getTransaction().rollback();
|
||||
s1.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.delete( readWriteCacheableItem );
|
||||
s.delete( readWriteVersionedCacheableItem );
|
||||
s.delete( nonStrictReadWriteCacheableItem );
|
||||
s.delete( nonStrictReadWriteVersionedCacheableItem );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Entity(name = "ReadWriteCacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "item")
|
||||
public static class ReadWriteCacheableItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||
@ElementCollection
|
||||
private List<String> tags = new ArrayList<String>();
|
||||
|
||||
public ReadWriteCacheableItem() {
|
||||
}
|
||||
|
||||
public ReadWriteCacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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 List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "ReadWriteVersionedCacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "item")
|
||||
public static class ReadWriteVersionedCacheableItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||
@ElementCollection
|
||||
private List<String> tags = new ArrayList<String>();
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public ReadWriteVersionedCacheableItem() {
|
||||
}
|
||||
|
||||
public ReadWriteVersionedCacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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 List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "NonStrictReadWriteCacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "item")
|
||||
public static class NonStrictReadWriteCacheableItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
@ElementCollection
|
||||
private List<String> tags = new ArrayList<String>();
|
||||
|
||||
public NonStrictReadWriteCacheableItem() {
|
||||
}
|
||||
|
||||
public NonStrictReadWriteCacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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 List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "NonStrictReadWriteVersionedCacheableItem")
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "item")
|
||||
public static class NonStrictReadWriteVersionedCacheableItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
@ElementCollection
|
||||
private List<String> tags = new ArrayList<String>();
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public NonStrictReadWriteVersionedCacheableItem() {
|
||||
}
|
||||
|
||||
public NonStrictReadWriteVersionedCacheableItem(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
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 List<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue