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 org.hibernate.internal.CoreMessageLogger;
import static org.jboss.logging.Logger.Level.ERROR;
import static org.jboss.logging.Logger.Level.WARN; import static org.jboss.logging.Logger.Level.WARN;
/** /**
@ -36,4 +37,12 @@ public interface JCacheMessageLogger extends CoreMessageLogger {
id = NAMESPACE + 2 id = NAMESPACE + 2
) )
void attemptToRestopAlreadyStoppedJCacheProvider(); 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.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.jcache.time.Timestamper;
import org.hibernate.cache.spi.CacheDataDescription; import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion; import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion; import org.hibernate.cache.spi.EntityRegion;
@ -187,11 +188,11 @@ public class JCacheRegionFactory implements RegionFactory {
} }
static long nextTS() { static long nextTS() {
return System.currentTimeMillis() / 100; return Timestamper.next();
} }
static int timeOut() { 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) { 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.EnumSet;
import java.util.Set; import java.util.Set;
import javax.cache.Cache; import javax.cache.Cache;
import javax.cache.processor.EntryProcessor;
import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.spi.CacheDataDescription; import org.hibernate.cache.spi.CacheDataDescription;
@ -33,17 +32,19 @@ public class JCacheTransactionalDataRegion extends JCacheRegion implements Trans
this.options = options; this.options = options;
} }
@Override
public boolean isTransactionAware() { public boolean isTransactionAware() {
return false; return false;
} }
@Override
public CacheDataDescription getCacheDataDescription() { public CacheDataDescription getCacheDataDescription() {
return metadata; return metadata;
} }
protected void throwIfAccessTypeUnsupported(AccessType accessType) { protected void throwIfAccessTypeUnsupported(AccessType accessType) {
if ( supportedAccessTypes().contains( accessType ) ) { if ( !supportedAccessTypes().contains( accessType ) ) {
throw new UnsupportedOperationException( "This doesn't JCacheTransactionalDataRegion doesn't support " + accessType ); throw new UnsupportedOperationException( "JCacheTransactionalDataRegion doesn't support " + accessType );
} }
} }
@ -67,12 +68,15 @@ public class JCacheTransactionalDataRegion extends JCacheRegion implements Trans
cache.put( key, value ); 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() { public SessionFactoryOptions getSessionFactoryOptions() {
return options; 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.Comparator;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong; 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.CacheException;
import org.hibernate.cache.jcache.JCacheMessageLogger;
import org.hibernate.cache.jcache.JCacheTransactionalDataRegion; import org.hibernate.cache.jcache.JCacheTransactionalDataRegion;
import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.jboss.logging.Logger;
/** /**
* @author Alex Snaps * @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 R region;
protected final Comparator versionComparator; protected final Comparator versionComparator;
private final UUID uuid = UUID.randomUUID(); private final UUID uuid = UUID.randomUUID();
private final AtomicLong nextLockId = new AtomicLong(); private final AtomicLong nextLockId = new AtomicLong();
private final AtomicLong nextItemId = new AtomicLong();
public AbstractReadWriteRegionAccessStrategy(R region) { public AbstractReadWriteRegionAccessStrategy(R region) {
this.versionComparator = region.getCacheDataDescription().getVersionComparator(); 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 { public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
return region.invoke( while (true) {
key, new EntryProcessor<Object, Object, Boolean>() { Lockable item = (Lockable) region.get( key );
@Override
public Boolean process(MutableEntry<Object, Object> entry, Object... args) if (item == null) {
throws EntryProcessorException { /*
final Lockable item = (Lockable) entry.getValue(); * If the item is null due a softlock being evicted... then this
final boolean writeable = item == null || item.isWriteable( * is wrong, the in-doubt soft-lock could get replaced with the
(Long) args[1], * old value. All that can be done from a JCache perspective is
args[2], * to log a warning.
versionComparator */
); if (region.putIfAbsent( key, new Item( value, version, txTimestamp, nextItemId() ))) {
if ( writeable ) { return true;
entry.setValue( new Item( args[0], args[2], region.nextTimestamp() ) ); }
return true; }
} else if (item.isWriteable( txTimestamp, version, versionComparator )) {
else { if (region.replace( key, item, new Item( value, version, txTimestamp, nextItemId() ))) {
return false; return true;
} }
} }
}, value, txTimestamp, version); else {
return false;
}
}
} }
public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) 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 { public SoftLock lockItem(SharedSessionContractImplementor session, Object key, Object version) throws CacheException {
return region.invoke( long timeout = region.nextTimestamp() + region.getTimeout();
key, new EntryProcessor<Object, Object, SoftLock>() { while (true) {
@Override Lockable item = (Lockable) region.get( key );
public SoftLock process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException { if ( item == null ) {
final Lockable item = (Lockable) entry.getValue(); /*
final long timeout = region.nextTimestamp() + region.getTimeout(); * What happens here if a previous soft-lock was evicted to make
final Lock lock = ( item == null ) ? new Lock( * this null.
timeout, */
(UUID) args[0], Lock lock = new Lock(timeout, uuid, nextLockId(), version);
(Long) args[1], if (region.putIfAbsent( key, lock )) {
args[2] return lock;
) }
: item.lock( timeout, (UUID) args[0], (Long) args[1] ); }
entry.setValue( lock ); else {
return lock; Lock lock = item.lock( timeout, uuid, nextLockId() );
} if (region.replace(key, item, lock)) {
}, uuid, nextLockId(), version return lock;
); }
}
}
} }
public void unlockItem(SharedSessionContractImplementor session, Object key, SoftLock lock) throws CacheException { public void unlockItem(SharedSessionContractImplementor session, Object key, SoftLock lock) throws CacheException {
region.invoke( while (true) {
key, new EntryProcessor<Object, Object, Void>() { Lockable item = (Lockable) region.get( key );
@Override
public Void process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException {
final Lockable item = (Lockable) entry.getValue();
if ( (item != null) && item.isUnlockable( (SoftLock) args[0] ) ) { if (item != null && item.isUnlockable( lock )) {
( (Lock) item ).unlock( region.nextTimestamp() ); if (region.replace(key, item, ((Lock) item ).unlock(region.nextTimestamp()))) {
entry.setValue( item ); return;
} }
else { }
entry.setValue( null ); else {
} handleMissingLock( key, item );
return null; return;
} }
}, lock); }
} }
public void remove(SharedSessionContractImplementor session, Object key) throws CacheException { public void remove(SharedSessionContractImplementor session, Object key) throws CacheException {
region.remove( key ); //this access strategy is asynchronous
} }
public void removeAll() throws CacheException { public void removeAll() throws CacheException {
@ -141,13 +148,25 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
} }
public void unlockRegion(SoftLock lock) throws CacheException { public void unlockRegion(SoftLock lock) throws CacheException {
throw new UnsupportedOperationException( "JCache doesn't support region locking" ); region.clear();
} }
private long nextLockId() { private long nextLockId() {
return nextLockId.getAndIncrement(); 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. * 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 value;
private final Object version; private final Object version;
private final long timestamp; private final long timestamp;
private final long itemId;
/** /**
* Creates an unlocked item wrapping the given value with a version and creation timestamp. * 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.value = value;
this.version = version; this.version = version;
this.timestamp = timestamp; this.timestamp = timestamp;
this.itemId = itemId;
} }
@Override @Override
@ -224,6 +245,29 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
public Lock lock(long timeout, UUID uuid, long lockId) { public Lock lock(long timeout, UUID uuid, long lockId) {
return new Lock( timeout, uuid, lockId, version ); 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 long lockId;
private final Object version; private final Object version;
private long timeout; private final long timeout;
private boolean concurrent; private final boolean concurrent;
private int multiplicity = 1; private final int multiplicity;
private long unlockTimestamp; private final long unlockTimestamp;
/** /**
* Creates a locked item with the given identifiers and object version. * Creates a locked item with the given identifiers and object version.
*/ */
public Lock(long timeout, UUID sourceUuid, long lockId, 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.lockId = lockId;
this.version = version; this.version = version;
this.sourceUuid = sourceUuid;
this.timeout = timeout;
this.unlockTimestamp = unlockTimestamp;
this.multiplicity = multiplicity;
this.concurrent = concurrent;
} }
@Override @Override
@ -279,7 +332,15 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
@Override @Override
public boolean isUnlockable(SoftLock lock) { 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 @Override
@ -289,7 +350,8 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
return true; return true;
} }
else if ( o instanceof Lock ) { 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 { else {
return false; return false;
@ -299,11 +361,7 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
@Override @Override
public int hashCode() { public int hashCode() {
final int hash = ( sourceUuid != null ? sourceUuid.hashCode() : 0 ); final int hash = ( sourceUuid != null ? sourceUuid.hashCode() : 0 );
int temp = (int) lockId; return hash ^ Long.hashCode( lockId );
for ( int i = 1; i < Long.SIZE / Integer.SIZE; i++ ) {
temp ^= ( lockId >>> ( i * Integer.SIZE ) );
}
return hash + temp;
} }
/** /**
@ -315,18 +373,22 @@ public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactional
@Override @Override
public Lock lock(long timeout, UUID uuid, long lockId) { public Lock lock(long timeout, UUID uuid, long lockId) {
concurrent = true; return new Lock( timeout, this.sourceUuid, this.lockId, this.version,
multiplicity++; 0, this.multiplicity + 1, true );
this.timeout = timeout;
return this;
} }
/** /**
* Unlocks this Lock, and timestamps the unlock event. * Unlocks this Lock, and timestamps the unlock event.
*/ */
public void unlock(long timestamp) { public Lock unlock(long timestamp) {
if ( --multiplicity == 0 ) { if (multiplicity == 1) {
unlockTimestamp = timestamp; 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 @Override
public void unlockRegion(SoftLock lock) throws CacheException { public void unlockRegion(SoftLock lock) throws CacheException {
// no op evictAll();
} }
@Override @Override
public void remove(SharedSessionContractImplementor session, Object key) throws CacheException { public void remove(SharedSessionContractImplementor session, Object key) throws CacheException {
region.remove( key ); // jcache only supports asynchronous access strategies
} }
@Override @Override
public void removeAll() throws CacheException { public void removeAll() throws CacheException {
region.clear(); evictAll();
} }
@Override @Override

View File

@ -7,10 +7,12 @@
package org.hibernate.cache.jcache.access; package org.hibernate.cache.jcache.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory; import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheCollectionRegion; import org.hibernate.cache.jcache.JCacheCollectionRegion;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
/** /**
@ -34,4 +36,9 @@ public class NonStrictCollectionRegionAccessStrategy
return DefaultCacheKeysFactory.getCollectionId( cacheKey ); 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) { public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.getEntityId( 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; 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.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory; import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheEntityRegion; import org.hibernate.cache.jcache.JCacheEntityRegion;
@ -38,21 +34,7 @@ public class ReadWriteEntityRegionAccessStrategy
@Override @Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException { public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException {
return region.invoke( return region.putIfAbsent( key, new Item(value, version, region.nextTimestamp(), nextItemId() ));
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()
);
} }
@Override @Override
@ -64,33 +46,28 @@ public class ReadWriteEntityRegionAccessStrategy
@Override @Override
public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException { throws CacheException {
return region.invoke( while (true) {
key, new EntryProcessor<Object, Object, Boolean>() { Lockable item = (Lockable) region.get( key );
@Override
public Boolean process(MutableEntry<Object, Object> 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;
}
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 @Override

View File

@ -7,10 +7,6 @@
package org.hibernate.cache.jcache.access; 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.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory; import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheNaturalIdRegion; import org.hibernate.cache.jcache.JCacheNaturalIdRegion;
@ -37,21 +33,7 @@ public class ReadWriteNaturalIdRegionAccessStrategy
@Override @Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException { public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
return region.invoke( return region.putIfAbsent( key, new Item( value, null, region.nextTimestamp(), nextItemId() ));
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()
);
} }
@Override @Override
@ -63,33 +45,28 @@ public class ReadWriteNaturalIdRegionAccessStrategy
@Override @Override
public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, SoftLock lock) public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, SoftLock lock)
throws CacheException { throws CacheException {
return region.invoke( while (true) {
key, new EntryProcessor<Object, Object, Boolean>() { Lockable item = (Lockable) region.get( key );
@Override
public Boolean process(MutableEntry<Object, Object> 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;
}
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 @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