HHH-10770 JCache provider updates

* Steer away from EntryProcessor
* Add tests
This commit is contained in:
Chris Dennis 2016-04-22 16:08:29 -04:00 committed by Steve Ebersole
parent a872885a43
commit 2ddefd615b
32 changed files with 2143 additions and 183 deletions

View File

@ -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);
}

View File

@ -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) {

View File

@ -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> T invoke(Object key, EntryProcessor<Object, Object, T> entryProcessor, Object... args) {
return cache.invoke( key, entryProcessor, args);
}
}

View File

@ -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<R extends JCacheTransactionalDataRegion> {
abstract class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactionalDataRegion> {
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<R extends JCacheTransactional
}
public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> 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() ) );
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;
}
}
}, value, txTimestamp, version);
}
public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
@ -79,49 +88,47 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
}
public SoftLock lockItem(SharedSessionContractImplementor session, Object key, Object version) throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, SoftLock>() {
@Override
public SoftLock process(MutableEntry<Object, Object> 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 );
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;
}
}, uuid, nextLockId(), version
);
}
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<Object, Object, Void>() {
@Override
public Void process(MutableEntry<Object, Object> 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 );
if (item != null && item.isUnlockable( lock )) {
if (region.replace(key, item, ((Lock) item ).unlock(region.nextTimestamp()))) {
return;
}
}
else {
entry.setValue( null );
handleMissingLock( key, item );
return;
}
return null;
}
}, lock);
}
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<R extends JCacheTransactional
}
public void unlockRegion(SoftLock lock) throws CacheException {
throw new UnsupportedOperationException( "JCache doesn't support region locking" );
region.clear();
}
private long nextLockId() {
return nextLockId.getAndIncrement();
}
protected long nextItemId() {
return nextItemId.getAndIncrement();
}
protected void handleMissingLock(Object key, Lockable lock) {
LOG.missingLock( region, key, lock );
long ts = region.nextTimestamp() + region.getTimeout();
// create new lock that times out immediately
Lock newLock = new Lock( ts, uuid, nextLockId.getAndIncrement(), null ).unlock( ts );
region.put( key, newLock );
}
/**
* Interface type implemented by all wrapper objects in the cache.
*/
@ -189,14 +208,16 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
private final Object value;
private final Object version;
private final long timestamp;
private final long itemId;
/**
* Creates an unlocked item wrapping the given value with a version and creation timestamp.
*/
Item(Object value, Object version, long timestamp) {
Item(Object value, Object version, long timestamp, long itemId) {
this.value = value;
this.version = version;
this.timestamp = timestamp;
this.itemId = itemId;
}
@Override
@ -224,6 +245,29 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
public Lock lock(long timeout, UUID uuid, long lockId) {
return new Lock( timeout, uuid, lockId, version );
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
else if (obj instanceof Item) {
return itemId == ((Item) obj).itemId;
}
else {
return false;
}
}
@Override
public int hashCode() {
return Long.hashCode( itemId );
}
@Override
public String toString() {
return value.toString() + " version: " + version;
}
}
/**
@ -236,19 +280,28 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
private final long lockId;
private final Object version;
private long timeout;
private boolean concurrent;
private int multiplicity = 1;
private long unlockTimestamp;
private final long timeout;
private final boolean concurrent;
private final int multiplicity;
private final long unlockTimestamp;
/**
* Creates a locked item with the given identifiers and object version.
*/
public Lock(long timeout, UUID sourceUuid, long lockId, Object version) {
this.timeout = timeout;
this(timeout, sourceUuid, lockId, version, 0, 1, false);
}
private Lock(long timeout, UUID sourceUuid, long lockId, Object version,
long unlockTimestamp, int multiplicity, boolean concurrent) {
this.sourceUuid = sourceUuid;
this.lockId = lockId;
this.version = version;
this.sourceUuid = sourceUuid;
this.timeout = timeout;
this.unlockTimestamp = unlockTimestamp;
this.multiplicity = multiplicity;
this.concurrent = concurrent;
}
@Override
@ -279,7 +332,15 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
@Override
public boolean isUnlockable(SoftLock lock) {
return equals( lock );
if ( lock == this ) {
return true;
}
else if ( lock instanceof Lock ) {
return (lockId == ((Lock) lock).lockId) && sourceUuid.equals(((Lock) lock).sourceUuid);
}
else {
return false;
}
}
@Override
@ -289,7 +350,8 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
return true;
}
else if ( o instanceof Lock ) {
return ( lockId == ( (Lock) o ).lockId ) && sourceUuid.equals( ( (Lock) o ).sourceUuid );
return (lockId == ((Lock)o ).lockId) && sourceUuid.equals( ( (Lock) o ).sourceUuid )
&& (multiplicity == ((Lock) o).multiplicity);
}
else {
return false;
@ -299,11 +361,7 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
@Override
public int hashCode() {
final int hash = ( sourceUuid != null ? sourceUuid.hashCode() : 0 );
int temp = (int) lockId;
for ( int i = 1; i < Long.SIZE / Integer.SIZE; i++ ) {
temp ^= ( lockId >>> ( i * Integer.SIZE ) );
}
return hash + temp;
return hash ^ Long.hashCode( lockId );
}
/**
@ -315,18 +373,22 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
@Override
public Lock lock(long timeout, UUID uuid, long lockId) {
concurrent = true;
multiplicity++;
this.timeout = timeout;
return this;
return new Lock( timeout, this.sourceUuid, this.lockId, this.version,
0, this.multiplicity + 1, true );
}
/**
* Unlocks this Lock, and timestamps the unlock event.
*/
public void unlock(long timestamp) {
if ( --multiplicity == 0 ) {
unlockTimestamp = timestamp;
public Lock unlock(long timestamp) {
if (multiplicity == 1) {
return new Lock(timeout, sourceUuid, lockId, version,
timestamp, 0, concurrent );
}
else {
return new Lock(timeout, sourceUuid, lockId, version,
0, multiplicity - 1, concurrent );
}
}

View File

@ -68,17 +68,17 @@ abstract class JCacheRegionAccessStrategy<R extends JCacheTransactionalDataRegio
@Override
public void unlockRegion(SoftLock lock) throws CacheException {
// no op
evictAll();
}
@Override
public void remove(SharedSessionContractImplementor session, Object key) throws CacheException {
region.remove( key );
// jcache only supports asynchronous access strategies
}
@Override
public void removeAll() throws CacheException {
region.clear();
evictAll();
}
@Override

View File

@ -7,10 +7,12 @@
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheCollectionRegion;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.collection.CollectionPersister;
/**
@ -34,4 +36,9 @@ public class NonStrictCollectionRegionAccessStrategy
return DefaultCacheKeysFactory.getCollectionId( cacheKey );
}
@Override
public void remove(SharedSessionContractImplementor session, Object key) throws CacheException {
evict( key );
}
}

View File

@ -60,4 +60,9 @@ public class NonStrictEntityRegionAccessStrategy extends JCacheRegionAccessStrat
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.getEntityId( cacheKey );
}
@Override
public void remove(SharedSessionContractImplementor session, Object key) throws CacheException {
evict( key );
}
}

View File

@ -6,10 +6,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.JCacheEntityRegion;
@ -38,21 +34,7 @@ public class ReadWriteEntityRegionAccessStrategy
@Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> 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<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> 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[3] ) ) {
final Lock lockItem = (Lock) item;
if ( item != null && item.isUnlockable( lock ) ) {
Lock lockItem = (Lock) item;
if ( lockItem.wasLockedConcurrently() ) {
lockItem.unlock( (Long) args[1] );
entry.setValue( lockItem );
if (region.replace( key, lockItem, lockItem.unlock( region.nextTimestamp() ))) {
return false;
}
}
else {
entry.setValue( new Item( args[0], args[1], (Long) args[4] ) );
if (region.replace( key, lockItem, new Item(value, currentVersion, region.nextTimestamp(), nextItemId() ))) {
return true;
}
}
}
else {
entry.setValue( null );
handleMissingLock( key, item );
return false;
}
}
}, value, currentVersion, previousVersion, lock, region.nextTimestamp()
);
}
@Override

View File

@ -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<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> 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<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> 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[1] ) ) {
if ( item != null && item.isUnlockable( lock ) ) {
final Lock lockItem = (Lock) item;
if ( lockItem.wasLockedConcurrently() ) {
lockItem.unlock( region.nextTimestamp() );
entry.setValue( lockItem );
if (region.replace( key, lockItem, lockItem.unlock( region.nextTimestamp() ) )) {
return false;
}
}
else {
entry.setValue( new Item( args[0], null, (Long) args[2] ) );
if (region.replace( key, lockItem, new Item(value, null, region.nextTimestamp(), nextItemId() ))) {
return true;
}
}
}
else {
entry.setValue( null );
handleMissingLock( key, item );
return false;
}
}
}, value, lock, region.nextTimestamp()
);
}
@Override

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.
* <p/>
* 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() {
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.jcache.JCacheTransactionalDataRegion;
/**
* @author Alex Snaps
*/
public class ItemValueExtractor extends AbstractReadWriteRegionAccessStrategy<JCacheTransactionalDataRegion> {
/**
* Creates a read/write cache access strategy around the given cache region.
*/
public ItemValueExtractor(JCacheTransactionalDataRegion region) {
super(region);
}
public static <T> 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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<Event>) 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 ) );
}
}

