diff --git a/hibernate-core/src/main/java/org/hibernate/cache/internal/TenantAwareCacheKey.java b/hibernate-core/src/main/java/org/hibernate/cache/internal/TenantAwareCacheKey.java new file mode 100644 index 0000000000..0b35bcd7f7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cache/internal/TenantAwareCacheKey.java @@ -0,0 +1,54 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.internal; + +import java.io.Serializable; + +import org.hibernate.cache.spi.CacheKey; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.type.Type; + +/** + * Variation of CacheKey to be used when multi-tenancy is applied. + * The tenantId field was not simply added to the superclass because of performance considerations: + * CacheKey instances are have a very high allocation frequency and this field would enlarge the + * size by 50%. + * + * @author Sanne Grinovero + */ +public class TenantAwareCacheKey extends CacheKey { + + private final String tenantId; + + public TenantAwareCacheKey(Serializable id, Type type, String entityOrRoleName, SessionFactoryImplementor factory, String tenantId) { + super( id, type, entityOrRoleName, factory, tenantId ); + this.tenantId = tenantId; + } + + @Override + public String getTenantId() { + return tenantId; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheKey.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheKey.java index 3fa4b5058f..ac9a815bd6 100755 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheKey.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheKey.java @@ -33,14 +33,20 @@ import org.hibernate.type.Type; * Allows multiple entity classes / collection roles to be stored in the same cache region. Also allows for composite * keys which do not properly implement equals()/hashCode(). * + * Note on performance: this class is allocated very heavily! + * Make sure the allocation cost stays as low as possible: + * if you need to add fields for not-so-common use cases, + * it's probably better to create an ad-hoc implementation + * extending this one. + * * @author Gavin King * @author Steve Ebersole + * @author Sanne Grinovero */ public class CacheKey implements Serializable { + private final Serializable key; private final Type type; - private final String entityOrRoleName; - private final String tenantId; private final int hashCode; /** @@ -58,16 +64,25 @@ public class CacheKey implements Serializable { final Serializable id, final Type type, final String entityOrRoleName, - final String tenantId, final SessionFactoryImplementor factory) { - this.key = id; - this.type = type; - this.entityOrRoleName = entityOrRoleName; - this.tenantId = tenantId; - this.hashCode = calculateHashCode( type, factory ); + this( id, type, entityOrRoleName, factory, null ); } - private int calculateHashCode(Type type, SessionFactoryImplementor factory) { + /** + * Used by subclasses: need to specify a tenantId + */ + protected CacheKey( + final Serializable id, + final Type type, + final String entityOrRoleName, + final SessionFactoryImplementor factory, + final String tenantId) { + this.key = id; + this.type = type; + this.hashCode = calculateHashCode( type, factory, tenantId ); + } + + private int calculateHashCode(Type type, SessionFactoryImplementor factory, String tenantId) { int result = type.getHashCode( key, factory ); result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); return result; @@ -77,12 +92,8 @@ public class CacheKey implements Serializable { return key; } - public String getEntityOrRoleName() { - return entityOrRoleName; - } - public String getTenantId() { - return tenantId; + return null; } @Override @@ -97,20 +108,14 @@ public class CacheKey implements Serializable { //hashCode is part of this check since it is pre-calculated and hash must match for equals to be true return false; } - CacheKey that = (CacheKey) other; - return EqualsHelper.equals( entityOrRoleName, that.entityOrRoleName ) && - type.isEqual( key, that.key ) && - EqualsHelper.equals( tenantId, that.tenantId ); + //Warning: this equals implementation needs to work correctly also for the subclass + final CacheKey that = (CacheKey) other; + return EqualsHelper.equals( getTenantId(), that.getTenantId() ) + && type.isEqual( key, that.key ); } @Override public int hashCode() { return hashCode; } - - @Override - public String toString() { - // Mainly for OSCache - return entityOrRoleName + '#' + key.toString();//"CacheKey#" + type.toString(key, sf); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSessionImpl.java index 954d58bfe9..d6ac52e42e 100755 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSessionImpl.java @@ -38,6 +38,7 @@ import org.hibernate.ScrollableResults; import org.hibernate.SessionEventListener; import org.hibernate.SessionException; import org.hibernate.SharedSessionContract; +import org.hibernate.cache.internal.TenantAwareCacheKey; import org.hibernate.cache.spi.CacheKey; import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess; @@ -250,7 +251,13 @@ public abstract class AbstractSessionImpl implements Serializable, SharedSession @Override public CacheKey generateCacheKey(Serializable id, Type type, String entityOrRoleName) { - return new CacheKey( id, type, entityOrRoleName, getTenantIdentifier(), getFactory() ); + final String tenantIdentifier = getTenantIdentifier(); + if ( tenantIdentifier == null ) { + return new CacheKey( id, type, entityOrRoleName, getFactory() ); + } + else { + return new TenantAwareCacheKey( id, type, entityOrRoleName, getFactory(), tenantIdentifier ); + } } private transient JdbcConnectionAccess jdbcConnectionAccess; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CacheImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/CacheImpl.java index 7735b4b8d0..de22135b45 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CacheImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CacheImpl.java @@ -119,11 +119,11 @@ public class CacheImpl implements CacheImplementor { } private CacheKey buildCacheKey(Serializable identifier, EntityPersister p) { + // have to assume non tenancy return new CacheKey( identifier, p.getIdentifierType(), p.getRootEntityName(), - null, // have to assume non tenancy sessionFactory ); } @@ -197,11 +197,11 @@ public class CacheImpl implements CacheImplementor { } private CacheKey buildCacheKey(Serializable ownerIdentifier, CollectionPersister p) { + // have to assume non tenancy return new CacheKey( ownerIdentifier, p.getKeyType(), p.getRole(), - null, // have to assume non tenancy sessionFactory ); }