From 2ddefd615b53325ec58b1c92aaba63878f8c3eee Mon Sep 17 00:00:00 2001 From: Chris Dennis Date: Fri, 22 Apr 2016 16:08:29 -0400 Subject: [PATCH] HHH-10770 JCache provider updates * Steer away from EntryProcessor * Add tests --- .../cache/jcache/JCacheMessageLogger.java | 9 + .../cache/jcache/JCacheRegionFactory.java | 5 +- .../jcache/JCacheTransactionalDataRegion.java | 20 +- ...AbstractReadWriteRegionAccessStrategy.java | 226 +++++++----- .../access/JCacheRegionAccessStrategy.java | 6 +- ...nStrictCollectionRegionAccessStrategy.java | 7 + .../NonStrictEntityRegionAccessStrategy.java | 5 + .../ReadWriteEntityRegionAccessStrategy.java | 65 ++-- ...eadWriteNaturalIdRegionAccessStrategy.java | 65 ++-- .../cache/jcache/time/Timestamper.java | 41 +++ .../jcache/access/ItemValueExtractor.java | 32 ++ .../test/cache/HibernateCacheTest.java | 248 +++++++++++++ .../jcache/functional/InsertedDataTest.java | 277 ++++++++++++++ .../functional/RefreshUpdatedDataTest.java | 342 ++++++++++++++++++ .../org/hibernate/test/domain/Account.java | 38 ++ .../java/org/hibernate/test/domain/Event.java | 74 ++++ .../hibernate/test/domain/EventManager.java | 240 ++++++++++++ .../test/domain/HolidayCalendar.java | 66 ++++ .../java/org/hibernate/test/domain/Item.java | 37 ++ .../org/hibernate/test/domain/Person.java | 111 ++++++ .../hibernate/test/domain/PhoneNumber.java | 78 ++++ .../org/hibernate/test/domain/UuidItem.java | 37 ++ .../hibernate/test/domain/VersionedItem.java | 19 + .../hibernate-config/domain/Account.hbm.xml | 25 ++ .../hibernate-config/domain/Event.hbm.xml | 32 ++ .../domain/HolidayCalendar.hbm.xml | 27 ++ .../hibernate-config/domain/Item.hbm.xml | 34 ++ .../hibernate-config/domain/Person.hbm.xml | 46 +++ .../domain/PhoneNumber.hbm.xml | 26 ++ .../hibernate-config/hibernate.cfg.xml | 54 +++ .../src/test/resources/hibernate.properties | 17 + .../src/test/resources/log4j.properties | 17 + 32 files changed, 2143 insertions(+), 183 deletions(-) create mode 100644 hibernate-jcache/src/main/java/org/hibernate/cache/jcache/time/Timestamper.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/cache/jcache/access/ItemValueExtractor.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/cache/HibernateCacheTest.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/cache/jcache/functional/InsertedDataTest.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/cache/jcache/functional/RefreshUpdatedDataTest.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/Account.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/Event.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/EventManager.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/HolidayCalendar.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/Item.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/Person.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/PhoneNumber.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/UuidItem.java create mode 100644 hibernate-jcache/src/test/java/org/hibernate/test/domain/VersionedItem.java create mode 100644 hibernate-jcache/src/test/resources/hibernate-config/domain/Account.hbm.xml create mode 100644 hibernate-jcache/src/test/resources/hibernate-config/domain/Event.hbm.xml create mode 100644 hibernate-jcache/src/test/resources/hibernate-config/domain/HolidayCalendar.hbm.xml create mode 100644 hibernate-jcache/src/test/resources/hibernate-config/domain/Item.hbm.xml create mode 100644 hibernate-jcache/src/test/resources/hibernate-config/domain/Person.hbm.xml create mode 100644 hibernate-jcache/src/test/resources/hibernate-config/domain/PhoneNumber.hbm.xml create mode 100644 hibernate-jcache/src/test/resources/hibernate-config/hibernate.cfg.xml create mode 100644 hibernate-jcache/src/test/resources/hibernate.properties create mode 100644 hibernate-jcache/src/test/resources/log4j.properties diff --git a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheMessageLogger.java b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheMessageLogger.java index acc7d4bc39..8c5142e50b 100644 --- a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheMessageLogger.java +++ b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheMessageLogger.java @@ -12,6 +12,7 @@ import org.jboss.logging.annotations.MessageLogger; import org.hibernate.internal.CoreMessageLogger; +import static org.jboss.logging.Logger.Level.ERROR; import static org.jboss.logging.Logger.Level.WARN; /** @@ -36,4 +37,12 @@ public interface JCacheMessageLogger extends CoreMessageLogger { id = NAMESPACE + 2 ) void attemptToRestopAlreadyStoppedJCacheProvider(); + + @LogMessage(level = ERROR) + @Message( + value = "Cache: %s Key: %s Lockable: %s. A soft-locked cache entry was missing. This is either" + + " out of balance lock/unlock sequences, or an eagerly evicting cache.", + id = NAMESPACE + 3 + ) + void missingLock(JCacheRegion region, Object key, Object value); } diff --git a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheRegionFactory.java b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheRegionFactory.java index 68488c8651..1eda50a05a 100644 --- a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheRegionFactory.java +++ b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheRegionFactory.java @@ -22,6 +22,7 @@ import org.jboss.logging.Logger; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cache.CacheException; +import org.hibernate.cache.jcache.time.Timestamper; import org.hibernate.cache.spi.CacheDataDescription; import org.hibernate.cache.spi.CollectionRegion; import org.hibernate.cache.spi.EntityRegion; @@ -187,11 +188,11 @@ public class JCacheRegionFactory implements RegionFactory { } static long nextTS() { - return System.currentTimeMillis() / 100; + return Timestamper.next(); } static int timeOut() { - return (int) (TimeUnit.SECONDS.toMillis(60) / 100); + return (int) TimeUnit.SECONDS.toMillis( 60 ) * Timestamper.ONE_MS; } private String getProp(Properties properties, String prop) { diff --git a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheTransactionalDataRegion.java b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheTransactionalDataRegion.java index 55908add18..3a8160b5bf 100644 --- a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheTransactionalDataRegion.java +++ b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheTransactionalDataRegion.java @@ -9,7 +9,6 @@ package org.hibernate.cache.jcache; import java.util.EnumSet; import java.util.Set; import javax.cache.Cache; -import javax.cache.processor.EntryProcessor; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cache.spi.CacheDataDescription; @@ -33,17 +32,19 @@ public class JCacheTransactionalDataRegion extends JCacheRegion implements Trans this.options = options; } + @Override public boolean isTransactionAware() { return false; } + @Override public CacheDataDescription getCacheDataDescription() { return metadata; } protected void throwIfAccessTypeUnsupported(AccessType accessType) { - if ( supportedAccessTypes().contains( accessType ) ) { - throw new UnsupportedOperationException( "This doesn't JCacheTransactionalDataRegion doesn't support " + accessType ); + if ( !supportedAccessTypes().contains( accessType ) ) { + throw new UnsupportedOperationException( "JCacheTransactionalDataRegion doesn't support " + accessType ); } } @@ -67,12 +68,15 @@ public class JCacheTransactionalDataRegion extends JCacheRegion implements Trans cache.put( key, value ); } + public boolean putIfAbsent(Object key, Object value) { + return cache.putIfAbsent( key, value ); + } + + public boolean replace(Object key, Object expected, Object value) { + return cache.replace( key, expected, value ); + } + public SessionFactoryOptions getSessionFactoryOptions() { return options; } - - public T invoke(Object key, EntryProcessor entryProcessor, Object... args) { - return cache.invoke( key, entryProcessor, args); - } - } diff --git a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/access/AbstractReadWriteRegionAccessStrategy.java b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/access/AbstractReadWriteRegionAccessStrategy.java index 9b7fa6919e..10ba9d95dd 100644 --- a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/access/AbstractReadWriteRegionAccessStrategy.java +++ b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/access/AbstractReadWriteRegionAccessStrategy.java @@ -11,23 +11,29 @@ import java.io.Serializable; import java.util.Comparator; import java.util.UUID; import java.util.concurrent.atomic.AtomicLong; -import javax.cache.processor.EntryProcessor; -import javax.cache.processor.EntryProcessorException; -import javax.cache.processor.MutableEntry; import org.hibernate.cache.CacheException; +import org.hibernate.cache.jcache.JCacheMessageLogger; import org.hibernate.cache.jcache.JCacheTransactionalDataRegion; import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.jboss.logging.Logger; /** * @author Alex Snaps */ -public class AbstractReadWriteRegionAccessStrategy { +abstract class AbstractReadWriteRegionAccessStrategy { + + private static final JCacheMessageLogger LOG = Logger.getMessageLogger( + JCacheMessageLogger.class, + AbstractReadWriteRegionAccessStrategy.class.getName() + ); + protected final R region; protected final Comparator versionComparator; private final UUID uuid = UUID.randomUUID(); private final AtomicLong nextLockId = new AtomicLong(); + private final AtomicLong nextItemId = new AtomicLong(); public AbstractReadWriteRegionAccessStrategy(R region) { this.versionComparator = region.getCacheDataDescription().getVersionComparator(); @@ -51,26 +57,29 @@ public class AbstractReadWriteRegionAccessStrategy() { - @Override - public Boolean process(MutableEntry entry, Object... args) - throws EntryProcessorException { - final Lockable item = (Lockable) entry.getValue(); - final boolean writeable = item == null || item.isWriteable( - (Long) args[1], - args[2], - versionComparator - ); - if ( writeable ) { - entry.setValue( new Item( args[0], args[2], region.nextTimestamp() ) ); - return true; - } - else { - return false; - } - } - }, value, txTimestamp, version); + while (true) { + Lockable item = (Lockable) region.get( key ); + + if (item == null) { + /* + * If the item is null due a softlock being evicted... then this + * is wrong, the in-doubt soft-lock could get replaced with the + * old value. All that can be done from a JCache perspective is + * to log a warning. + */ + if (region.putIfAbsent( key, new Item( value, version, txTimestamp, nextItemId() ))) { + return true; + } + } + else if (item.isWriteable( txTimestamp, version, versionComparator )) { + if (region.replace( key, item, new Item( value, version, txTimestamp, nextItemId() ))) { + return true; + } + } + else { + return false; + } + } } public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) @@ -79,49 +88,47 @@ public class AbstractReadWriteRegionAccessStrategy() { - @Override - public SoftLock process(MutableEntry entry, Object... args) - throws EntryProcessorException { - final Lockable item = (Lockable) entry.getValue(); - final long timeout = region.nextTimestamp() + region.getTimeout(); - final Lock lock = ( item == null ) ? new Lock( - timeout, - (UUID) args[0], - (Long) args[1], - args[2] - ) - : item.lock( timeout, (UUID) args[0], (Long) args[1] ); - entry.setValue( lock ); - return lock; - } - }, uuid, nextLockId(), version - ); + long timeout = region.nextTimestamp() + region.getTimeout(); + while (true) { + Lockable item = (Lockable) region.get( key ); + + if ( item == null ) { + /* + * What happens here if a previous soft-lock was evicted to make + * this null. + */ + Lock lock = new Lock(timeout, uuid, nextLockId(), version); + if (region.putIfAbsent( key, lock )) { + return lock; + } + } + else { + Lock lock = item.lock( timeout, uuid, nextLockId() ); + if (region.replace(key, item, lock)) { + return lock; + } + } + } } public void unlockItem(SharedSessionContractImplementor session, Object key, SoftLock lock) throws CacheException { - region.invoke( - key, new EntryProcessor() { - @Override - public Void process(MutableEntry entry, Object... args) - throws EntryProcessorException { - final Lockable item = (Lockable) entry.getValue(); + while (true) { + Lockable item = (Lockable) region.get( key ); - if ( (item != null) && item.isUnlockable( (SoftLock) args[0] ) ) { - ( (Lock) item ).unlock( region.nextTimestamp() ); - entry.setValue( item ); - } - else { - entry.setValue( null ); - } - return null; - } - }, lock); + if (item != null && item.isUnlockable( lock )) { + if (region.replace(key, item, ((Lock) item ).unlock(region.nextTimestamp()))) { + return; + } + } + else { + handleMissingLock( key, item ); + return; + } + } } public void remove(SharedSessionContractImplementor session, Object key) throws CacheException { - region.remove( key ); + //this access strategy is asynchronous } public void removeAll() throws CacheException { @@ -141,13 +148,25 @@ public class AbstractReadWriteRegionAccessStrategy>> ( i * Integer.SIZE ) ); - } - return hash + temp; + return hash ^ Long.hashCode( lockId ); } /** @@ -315,18 +373,22 @@ public class AbstractReadWriteRegionAccessStrategy() { - @Override - public Boolean process(MutableEntry entry, Object... args) - throws EntryProcessorException { - if ( !entry.exists() ) { - entry.setValue( new Item( args[0], args[1], (Long) args[2] ) ); - return true; - } - else { - return false; - } - } - }, value, version, region.nextTimestamp() - ); + return region.putIfAbsent( key, new Item(value, version, region.nextTimestamp(), nextItemId() )); } @Override @@ -64,33 +46,28 @@ public class ReadWriteEntityRegionAccessStrategy @Override public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) throws CacheException { - return region.invoke( - key, new EntryProcessor() { - @Override - public Boolean process(MutableEntry entry, Object... args) - throws EntryProcessorException { - final Lockable item = (Lockable) entry.getValue(); - - if ( item != null && item.isUnlockable( (SoftLock) args[3] ) ) { - final Lock lockItem = (Lock) item; - if ( lockItem.wasLockedConcurrently() ) { - lockItem.unlock( (Long) args[1] ); - entry.setValue( lockItem ); - return false; - } - else { - entry.setValue( new Item( args[0], args[1], (Long) args[4] ) ); - return true; - } - } - else { - entry.setValue( null ); - return false; - } + while (true) { + Lockable item = (Lockable) region.get( key ); + if ( item != null && item.isUnlockable( lock ) ) { + Lock lockItem = (Lock) item; + if ( lockItem.wasLockedConcurrently() ) { + if (region.replace( key, lockItem, lockItem.unlock( region.nextTimestamp() ))) { + return false; } - }, value, currentVersion, previousVersion, lock, region.nextTimestamp() - ); + } + else { + if (region.replace( key, lockItem, new Item(value, currentVersion, region.nextTimestamp(), nextItemId() ))) { + return true; + } + } + } + else { + handleMissingLock( key, item ); + return false; + } + + } } @Override diff --git a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/access/ReadWriteNaturalIdRegionAccessStrategy.java b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/access/ReadWriteNaturalIdRegionAccessStrategy.java index b4b8de9d5a..d92ae39bdc 100644 --- a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/access/ReadWriteNaturalIdRegionAccessStrategy.java +++ b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/access/ReadWriteNaturalIdRegionAccessStrategy.java @@ -7,10 +7,6 @@ package org.hibernate.cache.jcache.access; -import javax.cache.processor.EntryProcessor; -import javax.cache.processor.EntryProcessorException; -import javax.cache.processor.MutableEntry; - import org.hibernate.cache.CacheException; import org.hibernate.cache.internal.DefaultCacheKeysFactory; import org.hibernate.cache.jcache.JCacheNaturalIdRegion; @@ -37,21 +33,7 @@ public class ReadWriteNaturalIdRegionAccessStrategy @Override public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException { - return region.invoke( - key, new EntryProcessor() { - @Override - public Boolean process(MutableEntry entry, Object... args) - throws EntryProcessorException { - if ( !entry.exists() ) { - entry.setValue( new Item( args[0], null, (Long) args[1] ) ); - return true; - } - else { - return false; - } - } - }, value, region.nextTimestamp() - ); + return region.putIfAbsent( key, new Item( value, null, region.nextTimestamp(), nextItemId() )); } @Override @@ -63,33 +45,28 @@ public class ReadWriteNaturalIdRegionAccessStrategy @Override public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, SoftLock lock) throws CacheException { - return region.invoke( - key, new EntryProcessor() { - @Override - public Boolean process(MutableEntry entry, Object... args) - throws EntryProcessorException { - final Lockable item = (Lockable) entry.getValue(); - - if ( item != null && item.isUnlockable( (SoftLock) args[1] ) ) { - final Lock lockItem = (Lock) item; - if ( lockItem.wasLockedConcurrently() ) { - lockItem.unlock( region.nextTimestamp() ); - entry.setValue( lockItem ); - return false; - } - else { - entry.setValue( new Item( args[0], null, (Long) args[2] ) ); - return true; - } - } - else { - entry.setValue( null ); - return false; - } + while (true) { + Lockable item = (Lockable) region.get( key ); + if ( item != null && item.isUnlockable( lock ) ) { + final Lock lockItem = (Lock) item; + if ( lockItem.wasLockedConcurrently() ) { + if (region.replace( key, lockItem, lockItem.unlock( region.nextTimestamp() ) )) { + return false; } - }, value, lock, region.nextTimestamp() - ); + } + else { + if (region.replace( key, lockItem, new Item(value, null, region.nextTimestamp(), nextItemId() ))) { + return true; + } + } + } + else { + handleMissingLock( key, item ); + return false; + } + + } } @Override diff --git a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/time/Timestamper.java b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/time/Timestamper.java new file mode 100644 index 0000000000..345f061519 --- /dev/null +++ b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/time/Timestamper.java @@ -0,0 +1,41 @@ +/* + * 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 . + */ +package org.hibernate.cache.jcache.time; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Generates increasing identifiers (in a single VM only). Not valid across multiple VMs. Identifiers are not + * necessarily strictly increasing, but usually are. + *

+ * Core while loop implemented by Alex Snaps - EHCache project - under ASL 2.0 + * + * @author Hibernate team + * @author Alex Snaps + */ +public final class Timestamper { + private static final int BIN_DIGITS = 12; + public static final short ONE_MS = 1 << BIN_DIGITS; + private static final AtomicLong VALUE = new AtomicLong(); + + public static long next() { + while ( true ) { + long base = System.currentTimeMillis() << BIN_DIGITS; + long maxValue = base + ONE_MS - 1; + + for ( long current = VALUE.get(), update = Math.max( base, current + 1 ); update < maxValue; + current = VALUE.get(), update = Math.max( base, current + 1 ) ) { + if ( VALUE.compareAndSet( current, update ) ) { + return update; + } + } + } + } + + private Timestamper() { + } +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/cache/jcache/access/ItemValueExtractor.java b/hibernate-jcache/src/test/java/org/hibernate/cache/jcache/access/ItemValueExtractor.java new file mode 100644 index 0000000000..72c9f7f9ea --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/cache/jcache/access/ItemValueExtractor.java @@ -0,0 +1,32 @@ +/* + * 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 . + */ +package org.hibernate.cache.jcache.access; + +import org.hibernate.cache.jcache.JCacheTransactionalDataRegion; + + +/** + * @author Alex Snaps + */ +public class ItemValueExtractor extends AbstractReadWriteRegionAccessStrategy { + + + /** + * Creates a read/write cache access strategy around the given cache region. + */ + public ItemValueExtractor(JCacheTransactionalDataRegion region) { + super(region); + } + + + public static T getValue(final Object entry) { + if(!(entry instanceof Item)) { + throw new IllegalArgumentException("Entry needs to be of type " + Item.class.getName()); + } + return (T)((Item)entry).getValue(); + } +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/cache/HibernateCacheTest.java b/hibernate-jcache/src/test/java/org/hibernate/test/cache/HibernateCacheTest.java new file mode 100644 index 0000000000..441ee20a78 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/cache/HibernateCacheTest.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +package org.hibernate.test.cache; + +import org.hibernate.cache.jcache.access.ItemValueExtractor; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cache.spi.access.SoftLock; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.stat.QueryStatistics; +import org.hibernate.stat.SecondLevelCacheStatistics; +import org.hibernate.stat.Statistics; + +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.hibernate.test.domain.Event; +import org.hibernate.test.domain.EventManager; +import org.hibernate.test.domain.Item; +import org.hibernate.test.domain.Person; +import org.hibernate.test.domain.PhoneNumber; +import org.hibernate.test.domain.VersionedItem; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +/** + * @author Chris Dennis + * @author Brett Meyer + */ +public class HibernateCacheTest extends BaseNonConfigCoreFunctionalTestCase { + + private static final String REGION_PREFIX = "hibernate.test."; + + public HibernateCacheTest() { + System.setProperty( "derby.system.home", "target/derby" ); + } + + @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 testQueryCacheInvalidation() throws Exception { + Session s = sessionFactory().openSession(); + Transaction t = s.beginTransaction(); + Item i = new Item(); + i.setName( "widget" ); + i.setDescription( "A really top-quality, full-featured widget." ); + s.persist( i ); + t.commit(); + s.close(); + + SecondLevelCacheStatistics slcs = sessionFactory() + .getStatistics() + .getSecondLevelCacheStatistics( REGION_PREFIX + Item.class.getName() ); + + assertThat( slcs.getPutCount(), equalTo( 1L ) ); + assertThat( slcs.getEntries().size(), equalTo( 1 ) ); + + s = sessionFactory().openSession(); + t = s.beginTransaction(); + i = (Item) s.get( Item.class, i.getId() ); + + assertThat( slcs.getHitCount(), equalTo( 1L ) ); + assertThat( slcs.getMissCount(), equalTo( 0L ) ); + + i.setDescription( "A bog standard item" ); + + t.commit(); + s.close(); + + assertThat( slcs.getPutCount(), equalTo( 2L ) ); + + Object entry = slcs.getEntries().get( i.getId() ); + Map map; + if ( entry instanceof Map ) { + map = (Map) entry; + } + else { + map = ItemValueExtractor.getValue( entry ); + } + assertThat( (String) map.get( "description" ), equalTo( "A bog standard item" ) ); + assertThat( (String) map.get( "name" ), equalTo( "widget" ) ); + + // cleanup + s = sessionFactory().openSession(); + t = s.beginTransaction(); + s.delete( i ); + t.commit(); + s.close(); + } + + @Test + public void testEmptySecondLevelCacheEntry() throws Exception { + sessionFactory().getCache().evictEntityRegion( Item.class.getName() ); + Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + SecondLevelCacheStatistics statistics = stats.getSecondLevelCacheStatistics( REGION_PREFIX + Item.class.getName() ); + Map cacheEntries = statistics.getEntries(); + assertThat( cacheEntries.size(), equalTo( 0 ) ); + } + + @Test + public void testStaleWritesLeaveCacheConsistent() { + Session s = sessionFactory().openSession(); + Transaction txn = s.beginTransaction(); + VersionedItem item = new VersionedItem(); + item.setName( "steve" ); + item.setDescription( "steve's item" ); + s.save( item ); + txn.commit(); + s.close(); + + Long initialVersion = item.getVersion(); + + // manually revert the version property + item.setVersion( item.getVersion() - 1 ); + + try { + s = sessionFactory().openSession(); + txn = s.beginTransaction(); + s.update( item ); + txn.commit(); + s.close(); + fail( "expected stale write to fail" ); + } + catch ( Throwable expected ) { + // expected behavior here + if ( txn != null ) { + try { + txn.rollback(); + } + catch ( Throwable ignore ) { + } + } + } + finally { + if ( s != null && s.isOpen() ) { + try { + s.close(); + } + catch ( Throwable ignore ) { + } + } + } + + // check the version value in the cache... + SecondLevelCacheStatistics slcs = sessionFactory().getStatistics() + .getSecondLevelCacheStatistics( REGION_PREFIX + VersionedItem.class.getName() ); + assertNotNull(slcs); + final Map entries = slcs.getEntries(); + Object entry = entries.get( item.getId() ); + Long cachedVersionValue; + if ( entry instanceof SoftLock ) { + //FIXME don't know what to test here + //cachedVersionValue = new Long( ( (ReadWriteCache.Lock) entry).getUnlockTimestamp() ); + } + else { + cachedVersionValue = (Long) ( (Map) entry ).get( "_version" ); + assertThat( initialVersion, equalTo( cachedVersionValue ) ); + } + + + // cleanup + s = sessionFactory().openSession(); + txn = s.beginTransaction(); + item = (VersionedItem) s.load( VersionedItem.class, item.getId() ); + s.delete( item ); + txn.commit(); + s.close(); + + } + + @Test + public void testGeneralUsage() { + EventManager mgr = new EventManager( sessionFactory() ); + Statistics stats = sessionFactory().getStatistics(); + + // create 3 persons Steve, Orion, Tim + Person stevePerson = new Person(); + stevePerson.setFirstname( "Steve" ); + stevePerson.setLastname( "Harris" ); + Long steveId = mgr.createAndStorePerson( stevePerson ); + mgr.addEmailToPerson( steveId, "steve@tc.com" ); + mgr.addEmailToPerson( steveId, "sharrif@tc.com" ); + mgr.addTalismanToPerson( steveId, "rabbit foot" ); + mgr.addTalismanToPerson( steveId, "john de conqueroo" ); + + PhoneNumber p1 = new PhoneNumber(); + p1.setNumberType( "Office" ); + p1.setPhone( 111111 ); + mgr.addPhoneNumberToPerson( steveId, p1 ); + + PhoneNumber p2 = new PhoneNumber(); + p2.setNumberType( "Home" ); + p2.setPhone( 222222 ); + mgr.addPhoneNumberToPerson( steveId, p2 ); + + Person orionPerson = new Person(); + orionPerson.setFirstname( "Orion" ); + orionPerson.setLastname( "Letizi" ); + Long orionId = mgr.createAndStorePerson( orionPerson ); + mgr.addEmailToPerson( orionId, "orion@tc.com" ); + mgr.addTalismanToPerson( orionId, "voodoo doll" ); + + Long timId = mgr.createAndStorePerson( "Tim", "Teck" ); + mgr.addEmailToPerson( timId, "teck@tc.com" ); + mgr.addTalismanToPerson( timId, "magic decoder ring" ); + + Long engMeetingId = mgr.createAndStoreEvent( "Eng Meeting", stevePerson, new Date() ); + mgr.addPersonToEvent( steveId, engMeetingId ); + mgr.addPersonToEvent( orionId, engMeetingId ); + mgr.addPersonToEvent( timId, engMeetingId ); + + Long docMeetingId = mgr.createAndStoreEvent( "Doc Meeting", orionPerson, new Date() ); + mgr.addPersonToEvent( steveId, docMeetingId ); + mgr.addPersonToEvent( orionId, docMeetingId ); + + for ( Event event : (List) mgr.listEvents() ) { + mgr.listEmailsOfEvent( event.getId() ); + } + + QueryStatistics queryStats = stats.getQueryStatistics( "from Event" ); + assertThat( "Cache Miss Count", queryStats.getCacheMissCount(), equalTo( 1L ) ); + assertThat( "Cache Hit Count", queryStats.getCacheHitCount(), equalTo( 0L ) ); + assertThat( "Cache Put Count", queryStats.getCachePutCount(), equalTo( 1L ) ); + } +} \ No newline at end of file diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/cache/jcache/functional/InsertedDataTest.java b/hibernate-jcache/src/test/java/org/hibernate/test/cache/jcache/functional/InsertedDataTest.java new file mode 100644 index 0000000000..01be8b1134 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/cache/jcache/functional/InsertedDataTest.java @@ -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 . + */ +package org.hibernate.test.cache.jcache.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.jcache.access.AbstractReadWriteRegionAccessStrategy$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; + } + } +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/cache/jcache/functional/RefreshUpdatedDataTest.java b/hibernate-jcache/src/test/java/org/hibernate/test/cache/jcache/functional/RefreshUpdatedDataTest.java new file mode 100644 index 0000000000..4f372f43f1 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/cache/jcache/functional/RefreshUpdatedDataTest.java @@ -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 . + */ +package org.hibernate.test.cache.jcache.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 tags = new ArrayList<>(); + + 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 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 tags = new ArrayList<>(); + + @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 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 tags = new ArrayList<>(); + + 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 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 tags = new ArrayList<>(); + + @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 getTags() { + return tags; + } + } +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/Account.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/Account.java new file mode 100644 index 0000000000..65f177b77c --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/Account.java @@ -0,0 +1,38 @@ +/* + * 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 . + */ + +package org.hibernate.test.domain; + +public class Account { + + private Long id; + private Person person; + + public Account() { + // + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String toString() { + return super.toString(); + } +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/Event.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/Event.java new file mode 100644 index 0000000000..8821a4330c --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/Event.java @@ -0,0 +1,74 @@ +/* + * 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 . + */ +package org.hibernate.test.domain; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +public class Event { + private Long id; + + private String title; + private Date date; + private Set participants = new HashSet(); + private Person organizer; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setOrganizer(Person organizer) { + this.organizer = organizer; + } + + public Person getOrganizer() { + return organizer; + } + + public Set getParticipants() { + return participants; + } + + public void setParticipants(Set participants) { + this.participants = participants; + } + + public void addParticipant(Person person) { + participants.add(person); + person.getEvents().add(this); + } + + public void removeParticipant(Person person) { + participants.remove(person); + person.getEvents().remove(this); + } + + public String toString() { + return getTitle() + ": " + getDate(); + } +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/EventManager.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/EventManager.java new file mode 100644 index 0000000000..1a91420dbb --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/EventManager.java @@ -0,0 +1,240 @@ +/* + * 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 . + */ +package org.hibernate.test.domain; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; + +public class EventManager { + + private final SessionFactory sessionFactory; + + public EventManager(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory; + } + + public List listEmailsOfEvent(Long eventId) { + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + List emailList = new ArrayList(); + Event event = (Event)session.load(Event.class, eventId); + for (Iterator it = event.getParticipants().iterator(); it.hasNext(); ) { + Person person = (Person)it.next(); + emailList.addAll(person.getEmailAddresses()); + } + + session.getTransaction().commit(); + return emailList; + } + + public Long createAndStoreEvent(String title, Person organizer, Date theDate) { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + Event theEvent = new Event(); + theEvent.setTitle(title); + theEvent.setDate(theDate); + theEvent.setOrganizer(organizer); + + Long eventId = (Long)session.save(theEvent); + + session.getTransaction().commit(); + return eventId; + } + + public Long createAndStorePerson(String firstName, String lastName) { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + Person person = new Person(); + person.setFirstname(firstName); + person.setLastname(lastName); + + Long personId = (Long)session.save(person); + + session.getTransaction().commit(); + return personId; + } + + public Long createAndStorePerson(Person person) { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + Long personId = (Long)session.save(person); + + session.getTransaction().commit(); + return personId; + } + + public List listEvents() { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + List result = session.createQuery("from Event").setCacheable(true).list(); + + session.getTransaction().commit(); + + return result; + } + + /** + * Call setEntity() on a cacheable query - see FORGE-265 + */ + public List listEventsOfOrganizer(Person organizer) { + + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + Query query = session.createQuery("from Event ev where ev.organizer = :organizer"); + + query.setCacheable(true); + query.setEntity("organizer", organizer); + List result = query.list(); + + session.getTransaction().commit(); + + return result; + } + + /** + * Use a Criteria query - see FORGE-247 + */ + public List listEventsWithCriteria() { + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + List result = session.createCriteria(Event.class) + .setCacheable(true) + .list(); + + session.getTransaction().commit(); + + return result; + } + + public void addPersonToEvent(Long personId, Long eventId) { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + Event anEvent = (Event)session.load(Event.class, eventId); + + aPerson.getEvents().add(anEvent); + + session.getTransaction().commit(); + } + + public Long addPersonToAccount(Long personId, Account account) { + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + account.setPerson(aPerson); + + Long accountId = (Long)session.save(account); + + session.getTransaction().commit(); + return accountId; + } + + public Account getAccount(Long accountId) { + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Account account = (Account)session.load(Account.class, accountId); + + session.getTransaction().commit(); + return account; + } + + public void addEmailToPerson(Long personId, String emailAddress) { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + + // The getEmailAddresses() might trigger a lazy load of the collection + aPerson.getEmailAddresses().add(emailAddress); + + session.getTransaction().commit(); + } + + public void addPhoneNumberToPerson(Long personId, PhoneNumber pN) { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + pN.setPersonId(personId.longValue()); + aPerson.getPhoneNumbers().add(pN); + + session.getTransaction().commit(); + } + + public void addTalismanToPerson(Long personId, String talisman) { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + Person aPerson = (Person)session.load(Person.class, personId); + aPerson.addTalisman(talisman); + + session.getTransaction().commit(); + } + + public Long createHolidayCalendar() { + + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + + // delete all existing calendars + List calendars = session.createQuery("from HolidayCalendar").setCacheable(true).list(); + for (ListIterator li = calendars.listIterator(); li.hasNext(); ) { + session.delete(li.next()); + } + + HolidayCalendar calendar = new HolidayCalendar(); + calendar.init(); + + Long calendarId = (Long)session.save(calendar); + + session.getTransaction().commit(); + return calendarId; + } + + public HolidayCalendar getHolidayCalendar() { + Session session = sessionFactory.getCurrentSession(); + + session.beginTransaction(); + + List calendars = session.createQuery("from HolidayCalendar").setCacheable(true).list(); + + session.getTransaction().commit(); + + return calendars.isEmpty() ? null : (HolidayCalendar)calendars.get(0); + } +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/HolidayCalendar.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/HolidayCalendar.java new file mode 100644 index 0000000000..9b00afbfde --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/HolidayCalendar.java @@ -0,0 +1,66 @@ +/* + * 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 . + */ +package org.hibernate.test.domain; + + + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class HolidayCalendar { + + + private Long id; + // Date -> String + private Map holidays = new HashMap(); + + public HolidayCalendar init() { + DateFormat df = new SimpleDateFormat("yyyy.MM.dd"); + try { + holidays.clear(); + holidays.put(df.parse("2009.01.01"), "New Year's Day"); + holidays.put(df.parse("2009.02.14"), "Valentine's Day"); + holidays.put(df.parse("2009.11.11"), "Armistice Day"); + } catch (ParseException e) { + throw new RuntimeException(e); + } + return this; + } + + public Map getHolidays() { + return holidays; + } + + protected void setHolidays(Map holidays) { + this.holidays = holidays; + } + + public void addHoliday(Date d, String name) { + holidays.put(d, name); + } + + public String getHoliday(Date d) { + return (String)holidays.get(d); + } + + public boolean isHoliday(Date d) { + return holidays.containsKey(d); + } + + protected Long getId() { + return id; + } + + protected void setId(Long id) { + this.id = id; + } +} + diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/Item.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/Item.java new file mode 100644 index 0000000000..b0846a0615 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/Item.java @@ -0,0 +1,37 @@ +/* + * 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 . + */ +package org.hibernate.test.domain; + +public class Item { + private Long id; + private String name; + private String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + 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; + } +} \ No newline at end of file diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/Person.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/Person.java new file mode 100644 index 0000000000..b935fd0581 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/Person.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ + +package org.hibernate.test.domain; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Person { + + private Long id; + private int age; + private String firstname; + private String lastname; + private List events = new ArrayList(); // list semantics, e.g., indexed + private Set emailAddresses = new HashSet(); + private Set phoneNumbers = new HashSet(); + private List talismans = new ArrayList(); // a Bag of good-luck charms. + + public Person() { + // + } + + public List getEvents() { + return events; + } + + protected void setEvents(List events) { + this.events = events; + } + + public void addToEvent(Event event) { + this.getEvents().add(event); + event.getParticipants().add(this); + } + + public void removeFromEvent(Event event) { + this.getEvents().remove(event); + event.getParticipants().remove(this); + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public Set getEmailAddresses() { + return emailAddresses; + } + + public void setEmailAddresses(Set emailAddresses) { + this.emailAddresses = emailAddresses; + } + + public Set getPhoneNumbers() { + return phoneNumbers; + } + + public void setPhoneNumbers(Set phoneNumbers) { + this.phoneNumbers = phoneNumbers; + } + + public void addTalisman(String name) { + talismans.add(name); + } + + public List getTalismans() { + return talismans; + } + + public void setTalismans(List talismans) { + this.talismans = talismans; + } + + public String toString() { + return getFirstname() + " " + getLastname(); + } +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/PhoneNumber.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/PhoneNumber.java new file mode 100644 index 0000000000..eecbc0e558 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/PhoneNumber.java @@ -0,0 +1,78 @@ +/* + * 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 . + */ + +package org.hibernate.test.domain; + +import java.io.Serializable; + +/** + * PhoneNumber + */ +public class PhoneNumber implements Serializable { + private long personId = 0; + private String numberType = "home"; + private long phone = 0; + + public long getPersonId() { + return personId; + } + + public void setPersonId(long personId) { + this.personId = personId; + } + + public String getNumberType() { + return numberType; + } + + public void setNumberType(String numberType) { + this.numberType = numberType; + } + + public long getPhone() { + return phone; + } + + public void setPhone(long phone) { + this.phone = phone; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((numberType == null) ? 0 : numberType.hashCode()); + result = prime * result + (int)(personId ^ (personId >>> 32)); + result = prime * result + (int)(phone ^ (phone >>> 32)); + return result; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final PhoneNumber other = (PhoneNumber)obj; + if (numberType == null) { + if (other.numberType != null) + return false; + } else if (!numberType.equals(other.numberType)) + return false; + if (personId != other.personId) + return false; + if (phone != other.phone) + return false; + return true; + } + + public String toString() { + return numberType + ":" + phone; + } + +} diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/UuidItem.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/UuidItem.java new file mode 100644 index 0000000000..46c72b5047 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/UuidItem.java @@ -0,0 +1,37 @@ +/* + * 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 . + */ +package org.hibernate.test.domain; + +public class UuidItem { + private String id; + private String name; + private String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/hibernate-jcache/src/test/java/org/hibernate/test/domain/VersionedItem.java b/hibernate-jcache/src/test/java/org/hibernate/test/domain/VersionedItem.java new file mode 100644 index 0000000000..a909741978 --- /dev/null +++ b/hibernate-jcache/src/test/java/org/hibernate/test/domain/VersionedItem.java @@ -0,0 +1,19 @@ +/* + * 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 . + */ +package org.hibernate.test.domain; + +public class VersionedItem extends Item { + private Long version; + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } +} diff --git a/hibernate-jcache/src/test/resources/hibernate-config/domain/Account.hbm.xml b/hibernate-jcache/src/test/resources/hibernate-config/domain/Account.hbm.xml new file mode 100644 index 0000000000..b846c22a7a --- /dev/null +++ b/hibernate-jcache/src/test/resources/hibernate-config/domain/Account.hbm.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/hibernate-jcache/src/test/resources/hibernate-config/domain/Event.hbm.xml b/hibernate-jcache/src/test/resources/hibernate-config/domain/Event.hbm.xml new file mode 100644 index 0000000000..97aaf79289 --- /dev/null +++ b/hibernate-jcache/src/test/resources/hibernate-config/domain/Event.hbm.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-jcache/src/test/resources/hibernate-config/domain/HolidayCalendar.hbm.xml b/hibernate-jcache/src/test/resources/hibernate-config/domain/HolidayCalendar.hbm.xml new file mode 100644 index 0000000000..635fe04a84 --- /dev/null +++ b/hibernate-jcache/src/test/resources/hibernate-config/domain/HolidayCalendar.hbm.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-jcache/src/test/resources/hibernate-config/domain/Item.hbm.xml b/hibernate-jcache/src/test/resources/hibernate-config/domain/Item.hbm.xml new file mode 100644 index 0000000000..90d23079dd --- /dev/null +++ b/hibernate-jcache/src/test/resources/hibernate-config/domain/Item.hbm.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-jcache/src/test/resources/hibernate-config/domain/Person.hbm.xml b/hibernate-jcache/src/test/resources/hibernate-config/domain/Person.hbm.xml new file mode 100644 index 0000000000..97096adcf3 --- /dev/null +++ b/hibernate-jcache/src/test/resources/hibernate-config/domain/Person.hbm.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-jcache/src/test/resources/hibernate-config/domain/PhoneNumber.hbm.xml b/hibernate-jcache/src/test/resources/hibernate-config/domain/PhoneNumber.hbm.xml new file mode 100644 index 0000000000..733650c4fb --- /dev/null +++ b/hibernate-jcache/src/test/resources/hibernate-config/domain/PhoneNumber.hbm.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-jcache/src/test/resources/hibernate-config/hibernate.cfg.xml b/hibernate-jcache/src/test/resources/hibernate-config/hibernate.cfg.xml new file mode 100644 index 0000000000..af8f9ff7a6 --- /dev/null +++ b/hibernate-jcache/src/test/resources/hibernate-config/hibernate.cfg.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + org.h2.Driver + jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE + sa + + + + 1 + + + + org.hibernate.dialect.H2Dialect + + + + thread + + true + true + true + org.hibernate.cache.jcache.JCacheRegionFactory + /hibernate-config/ehcache.xml + + true + + + + + + + + + + + + diff --git a/hibernate-jcache/src/test/resources/hibernate.properties b/hibernate-jcache/src/test/resources/hibernate.properties new file mode 100644 index 0000000000..a643fcdfae --- /dev/null +++ b/hibernate-jcache/src/test/resources/hibernate.properties @@ -0,0 +1,17 @@ +# +# 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 . +# +hibernate.dialect @db.dialect@ +hibernate.connection.driver_class @jdbc.driver@ +hibernate.connection.url @jdbc.url@ +hibernate.connection.username @jdbc.user@ +hibernate.connection.password @jdbc.pass@ + +hibernate.connection.pool_size 5 + +hibernate.cache.region_prefix hibernate.test + +hibernate.service.allow_crawling=false \ No newline at end of file diff --git a/hibernate-jcache/src/test/resources/log4j.properties b/hibernate-jcache/src/test/resources/log4j.properties new file mode 100644 index 0000000000..f29002cc37 --- /dev/null +++ b/hibernate-jcache/src/test/resources/log4j.properties @@ -0,0 +1,17 @@ +# +# 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 . +# +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n + +log4j.rootLogger=info, stdout + +log4j.logger.org.hibernate.test=info + +# SQL Logging - HHH-6833 +log4j.logger.org.hibernate.SQL=debug \ No newline at end of file