View 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.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;
}
}
}

View File

@ -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.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<String> 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<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<>();
@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<>();
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<>();
@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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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();
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernate.test.domain.Account" table="ACCOUNT" lazy="false">
<id name="id" column="ACCOUNT_ID">
<generator class="native"/>
</id>
<many-to-one name="person" class="org.hibernate.test.domain.Person" cascade="save-update,lock"
column="person_id"
unique="true"
not-null="true"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,32 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernate.test.domain.Event" table="EVENTS">
<cache usage="read-write"/>
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
<many-to-one name="organizer" column="EVENT_ORGANIZER" class="org.hibernate.test.domain.Person"/>
<set name="participants" table="PERSON_EVENT" lazy="false"
inverse="true" cascade="lock">
<cache usage="read-write"/>
<key column="EVENT_ID"/>
<many-to-many column="PERSON_ID"
class="org.hibernate.test.domain.Person"/>
</set>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernate.test.domain.HolidayCalendar" table="CALENDAR" lazy="false">
<id name="id" column="CALENDAR_ID">
<generator class="native"/>
</id>
<map name="holidays" table="CALENDAR_HOLIDAYS" lazy="false">
<key column="CALENDAR_ID"/>
<map-key column="hol_date" type="date"/>
<element column="hol_name" type="string"/>
</map>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,34 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.domain">
<class name="Item" table="Items">
<cache usage="read-write"/>
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<property name="description" not-null="true"/>
</class>
<class name="VersionedItem" table="VersionedItems">
<cache usage="read-write"/>
<id name="id">
<generator class="increment"/>
</id>
<version name="version" type="long"/>
<property name="name" not-null="true"/>
<property name="description" not-null="true"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernate.test.domain.Person" table="PERSON" lazy="true">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
<property name="age"/>
<property name="firstname"/>
<property name="lastname"/>
<list name="events" table="PERSON_EVENT" lazy="true">
<key column="PERSON_ID"/>
<list-index column="EVENT_ORDER"/>
<many-to-many column="EVENT_ID" class="org.hibernate.test.domain.Event"/>
</list>
<bag name="talismans" table="PERSON_TALISMAN" lazy="true">
<key column="PERSON_ID"/>
<element type="string" column="TALISMAN_NAME"/>
</bag>
<set name="emailAddresses" table="PERSON_EMAIL_ADDR" lazy="true">
<key column="PERSON_ID"/>
<element type="string" column="EMAIL_ADDR"/>
</set>
<set name="phoneNumbers" cascade="all" lazy="true">
<key column="PERSON_ID"/>
<one-to-many class="org.hibernate.test.domain.PhoneNumber"/>
</set>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernate.test.domain.PhoneNumber" table="PHONE_NUMBERS">
<composite-id>
<key-property column="PERSON_ID" name="personId" type="java.lang.Long"/>
<key-property column="NUMBER_TYPE" name="numberType" type="java.lang.String"/>
</composite-id>
<property name="phone" type="java.lang.Long">
<column name="PHONE" precision="22" scale="0"/>
</property>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,54 @@
<?xml version='1.0' encoding='utf-8'?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<!-- <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost/TestDB</property> -->
<property name="connection.driver_class">org.h2.Driver</property>
<property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">
org.hibernate.dialect.H2Dialect
</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<property name="cache.use_query_cache">true</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_structured_entries">true</property>
<property name="cache.region.factory_class">org.hibernate.cache.jcache.JCacheRegionFactory</property>
<property name="net.sf.ehcache.configurationResourceName">/hibernate-config/ehcache.xml</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<mapping resource="hibernate-config/domain/Event.hbm.xml"/>
<mapping resource="hibernate-config/domain/Person.hbm.xml"/>
<mapping resource="hibernate-config/domain/PhoneNumber.hbm.xml"/>
<mapping resource="hibernate-config/domain/Account.hbm.xml"/>
<mapping resource="hibernate-config/domain/HolidayCalendar.hbm.xml"/>
<mapping resource="hibernate-config/domain/Item.hbm.xml"/>
</session-factory>
</hibernate-configuration>

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
#
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

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
#
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