Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HHH-6974 Add second level caching of NaturalId resolutions #266

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/Cache.java
Expand Up @@ -97,6 +97,27 @@ public interface Cache {
*/
public void evictEntityRegions();

/**
* Evicts all naturalId data from the given region (i.e. for all entities of
* type).
*
* @param naturalIdClass The naturalId class.
*/
public void evictNaturalIdRegion(Class naturalIdClass);

/**
* Evicts all naturalId data from the given region (i.e. for all entities of
* type).
*
* @param naturalIdName The naturalId name.
*/
public void evictNaturalIdRegion(String naturalIdName);

/**
* Evict data from all naturalId regions.
*/
public void evictNaturalIdRegions();

/**
* Determine whether the cache contains data for the given collection.
* <p/>
Expand Down
Expand Up @@ -23,17 +23,18 @@
*/
package org.hibernate.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* This specifies that a property is part of the natural id of the entity.
*
* @author Nicol�s Lichtmaier
* @see NaturalIdCache
*/
@Target( { METHOD, FIELD } )
@Retention( RUNTIME )
Expand Down
@@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, 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.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Add caching strategy for the NaturalId to Id of a root entity that has a natural id
*
* @author Eric Dalquist
* @see NaturalId
*/
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface NaturalIdCache {
/** cache region name, defaults to full.entity.Name##NaturalId */
String region() default "";
}
Expand Up @@ -30,6 +30,7 @@
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.PluralAttributeBinding;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.VersionType;

/**
Expand Down Expand Up @@ -90,6 +91,14 @@ public static CacheDataDescriptionImpl decode(PluralAttributeBinding model) {
);
}

public static CacheDataDescriptionImpl decode(EntityPersister persister) {
return new CacheDataDescriptionImpl(
!persister.getEntityMetamodel().hasImmutableNaturalId(),
false,
null
);
}

private static Comparator getVersionComparator(EntityBinding model ) {
Comparator versionComparator = null;
if ( model.isVersioned() ) {
Expand Down
Expand Up @@ -30,6 +30,7 @@
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
Expand Down Expand Up @@ -67,6 +68,11 @@ public EntityRegion buildEntityRegion(String regionName, Properties properties,
throws CacheException {
throw new NoCachingEnabledException();
}

public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
throw new NoCachingEnabledException();
}

public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
Expand Down
@@ -0,0 +1,158 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008-2011, 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.spi;

import java.io.Serializable;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
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().
*
* @author Gavin King
*/
public class NaturalIdCacheKey implements Serializable {
private final Serializable[] naturalId;
private final Type[] naturalIdTypes;
private final String entityName;
private final String tenantId;
private final int hashCode;
private final String toString;

/**
* Construct a new key for a collection or entity instance.
* Note that an entity name should always be the root entity
* name, not a subclass entity name.
*
* @param naturalId The naturalId associated with the cached data
* @param persister The persister for the entity
* @param session The session for which we are caching
*/
public NaturalIdCacheKey(
final Object[] naturalId,
final EntityPersister persister,
final SessionImplementor session) {

this.entityName = persister.getEntityName();
this.tenantId = session.getTenantIdentifier();

final Serializable[] disassembledNaturalId = new Serializable[naturalId.length];
final Type[] naturalIdTypes = new Type[naturalId.length];
final StringBuilder str = new StringBuilder(entityName).append( "##NaturalId[" );


final SessionFactoryImplementor factory = session.getFactory();
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
final Type[] propertyTypes = persister.getPropertyTypes();

final int prime = 31;
int result = 1;
result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() );
result = prime * result + ( ( this.tenantId == null ) ? 0 : this.tenantId.hashCode() );
for ( int i = 0; i < naturalId.length; i++ ) {
final Type type = propertyTypes[naturalIdPropertyIndexes[i]];
final Object value = naturalId[i];

result = prime * result + type.getHashCode( value, factory );

disassembledNaturalId[i] = type.disassemble( value, session, null );

naturalIdTypes[i] = type;

str.append( type.toLoggableString( value, factory ) );
if (i + 1 < naturalId.length) {
str.append( ", " );
}
}
str.append( "]" );

this.naturalId = disassembledNaturalId;
this.naturalIdTypes = naturalIdTypes;
this.hashCode = result;
this.toString = str.toString();
}

public String getEntityName() {
return entityName;
}

public String getTenantId() {
return tenantId;
}

public Serializable[] getNaturalId() {
return naturalId;
}

@Override
public String toString() {
return this.toString;
}

@Override
public int hashCode() {
return this.hashCode;
}

@Override
public boolean equals(Object obj) {
if ( this == obj )
return true;
if ( obj == null )
return false;
if ( getClass() != obj.getClass() )
return false;
NaturalIdCacheKey other = (NaturalIdCacheKey) obj;
if ( entityName == null ) {
if ( other.entityName != null )
return false;
}
else if ( !entityName.equals( other.entityName ) )
return false;
if ( tenantId == null ) {
if ( other.tenantId != null )
return false;
}
else if ( !tenantId.equals( other.tenantId ) )
return false;
if ( naturalId == other.naturalId )
return true;
if ( naturalId == null || other.naturalId == null )
return false;
int length = naturalId.length;
if ( other.naturalId.length != length )
return false;
for ( int i = 0; i < length; i++ ) {
if ( !this.naturalIdTypes[i].isEqual( naturalId[i], other.naturalId[i] ) ) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,47 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008-2011, 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.spi;

import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;

/**
* Defines the contract for a cache region which will specifically be used to
* store naturalId data.
*
* @author Eric Dalquist
*/
public interface NaturalIdRegion extends TransactionalDataRegion {

/**
* Build an access strategy for the requested access type.
*
* @param accessType The type of access strategy to build; never null.
* @return The appropriate strategy contract for accessing this region
* for the requested type of access.
* @throws org.hibernate.cache.CacheException Usually indicates mis-configuration.
*/
public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException;
}
Expand Up @@ -106,6 +106,20 @@ public interface RegionFactory extends Service {
public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException;

/**
* Build a cache region specialized for storing NaturalId to Primary Key mappings.
*
* @param regionName The name of the region.
* @param properties Configuration properties.
* @param metadata Information regarding the type of data to be cached
*
* @return The built region
*
* @throws CacheException Indicates problems building the region.
*/
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException;

/**
* Build a cache region specialized for storing collection data.
*
Expand Down