mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
MNT-24641 Avoid duplicate key error on content upload (#2984)
MNT-24641 * On createOrGetByValue in EntityLookupCache, also cache by value * Created getCachedEntityByValue that attempt to retrieve the value only from cache * On attempt to create content URL, first check cache before attempting to create in the database avoiding a duplicate key
This commit is contained in:
@@ -28,30 +28,26 @@ package org.alfresco.repo.cache.lookup;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.Savepoint;
|
import java.sql.Savepoint;
|
||||||
|
|
||||||
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
|
import org.springframework.extensions.surf.util.ParameterCheck;
|
||||||
|
|
||||||
import org.alfresco.repo.cache.SimpleCache;
|
import org.alfresco.repo.cache.SimpleCache;
|
||||||
import org.alfresco.repo.domain.control.ControlDAO;
|
import org.alfresco.repo.domain.control.ControlDAO;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.util.Pair;
|
||||||
import org.springframework.dao.ConcurrencyFailureException;
|
|
||||||
import org.springframework.extensions.surf.util.ParameterCheck;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache for two-way lookups of database entities. These are characterized by having a unique
|
* A cache for two-way lookups of database entities. These are characterized by having a unique key (perhaps a database ID) and a separate unique key that identifies the object. If no cache is given, then all calls are passed through to the backing DAO.
|
||||||
* key (perhaps a database ID) and a separate unique key that identifies the object. If no cache
|
|
||||||
* is given, then all calls are passed through to the backing DAO.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The keys must have good <code>equals</code> and <code>hashCode</code> implementations and
|
* The keys must have good <code>equals</code> and <code>hashCode</code> implementations and must respect the case-sensitivity of the use-case.
|
||||||
* must respect the case-sensitivity of the use-case.
|
|
||||||
* <p>
|
* <p>
|
||||||
* All keys will be unique to the given cache region, allowing the cache to be shared
|
* All keys will be unique to the given cache region, allowing the cache to be shared between instances of this class.
|
||||||
* between instances of this class.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Generics:
|
* Generics:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>K: The database unique identifier.</li>
|
* <li>K: The database unique identifier.</li>
|
||||||
* <li>V: The value stored against K.</li>
|
* <li>V: The value stored against K.</li>
|
||||||
* <li>VK: The a value-derived key that will be used as a cache key when caching K for lookups by V.
|
* <li>VK: The a value-derived key that will be used as a cache key when caching K for lookups by V. This can be the value itself if it is itself a good key.</li>
|
||||||
* This can be the value itself if it is itself a good key.</li>
|
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Derek Hulley
|
* @author Derek Hulley
|
||||||
@@ -65,60 +61,47 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
public static interface EntityLookupCallbackDAO<K1 extends Serializable, V1 extends Object, VK1 extends Serializable>
|
public static interface EntityLookupCallbackDAO<K1 extends Serializable, V1 extends Object, VK1 extends Serializable>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Resolve the given value into a unique value key that can be used to find the entity's ID.
|
* Resolve the given value into a unique value key that can be used to find the entity's ID. A return value should be small and efficient; don't return a value if this is not possible.
|
||||||
* A return value should be small and efficient; don't return a value if this is not possible.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Implementations will often return the value itself, provided that the value is both
|
* Implementations will often return the value itself, provided that the value is both serializable and has a good <code>equals</code> and <code>hashCode</code>.
|
||||||
* serializable and has a good <code>equals</code> and <code>hashCode</code>.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Were no adequate key can be generated for the value, then <tt>null</tt> can be returned.
|
* Were no adequate key can be generated for the value, then <tt>null</tt> can be returned. In this case, the {@link #findByValue(Object) findByValue} method might not even do a search and just return <tt>null</tt> itself i.e. if it is difficult to look the value up in storage then it is probably difficult to generate a cache key from it, too.. In this scenario, the cache will be purely for key-based lookups
|
||||||
* In this case, the {@link #findByValue(Object) findByValue} method might not even do a search
|
|
||||||
* and just return <tt>null</tt> itself i.e. if it is difficult to look the value up in storage
|
|
||||||
* then it is probably difficult to generate a cache key from it, too.. In this scenario, the
|
|
||||||
* cache will be purely for key-based lookups
|
|
||||||
*
|
*
|
||||||
* @param value the full value being keyed (never <tt>null</tt>)
|
* @param value
|
||||||
* @return Returns the business key representing the entity, or <tt>null</tt>
|
* the full value being keyed (never <tt>null</tt>)
|
||||||
* if an economical key cannot be generated.
|
* @return Returns the business key representing the entity, or <tt>null</tt> if an economical key cannot be generated.
|
||||||
*/
|
*/
|
||||||
VK1 getValueKey(V1 value);
|
VK1 getValueKey(V1 value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find an entity for a given key.
|
* Find an entity for a given key.
|
||||||
*
|
*
|
||||||
* @param key the key (ID) used to identify the entity (never <tt>null</tt>)
|
* @param key
|
||||||
|
* the key (ID) used to identify the entity (never <tt>null</tt>)
|
||||||
* @return Return the entity or <tt>null</tt> if no entity is exists for the ID
|
* @return Return the entity or <tt>null</tt> if no entity is exists for the ID
|
||||||
*/
|
*/
|
||||||
Pair<K1, V1> findByKey(K1 key);
|
Pair<K1, V1> findByKey(K1 key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find and entity using the given value key. The <code>equals</code> and <code>hashCode</code>
|
* Find and entity using the given value key. The <code>equals</code> and <code>hashCode</code> methods of the value object should respect case-sensitivity in the same way that this lookup treats case-sensitivity i.e. if the <code>equals</code> method is <b>case-sensitive</b> then this method should look the entity up using a <b>case-sensitive</b> search.
|
||||||
* methods of the value object should respect case-sensitivity in the same way that this
|
|
||||||
* lookup treats case-sensitivity i.e. if the <code>equals</code> method is <b>case-sensitive</b>
|
|
||||||
* then this method should look the entity up using a <b>case-sensitive</b> search.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Since this is a cache backed by some sort of database, <tt>null</tt> values are allowed by the
|
* Since this is a cache backed by some sort of database, <tt>null</tt> values are allowed by the cache. The implementation of this method can throw an exception if <tt>null</tt> is not appropriate for the use-case.
|
||||||
* cache. The implementation of this method can throw an exception if <tt>null</tt> is not
|
|
||||||
* appropriate for the use-case.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* If the search is impossible or expensive, this method should just return <tt>null</tt>. This
|
* If the search is impossible or expensive, this method should just return <tt>null</tt>. This would usually be the case if the {@link #getValueKey(Object) getValueKey} method also returned <tt>null</tt> i.e. if it is difficult to look the value up in storage then it is probably difficult to generate a cache key from it, too.
|
||||||
* would usually be the case if the {@link #getValueKey(Object) getValueKey} method also returned
|
|
||||||
* <tt>null</tt> i.e. if it is difficult to look the value up in storage then it is probably
|
|
||||||
* difficult to generate a cache key from it, too.
|
|
||||||
*
|
*
|
||||||
* @param value the value (business object) used to identify the entity (<tt>null</tt> allowed).
|
* @param value
|
||||||
|
* the value (business object) used to identify the entity (<tt>null</tt> allowed).
|
||||||
* @return Return the entity or <tt>null</tt> if no entity matches the given value
|
* @return Return the entity or <tt>null</tt> if no entity matches the given value
|
||||||
*/
|
*/
|
||||||
Pair<K1, V1> findByValue(V1 value);
|
Pair<K1, V1> findByValue(V1 value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an entity using the given values. It is valid to assume that the entity does not exist
|
* Create an entity using the given values. It is valid to assume that the entity does not exist within the current transaction at least.
|
||||||
* within the current transaction at least.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Since persistence mechanisms often allow <tt>null</tt> values, these can be expected here. The
|
* Since persistence mechanisms often allow <tt>null</tt> values, these can be expected here. The implementation must throw an exception if <tt>null</tt> is not allowed for the specific use-case.
|
||||||
* implementation must throw an exception if <tt>null</tt> is not allowed for the specific use-case.
|
|
||||||
*
|
*
|
||||||
* @param value the value (business object) used to identify the entity (<tt>null</tt> allowed).
|
* @param value
|
||||||
|
* the value (business object) used to identify the entity (<tt>null</tt> allowed).
|
||||||
* @return Return the newly-created entity ID-value pair
|
* @return Return the newly-created entity ID-value pair
|
||||||
*/
|
*/
|
||||||
Pair<K1, V1> createValue(V1 value);
|
Pair<K1, V1> createValue(V1 value);
|
||||||
@@ -126,44 +109,47 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Update the entity identified by the given key.
|
* Update the entity identified by the given key.
|
||||||
* <p/>
|
* <p/>
|
||||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not.
|
||||||
* or not.
|
|
||||||
*
|
*
|
||||||
* @param key the existing key (ID) used to identify the entity (never <tt>null</tt>)
|
* @param key
|
||||||
* @param value the new value
|
* the existing key (ID) used to identify the entity (never <tt>null</tt>)
|
||||||
|
* @param value
|
||||||
|
* the new value
|
||||||
* @return Returns the row update count.
|
* @return Returns the row update count.
|
||||||
* @throws UnsupportedOperationException if entity updates are not supported
|
* @throws UnsupportedOperationException
|
||||||
|
* if entity updates are not supported
|
||||||
*/
|
*/
|
||||||
int updateValue(K1 key, V1 value);
|
int updateValue(K1 key, V1 value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete an entity for the given key.
|
* Delete an entity for the given key.
|
||||||
* <p/>
|
* <p/>
|
||||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not.
|
||||||
* or not.
|
|
||||||
*
|
*
|
||||||
* @param key the key (ID) used to identify the entity (never <tt>null</tt>)
|
* @param key
|
||||||
|
* the key (ID) used to identify the entity (never <tt>null</tt>)
|
||||||
* @return Returns the row deletion count.
|
* @return Returns the row deletion count.
|
||||||
* @throws UnsupportedOperationException if entity deletion is not supported
|
* @throws UnsupportedOperationException
|
||||||
|
* if entity deletion is not supported
|
||||||
*/
|
*/
|
||||||
int deleteByKey(K1 key);
|
int deleteByKey(K1 key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete an entity for the given value.
|
* Delete an entity for the given value.
|
||||||
* <p/>
|
* <p/>
|
||||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not.
|
||||||
* or not.
|
|
||||||
*
|
*
|
||||||
* @param value the value (business object) used to identify the enitity (<tt>null</tt> allowed)
|
* @param value
|
||||||
|
* the value (business object) used to identify the enitity (<tt>null</tt> allowed)
|
||||||
* @return Returns the row deletion count.
|
* @return Returns the row deletion count.
|
||||||
* @throws UnsupportedOperationException if entity deletion is not supported
|
* @throws UnsupportedOperationException
|
||||||
|
* if entity deletion is not supported
|
||||||
*/
|
*/
|
||||||
int deleteByValue(V1 value);
|
int deleteByValue(V1 value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adaptor for implementations that support immutable entities. The update and delete operations
|
* Adaptor for implementations that support immutable entities. The update and delete operations throw {@link UnsupportedOperationException}.
|
||||||
* throw {@link UnsupportedOperationException}.
|
|
||||||
*
|
*
|
||||||
* @author Derek Hulley
|
* @author Derek Hulley
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
@@ -194,7 +180,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Disallows the operation.
|
* Disallows the operation.
|
||||||
*
|
*
|
||||||
* @throws UnsupportedOperationException always
|
* @throws UnsupportedOperationException
|
||||||
|
* always
|
||||||
*/
|
*/
|
||||||
public int updateValue(K2 key, V2 value)
|
public int updateValue(K2 key, V2 value)
|
||||||
{
|
{
|
||||||
@@ -204,7 +191,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Disallows the operation.
|
* Disallows the operation.
|
||||||
*
|
*
|
||||||
* @throws UnsupportedOperationException always
|
* @throws UnsupportedOperationException
|
||||||
|
* always
|
||||||
*/
|
*/
|
||||||
public int deleteByKey(K2 key)
|
public int deleteByKey(K2 key)
|
||||||
{
|
{
|
||||||
@@ -214,7 +202,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Disallows the operation.
|
* Disallows the operation.
|
||||||
*
|
*
|
||||||
* @throws UnsupportedOperationException always
|
* @throws UnsupportedOperationException
|
||||||
|
* always
|
||||||
*/
|
*/
|
||||||
public int deleteByValue(V2 value)
|
public int deleteByValue(V2 value)
|
||||||
{
|
{
|
||||||
@@ -240,10 +229,10 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
private final String cacheRegion;
|
private final String cacheRegion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the lookup cache <b>without any cache</b>. All calls are passed directly to the
|
* Construct the lookup cache <b>without any cache</b>. All calls are passed directly to the underlying DAO entity lookup.
|
||||||
* underlying DAO entity lookup.
|
|
||||||
*
|
*
|
||||||
* @param entityLookup the instance that is able to find and persist entities
|
* @param entityLookup
|
||||||
|
* the instance that is able to find and persist entities
|
||||||
*/
|
*/
|
||||||
public EntityLookupCache(EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
public EntityLookupCache(EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
||||||
{
|
{
|
||||||
@@ -253,8 +242,10 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Construct the lookup cache, using the {@link #CACHE_REGION_DEFAULT default cache region}.
|
* Construct the lookup cache, using the {@link #CACHE_REGION_DEFAULT default cache region}.
|
||||||
*
|
*
|
||||||
* @param cache the cache that will back the two-way lookups
|
* @param cache
|
||||||
* @param entityLookup the instance that is able to find and persist entities
|
* the cache that will back the two-way lookups
|
||||||
|
* @param entityLookup
|
||||||
|
* the instance that is able to find and persist entities
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public EntityLookupCache(SimpleCache cache, EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
public EntityLookupCache(SimpleCache cache, EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
||||||
@@ -265,15 +256,16 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Construct the lookup cache, using the given cache region.
|
* Construct the lookup cache, using the given cache region.
|
||||||
* <p>
|
* <p>
|
||||||
* All keys will be unique to the given cache region, allowing the cache to be shared
|
* All keys will be unique to the given cache region, allowing the cache to be shared between instances of this class.
|
||||||
* between instances of this class.
|
|
||||||
*
|
*
|
||||||
* @param cache the cache that will back the two-way lookups; <tt>null</tt> to have no backing
|
* @param cache
|
||||||
* in a cache.
|
* the cache that will back the two-way lookups; <tt>null</tt> to have no backing in a cache.
|
||||||
* @param cacheRegion the region within the cache to use.
|
* @param cacheRegion
|
||||||
* @param entityLookup the instance that is able to find and persist entities
|
* the region within the cache to use.
|
||||||
|
* @param entityLookup
|
||||||
|
* the instance that is able to find and persist entities
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public EntityLookupCache(SimpleCache cache, String cacheRegion, EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
public EntityLookupCache(SimpleCache cache, String cacheRegion, EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
||||||
{
|
{
|
||||||
ParameterCheck.mandatory("cacheRegion", cacheRegion);
|
ParameterCheck.mandatory("cacheRegion", cacheRegion);
|
||||||
@@ -284,14 +276,12 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the entity associated with the given key.
|
* Find the entity associated with the given key. The {@link EntityLookupCallbackDAO#findByKey(Serializable) entity callback} will be used if necessary.
|
||||||
* The {@link EntityLookupCallbackDAO#findByKey(Serializable) entity callback} will be used if necessary.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* It is up to the client code to decide if a <tt>null</tt> return value indicates a concurrency violation
|
* It is up to the client code to decide if a <tt>null</tt> return value indicates a concurrency violation or not; the former would normally result in a concurrency-related exception such as {@link ConcurrencyFailureException}.
|
||||||
* or not; the former would normally result in a concurrency-related exception such as
|
|
||||||
* {@link ConcurrencyFailureException}.
|
|
||||||
*
|
*
|
||||||
* @param key The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
* @param key
|
||||||
|
* The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||||
* @return Returns the key-value pair or <tt>null</tt> if the key doesn't reference an entity
|
* @return Returns the key-value pair or <tt>null</tt> if the key doesn't reference an entity
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -337,7 +327,7 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
{
|
{
|
||||||
value = entityPair.getSecond();
|
value = entityPair.getSecond();
|
||||||
// Get the value key
|
// Get the value key
|
||||||
VK valueKey = (value == null) ? (VK)VALUE_NULL : entityLookup.getValueKey(value);
|
VK valueKey = (value == null) ? (VK) VALUE_NULL : entityLookup.getValueKey(value);
|
||||||
// Check if the value has a good key
|
// Check if the value has a good key
|
||||||
if (valueKey != null)
|
if (valueKey != null)
|
||||||
{
|
{
|
||||||
@@ -354,14 +344,12 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the entity associated with the given value.
|
* Find the entity associated with the given value. The {@link EntityLookupCallbackDAO#findByValue(Object) entity callback} will be used if no entry exists in the cache.
|
||||||
* The {@link EntityLookupCallbackDAO#findByValue(Object) entity callback} will be used if no entry exists in the cache.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* It is up to the client code to decide if a <tt>null</tt> return value indicates a concurrency violation
|
* It is up to the client code to decide if a <tt>null</tt> return value indicates a concurrency violation or not; the former would normally result in a concurrency-related exception such as {@link ConcurrencyFailureException}.
|
||||||
* or not; the former would normally result in a concurrency-related exception such as
|
|
||||||
* {@link ConcurrencyFailureException}.
|
|
||||||
*
|
*
|
||||||
* @param value The entity value, which may be valid or invalid (<tt>null</tt> is allowed)
|
* @param value
|
||||||
|
* The entity value, which may be valid or invalid (<tt>null</tt> is allowed)
|
||||||
* @return Returns the key-value pair or <tt>null</tt> if the value doesn't reference an entity
|
* @return Returns the key-value pair or <tt>null</tt> if the value doesn't reference an entity
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -376,7 +364,7 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
// Get the value key.
|
// Get the value key.
|
||||||
// The cast to (VK) is counter-intuitive, but works because they're all just Serializable
|
// The cast to (VK) is counter-intuitive, but works because they're all just Serializable
|
||||||
// It's nasty, but hidden from the cache client code.
|
// It's nasty, but hidden from the cache client code.
|
||||||
VK valueKey = (value == null) ? (VK)VALUE_NULL : entityLookup.getValueKey(value);
|
VK valueKey = (value == null) ? (VK) VALUE_NULL : entityLookup.getValueKey(value);
|
||||||
// Check if the value has a good key
|
// Check if the value has a good key
|
||||||
if (valueKey == null)
|
if (valueKey == null)
|
||||||
{
|
{
|
||||||
@@ -423,15 +411,14 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to create the entity and, failing that, look it up.<br/>
|
* Attempt to create the entity and, failing that, look it up.<br/>
|
||||||
* This method takes the opposite approach to {@link #getOrCreateByValue(Object)}, which assumes the entity's
|
* This method takes the opposite approach to {@link #getOrCreateByValue(Object)}, which assumes the entity's existence: in this case the entity is assumed to NOT exist. The {@link EntityLookupCallbackDAO#createValue(Object)} and {@link EntityLookupCallbackDAO#findByValue(Object)} will be used if necessary.<br/>
|
||||||
* existence: in this case the entity is assumed to NOT exist.
|
|
||||||
* The {@link EntityLookupCallbackDAO#createValue(Object)} and {@link EntityLookupCallbackDAO#findByValue(Object)}
|
|
||||||
* will be used if necessary.<br/>
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Use this method when the data involved is seldom reused.
|
* Use this method when the data involved is seldom reused.
|
||||||
*
|
*
|
||||||
* @param value The entity value (<tt>null</tt> is allowed)
|
* @param value
|
||||||
* @param controlDAO an essential DAO required in order to ensure a transactionally-safe attempt at data creation
|
* The entity value (<tt>null</tt> is allowed)
|
||||||
|
* @param controlDAO
|
||||||
|
* an essential DAO required in order to ensure a transactionally-safe attempt at data creation
|
||||||
* @return Returns the key-value pair (new or existing and never <tt>null</tt>)
|
* @return Returns the key-value pair (new or existing and never <tt>null</tt>)
|
||||||
*/
|
*/
|
||||||
public Pair<K, V> createOrGetByValue(V value, ControlDAO controlDAO)
|
public Pair<K, V> createOrGetByValue(V value, ControlDAO controlDAO)
|
||||||
@@ -448,6 +435,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
// Cache it
|
// Cache it
|
||||||
if (cache != null)
|
if (cache != null)
|
||||||
{
|
{
|
||||||
|
VK valueKey = (value == null) ? (VK) VALUE_NULL : entityLookup.getValueKey(value);
|
||||||
|
cache.put(new CacheRegionValueKey(cacheRegion, valueKey), entityPair.getFirst());
|
||||||
cache.put(
|
cache.put(
|
||||||
new CacheRegionKey(cacheRegion, entityPair.getFirst()),
|
new CacheRegionKey(cacheRegion, entityPair.getFirst()),
|
||||||
(entityPair.getSecond() == null ? VALUE_NULL : entityPair.getSecond()));
|
(entityPair.getSecond() == null ? VALUE_NULL : entityPair.getSecond()));
|
||||||
@@ -464,11 +453,10 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the entity associated with the given value and create it if it doesn't exist.
|
* Find the entity associated with the given value and create it if it doesn't exist. The {@link EntityLookupCallbackDAO#findByValue(Object)} and {@link EntityLookupCallbackDAO#createValue(Object)} will be used if necessary.
|
||||||
* The {@link EntityLookupCallbackDAO#findByValue(Object)} and {@link EntityLookupCallbackDAO#createValue(Object)}
|
|
||||||
* will be used if necessary.
|
|
||||||
*
|
*
|
||||||
* @param value The entity value (<tt>null</tt> is allowed)
|
* @param value
|
||||||
|
* The entity value (<tt>null</tt> is allowed)
|
||||||
* @return Returns the key-value pair (new or existing and never <tt>null</tt>)
|
* @return Returns the key-value pair (new or existing and never <tt>null</tt>)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -488,7 +476,7 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
// Get the value key
|
// Get the value key
|
||||||
// The cast to (VK) is counter-intuitive, but works because they're all just Serializable.
|
// The cast to (VK) is counter-intuitive, but works because they're all just Serializable.
|
||||||
// It's nasty, but hidden from the cache client code.
|
// It's nasty, but hidden from the cache client code.
|
||||||
VK valueKey = (value == null) ? (VK)VALUE_NULL : entityLookup.getValueKey(value);
|
VK valueKey = (value == null) ? (VK) VALUE_NULL : entityLookup.getValueKey(value);
|
||||||
// Check if the value has a good key
|
// Check if the value has a good key
|
||||||
if (valueKey == null)
|
if (valueKey == null)
|
||||||
{
|
{
|
||||||
@@ -530,16 +518,14 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the entity associated with the given key.
|
* Update the entity associated with the given key. The {@link EntityLookupCallbackDAO#updateValue(Serializable, Object)} callback will be used if necessary.
|
||||||
* The {@link EntityLookupCallbackDAO#updateValue(Serializable, Object)} callback
|
|
||||||
* will be used if necessary.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||||
* or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised
|
|
||||||
* by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
|
||||||
*
|
*
|
||||||
* @param key The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
* @param key
|
||||||
* @param value The new entity value (may be <tt>null</tt>)
|
* The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||||
|
* @param value
|
||||||
|
* The new entity value (may be <tt>null</tt>)
|
||||||
* @return Returns the row update count.
|
* @return Returns the row update count.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -564,7 +550,7 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the value key.
|
// Get the value key.
|
||||||
VK valueKey = (value == null) ? (VK)VALUE_NULL : entityLookup.getValueKey(value);
|
VK valueKey = (value == null) ? (VK) VALUE_NULL : entityLookup.getValueKey(value);
|
||||||
// Check if the value has a good key
|
// Check if the value has a good key
|
||||||
if (valueKey != null)
|
if (valueKey != null)
|
||||||
{
|
{
|
||||||
@@ -580,10 +566,44 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
return updateCount;
|
return updateCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the entity associated with the given value if its cached
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The entity value (<tt>null</tt> is not allowed)
|
||||||
|
* @return Returns the key-value pair (existing or <tt>null</tt>)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Pair<K, V> getCachedEntityByValue(V value)
|
||||||
|
{
|
||||||
|
if (cache == null || value == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
VK valueKey = entityLookup.getValueKey(value);
|
||||||
|
if (valueKey == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the cached value
|
||||||
|
CacheRegionValueKey valueCacheKey = new CacheRegionValueKey(cacheRegion, valueKey);
|
||||||
|
K key = (K) cache.get(valueCacheKey);
|
||||||
|
|
||||||
|
if (key != null && !key.equals(VALUE_NOT_FOUND))
|
||||||
|
{
|
||||||
|
return getByKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache-only operation: Get the key for a given value key (note: not 'value' but 'value key').
|
* Cache-only operation: Get the key for a given value key (note: not 'value' but 'value key').
|
||||||
*
|
*
|
||||||
* @param valueKey The entity value key, which must be valid (<tt>null</tt> not allowed)
|
* @param valueKey
|
||||||
|
* The entity value key, which must be valid (<tt>null</tt> not allowed)
|
||||||
* @return The entity key (may be <tt>null</tt>)
|
* @return The entity key (may be <tt>null</tt>)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -603,7 +623,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Cache-only operation: Get the value for a given key
|
* Cache-only operation: Get the value for a given key
|
||||||
*
|
*
|
||||||
* @param key The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
* @param key
|
||||||
|
* The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||||
* @return The entity value (may be <tt>null</tt>)
|
* @return The entity value (may be <tt>null</tt>)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -634,8 +655,10 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Cache-only operation: Update the cache's value
|
* Cache-only operation: Update the cache's value
|
||||||
*
|
*
|
||||||
* @param key The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
* @param key
|
||||||
* @param value The new entity value (may be <tt>null</tt>)
|
* The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||||
|
* @param value
|
||||||
|
* The new entity value (may be <tt>null</tt>)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void setValue(K key, V value)
|
public void setValue(K key, V value)
|
||||||
@@ -651,7 +674,7 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
removeByKey(key, false);
|
removeByKey(key, false);
|
||||||
|
|
||||||
// Get the value key.
|
// Get the value key.
|
||||||
VK valueKey = (value == null) ? (VK)VALUE_NULL : entityLookup.getValueKey(value);
|
VK valueKey = (value == null) ? (VK) VALUE_NULL : entityLookup.getValueKey(value);
|
||||||
// Check if the value has a good key
|
// Check if the value has a good key
|
||||||
if (valueKey != null)
|
if (valueKey != null)
|
||||||
{
|
{
|
||||||
@@ -667,14 +690,12 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the entity associated with the given key.
|
* Delete the entity associated with the given key. The {@link EntityLookupCallbackDAO#deleteByKey(Serializable)} callback will be used if necessary.
|
||||||
* The {@link EntityLookupCallbackDAO#deleteByKey(Serializable)} callback will be used if necessary.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||||
* or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised
|
|
||||||
* by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
|
||||||
*
|
*
|
||||||
* @param key the entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
* @param key
|
||||||
|
* the entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||||
* @return Returns the row deletion count
|
* @return Returns the row deletion count
|
||||||
*/
|
*/
|
||||||
public int deleteByKey(K key)
|
public int deleteByKey(K key)
|
||||||
@@ -693,14 +714,12 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the entity having the given value..
|
* Delete the entity having the given value.. The {@link EntityLookupCallbackDAO#deleteByValue(Object)} callback will be used if necessary.
|
||||||
* The {@link EntityLookupCallbackDAO#deleteByValue(Object)} callback will be used if necessary.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||||
* or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised
|
|
||||||
* by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
|
||||||
*
|
*
|
||||||
* @param value the entity value, which may be valid or invalid (<tt>null</tt> allowed)
|
* @param value
|
||||||
|
* the entity value, which may be valid or invalid (<tt>null</tt> allowed)
|
||||||
* @return Returns the row deletion count
|
* @return Returns the row deletion count
|
||||||
*/
|
*/
|
||||||
public int deleteByValue(V value)
|
public int deleteByValue(V value)
|
||||||
@@ -735,7 +754,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Cache-only operation: Remove all cache values associated with the given key.
|
* Cache-only operation: Remove all cache values associated with the given key.
|
||||||
*
|
*
|
||||||
* @param removeKey <tt>true</tt> to remove the given key's entry
|
* @param removeKey
|
||||||
|
* <tt>true</tt> to remove the given key's entry
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void removeByKey(K key, boolean removeKey)
|
private void removeByKey(K key, boolean removeKey)
|
||||||
@@ -761,7 +781,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
/**
|
/**
|
||||||
* Cache-only operation: Remove all cache values associated with the given value
|
* Cache-only operation: Remove all cache values associated with the given value
|
||||||
*
|
*
|
||||||
* @param value The entity value (<tt>null</tt> is allowed)
|
* @param value
|
||||||
|
* The entity value (<tt>null</tt> is allowed)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void removeByValue(V value)
|
public void removeByValue(V value)
|
||||||
@@ -773,7 +794,7 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the value key
|
// Get the value key
|
||||||
VK valueKey = (value == null) ? (VK)VALUE_NULL : entityLookup.getValueKey(value);
|
VK valueKey = (value == null) ? (VK) VALUE_NULL : entityLookup.getValueKey(value);
|
||||||
if (valueKey == null)
|
if (valueKey == null)
|
||||||
{
|
{
|
||||||
// No key generated for the value. There is nothing that can be done.
|
// No key generated for the value. There is nothing that can be done.
|
||||||
|
@@ -31,6 +31,11 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
|
|
||||||
import org.alfresco.repo.cache.SimpleCache;
|
import org.alfresco.repo.cache.SimpleCache;
|
||||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
||||||
@@ -45,19 +50,13 @@ import org.alfresco.service.cmr.repository.ContentData;
|
|||||||
import org.alfresco.util.EqualsHelper;
|
import org.alfresco.util.EqualsHelper;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.util.Pair;
|
||||||
import org.alfresco.util.transaction.TransactionListenerAdapter;
|
import org.alfresco.util.transaction.TransactionListenerAdapter;
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.dao.ConcurrencyFailureException;
|
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract implementation for ContentData DAO.
|
* Abstract implementation for ContentData DAO.
|
||||||
* <p>
|
* <p>
|
||||||
* This provides basic services such as caching, but defers to the underlying implementation
|
* This provides basic services such as caching, but defers to the underlying implementation for CRUD operations.
|
||||||
* for CRUD operations.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The DAO deals in {@link ContentData} instances. The cache is primarily present to decode
|
* The DAO deals in {@link ContentData} instances. The cache is primarily present to decode IDs into <code>ContentData</code> instances.
|
||||||
* IDs into <code>ContentData</code> instances.
|
|
||||||
*
|
*
|
||||||
* @author Derek Hulley
|
* @author Derek Hulley
|
||||||
* @author sglover
|
* @author sglover
|
||||||
@@ -127,7 +126,8 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
/**
|
/**
|
||||||
* Set this property to enable eager cleanup of orphaned content.
|
* Set this property to enable eager cleanup of orphaned content.
|
||||||
*
|
*
|
||||||
* @param contentStoreCleaner an eager cleaner (may be <tt>null</tt>)
|
* @param contentStoreCleaner
|
||||||
|
* an eager cleaner (may be <tt>null</tt>)
|
||||||
*/
|
*/
|
||||||
public void setContentStoreCleaner(EagerContentStoreCleaner contentStoreCleaner)
|
public void setContentStoreCleaner(EagerContentStoreCleaner contentStoreCleaner)
|
||||||
{
|
{
|
||||||
@@ -135,7 +135,8 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param contentDataCache the cache of IDs to ContentData and vice versa
|
* @param contentDataCache
|
||||||
|
* the cache of IDs to ContentData and vice versa
|
||||||
*/
|
*/
|
||||||
public void setContentDataCache(SimpleCache<Long, ContentData> contentDataCache)
|
public void setContentDataCache(SimpleCache<Long, ContentData> contentDataCache)
|
||||||
{
|
{
|
||||||
@@ -154,8 +155,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A <b>content_url</b> entity was dereferenced. This makes no assumptions about the
|
* A <b>content_url</b> entity was dereferenced. This makes no assumptions about the current references - dereference deletion is handled in the commit phase.
|
||||||
* current references - dereference deletion is handled in the commit phase.
|
|
||||||
*/
|
*/
|
||||||
protected void registerDereferencedContentUrl(String contentUrl)
|
protected void registerDereferencedContentUrl(String contentUrl)
|
||||||
{
|
{
|
||||||
@@ -205,7 +205,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
throw new IllegalArgumentException("Cannot look up ContentData by null ID.");
|
throw new IllegalArgumentException("Cannot look up ContentData by null ID.");
|
||||||
}
|
}
|
||||||
Pair<Long, ContentUrlEntity> pair = contentUrlCache.getByValue(contentUrl);
|
Pair<Long, ContentUrlEntity> pair = contentUrlCache.getByValue(contentUrl);
|
||||||
if(pair != null)
|
if (pair != null)
|
||||||
{
|
{
|
||||||
result = contentUrlCache.updateValue(pair.getFirst(), contentUrl);
|
result = contentUrlCache.updateValue(pair.getFirst(), contentUrl);
|
||||||
}
|
}
|
||||||
@@ -413,7 +413,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
// Decode content URL
|
// Decode content URL
|
||||||
Long contentUrlId = contentDataEntity.getContentUrlId();
|
Long contentUrlId = contentDataEntity.getContentUrlId();
|
||||||
String contentUrl = null;
|
String contentUrl = null;
|
||||||
if(contentUrlId != null)
|
if (contentUrlId != null)
|
||||||
{
|
{
|
||||||
Pair<Long, ContentUrlEntity> entityPair = contentUrlCache.getByKey(contentUrlId);
|
Pair<Long, ContentUrlEntity> entityPair = contentUrlCache.getByKey(contentUrlId);
|
||||||
if (entityPair == null)
|
if (entityPair == null)
|
||||||
@@ -470,7 +470,15 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
ContentUrlEntity contentUrlEntity = new ContentUrlEntity();
|
ContentUrlEntity contentUrlEntity = new ContentUrlEntity();
|
||||||
contentUrlEntity.setContentUrl(contentUrl);
|
contentUrlEntity.setContentUrl(contentUrl);
|
||||||
contentUrlEntity.setSize(size);
|
contentUrlEntity.setSize(size);
|
||||||
Pair<Long, ContentUrlEntity> pair = contentUrlCache.createOrGetByValue(contentUrlEntity, controlDAO);
|
|
||||||
|
// Attempt to get the data from cache
|
||||||
|
Pair<Long, ContentUrlEntity> pair = contentUrlCache.getCachedEntityByValue(contentUrlEntity);
|
||||||
|
|
||||||
|
if (pair == null)
|
||||||
|
{
|
||||||
|
pair = contentUrlCache.createOrGetByValue(contentUrlEntity, controlDAO);
|
||||||
|
}
|
||||||
|
|
||||||
contentUrlId = pair.getFirst();
|
contentUrlId = pair.getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +518,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
// Resolve the content URL
|
// Resolve the content URL
|
||||||
Long oldContentUrlId = contentDataEntity.getContentUrlId();
|
Long oldContentUrlId = contentDataEntity.getContentUrlId();
|
||||||
ContentUrlEntity contentUrlEntity = null;
|
ContentUrlEntity contentUrlEntity = null;
|
||||||
if(oldContentUrlId != null)
|
if (oldContentUrlId != null)
|
||||||
{
|
{
|
||||||
Pair<Long, ContentUrlEntity> entityPair = contentUrlCache.getByKey(oldContentUrlId);
|
Pair<Long, ContentUrlEntity> entityPair = contentUrlCache.getByKey(oldContentUrlId);
|
||||||
if (entityPair == null)
|
if (entityPair == null)
|
||||||
@@ -531,7 +539,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
}
|
}
|
||||||
if (newContentUrl != null)
|
if (newContentUrl != null)
|
||||||
{
|
{
|
||||||
if(contentUrlEntity == null)
|
if (contentUrlEntity == null)
|
||||||
{
|
{
|
||||||
contentUrlEntity = new ContentUrlEntity();
|
contentUrlEntity = new ContentUrlEntity();
|
||||||
contentUrlEntity.setContentUrl(newContentUrl);
|
contentUrlEntity.setContentUrl(newContentUrl);
|
||||||
@@ -595,7 +603,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
boolean success = true;
|
boolean success = true;
|
||||||
|
|
||||||
ContentUrlEntity existing = getContentUrl(contentUrlId);
|
ContentUrlEntity existing = getContentUrl(contentUrlId);
|
||||||
if(existing != null)
|
if (existing != null)
|
||||||
{
|
{
|
||||||
ContentUrlEntity entity = ContentUrlEntity.setContentUrlKey(existing, contentUrlKey);
|
ContentUrlEntity entity = ContentUrlEntity.setContentUrlKey(existing, contentUrlKey);
|
||||||
updateContentUrl(entity);
|
updateContentUrl(entity);
|
||||||
@@ -638,32 +646,36 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param contentUrl the content URL to create or search for
|
* @param contentUrl
|
||||||
|
* the content URL to create or search for
|
||||||
*/
|
*/
|
||||||
protected abstract ContentUrlEntity createContentUrlEntity(String contentUrl, long size, ContentUrlKeyEntity contentUrlKey);
|
protected abstract ContentUrlEntity createContentUrlEntity(String contentUrl, long size, ContentUrlKeyEntity contentUrlKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param id the ID of the <b>content url</b> entity
|
* @param id
|
||||||
|
* the ID of the <b>content url</b> entity
|
||||||
* @return Return the entity or <tt>null</tt> if it doesn't exist
|
* @return Return the entity or <tt>null</tt> if it doesn't exist
|
||||||
*/
|
*/
|
||||||
protected abstract ContentUrlEntity getContentUrlEntity(Long id);
|
protected abstract ContentUrlEntity getContentUrlEntity(Long id);
|
||||||
|
|
||||||
protected abstract ContentUrlEntity getContentUrlEntity(String contentUrl);
|
protected abstract ContentUrlEntity getContentUrlEntity(String contentUrl);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param contentUrl the URL of the <b>content url</b> entity
|
* @param contentUrl
|
||||||
* @return Return the entity or <tt>null</tt> if it doesn't exist or is still
|
* the URL of the <b>content url</b> entity
|
||||||
* referenced by a <b>content_data</b> entity
|
* @return Return the entity or <tt>null</tt> if it doesn't exist or is still referenced by a <b>content_data</b> entity
|
||||||
*/
|
*/
|
||||||
protected abstract ContentUrlEntity getContentUrlEntityUnreferenced(String contentUrl);
|
protected abstract ContentUrlEntity getContentUrlEntityUnreferenced(String contentUrl);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a content URL with the given orphan time
|
* Update a content URL with the given orphan time
|
||||||
*
|
*
|
||||||
* @param id the unique ID of the entity
|
* @param id
|
||||||
* @param orphanTime the time (ms since epoch) that the entity was orphaned
|
* the unique ID of the entity
|
||||||
* @param oldOrphanTime the orphan time we expect to update for optimistic locking (may be <tt>null</tt>)
|
* @param orphanTime
|
||||||
|
* the time (ms since epoch) that the entity was orphaned
|
||||||
|
* @param oldOrphanTime
|
||||||
|
* the orphan time we expect to update for optimistic locking (may be <tt>null</tt>)
|
||||||
* @return Returns the number of rows updated
|
* @return Returns the number of rows updated
|
||||||
*/
|
*/
|
||||||
protected abstract int updateContentUrlOrphanTime(Long id, Long orphanTime, Long oldOrphanTime);
|
protected abstract int updateContentUrlOrphanTime(Long id, Long orphanTime, Long oldOrphanTime);
|
||||||
@@ -678,13 +690,15 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
Long localeId);
|
Long localeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param id the entity ID
|
* @param id
|
||||||
|
* the entity ID
|
||||||
* @return Returns the entity or <tt>null</tt> if it doesn't exist
|
* @return Returns the entity or <tt>null</tt> if it doesn't exist
|
||||||
*/
|
*/
|
||||||
protected abstract ContentDataEntity getContentDataEntity(Long id);
|
protected abstract ContentDataEntity getContentDataEntity(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param nodeIds the node ID
|
* @param nodeIds
|
||||||
|
* the node ID
|
||||||
* @return Returns the associated entities or <tt>null</tt> if none exist
|
* @return Returns the associated entities or <tt>null</tt> if none exist
|
||||||
*/
|
*/
|
||||||
protected abstract List<ContentDataEntity> getContentDataEntitiesForNodes(Set<Long> nodeIds);
|
protected abstract List<ContentDataEntity> getContentDataEntitiesForNodes(Set<Long> nodeIds);
|
||||||
@@ -692,7 +706,8 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
/**
|
/**
|
||||||
* Update an existing <b>alf_content_data</b> entity
|
* Update an existing <b>alf_content_data</b> entity
|
||||||
*
|
*
|
||||||
* @param entity the existing entity that will be updated
|
* @param entity
|
||||||
|
* the existing entity that will be updated
|
||||||
* @return Returns the number of rows updated (should be 1)
|
* @return Returns the number of rows updated (should be 1)
|
||||||
*/
|
*/
|
||||||
protected abstract int updateContentDataEntity(ContentDataEntity entity);
|
protected abstract int updateContentDataEntity(ContentDataEntity entity);
|
||||||
@@ -705,6 +720,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
|||||||
protected abstract int deleteContentDataEntity(Long id);
|
protected abstract int deleteContentDataEntity(Long id);
|
||||||
|
|
||||||
protected abstract int deleteContentUrlEntity(long id);
|
protected abstract int deleteContentUrlEntity(long id);
|
||||||
|
|
||||||
protected abstract int updateContentUrlEntity(ContentUrlEntity existing, ContentUrlEntity entity);
|
protected abstract int updateContentUrlEntity(ContentUrlEntity existing, ContentUrlEntity entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -25,12 +25,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.cache.lookup;
|
package org.alfresco.repo.cache.lookup;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.sql.Savepoint;
|
import java.sql.Savepoint;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import junit.framework.AssertionFailedError;
|
import junit.framework.AssertionFailedError;
|
||||||
import junit.framework.TestCase;
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
|
|
||||||
import org.alfresco.repo.cache.MemoryCache;
|
import org.alfresco.repo.cache.MemoryCache;
|
||||||
import org.alfresco.repo.cache.SimpleCache;
|
import org.alfresco.repo.cache.SimpleCache;
|
||||||
@@ -38,20 +43,16 @@ import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO;
|
|||||||
import org.alfresco.repo.domain.control.ControlDAO;
|
import org.alfresco.repo.domain.control.ControlDAO;
|
||||||
import org.alfresco.util.EqualsHelper;
|
import org.alfresco.util.EqualsHelper;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.util.Pair;
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache for two-way lookups of database entities. These are characterized by having a unique
|
* A cache for two-way lookups of database entities. These are characterized by having a unique key (perhaps a database ID) and a separate unique key that identifies the object.
|
||||||
* key (perhaps a database ID) and a separate unique key that identifies the object.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The keys must have good <code>equals</code> and </code>hashCode</code> implementations and
|
* The keys must have good <code>equals</code> and </code>hashCode</code> implementations and must respect the case-sensitivity of the use-case.
|
||||||
* must respect the case-sensitivity of the use-case.
|
|
||||||
*
|
*
|
||||||
* @author Derek Hulley
|
* @author Derek Hulley
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
public class EntityLookupCacheTest extends TestCase implements EntityLookupCallbackDAO<Long, Object, String>
|
public class EntityLookupCacheTest implements EntityLookupCallbackDAO<Long, Object, String>
|
||||||
{
|
{
|
||||||
SimpleCache<Long, Object> cache;
|
SimpleCache<Long, Object> cache;
|
||||||
private EntityLookupCache<Long, Object, String> entityLookupCacheA;
|
private EntityLookupCache<Long, Object, String> entityLookupCacheA;
|
||||||
@@ -59,7 +60,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
private TreeMap<Long, String> database;
|
private TreeMap<Long, String> database;
|
||||||
private ControlDAO controlDAO;
|
private ControlDAO controlDAO;
|
||||||
|
|
||||||
@Override
|
@Before
|
||||||
protected void setUp() throws Exception
|
protected void setUp() throws Exception
|
||||||
{
|
{
|
||||||
cache = new MemoryCache<Long, Object>();
|
cache = new MemoryCache<Long, Object>();
|
||||||
@@ -71,6 +72,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
Mockito.when(controlDAO.createSavepoint(Mockito.anyString())).thenReturn(Mockito.mock(Savepoint.class));
|
Mockito.when(controlDAO.createSavepoint(Mockito.anyString())).thenReturn(Mockito.mock(Savepoint.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLookupsUsingIncorrectValue() throws Exception
|
public void testLookupsUsingIncorrectValue() throws Exception
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -84,6 +86,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLookupAgainstEmpty() throws Exception
|
public void testLookupAgainstEmpty() throws Exception
|
||||||
{
|
{
|
||||||
TestValue value = new TestValue("AAA");
|
TestValue value = new TestValue("AAA");
|
||||||
@@ -114,6 +117,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals("Looked-up type value incorrect", value, entityPair.getSecond());
|
assertEquals("Looked-up type value incorrect", value, entityPair.getSecond());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLookupAgainstExisting() throws Exception
|
public void testLookupAgainstExisting() throws Exception
|
||||||
{
|
{
|
||||||
// Put some values in the "database"
|
// Put some values in the "database"
|
||||||
@@ -136,6 +140,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals("ID is incorrect", Long.valueOf(3), entityPair.getFirst());
|
assertEquals("ID is incorrect", Long.valueOf(3), entityPair.getFirst());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRegions() throws Exception
|
public void testRegions() throws Exception
|
||||||
{
|
{
|
||||||
TestValue valueAAA = new TestValue("AAA");
|
TestValue valueAAA = new TestValue("AAA");
|
||||||
@@ -157,6 +162,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals(8, cache.getKeys().size());
|
assertEquals(8, cache.getKeys().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testNullLookups() throws Exception
|
public void testNullLookups() throws Exception
|
||||||
{
|
{
|
||||||
TestValue valueNull = null;
|
TestValue valueNull = null;
|
||||||
@@ -174,9 +180,10 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals(entityPairNull, entityPairCheck);
|
assertEquals(entityPairNull, entityPairCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testGetOrCreate() throws Exception
|
public void testGetOrCreate() throws Exception
|
||||||
{
|
{
|
||||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||||
assertNotNull(entityPairOne);
|
assertNotNull(entityPairOne);
|
||||||
Long id = entityPairOne.getFirst();
|
Long id = entityPairOne.getFirst();
|
||||||
@@ -188,24 +195,27 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals(id, entityPairOneCheck.getFirst());
|
assertEquals(id, entityPairOneCheck.getFirst());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCreateOrGet() throws Exception
|
public void testCreateOrGet() throws Exception
|
||||||
{
|
{
|
||||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
|
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
|
||||||
assertNotNull(entityPairOne);
|
assertNotNull(entityPairOne);
|
||||||
Long id = entityPairOne.getFirst();
|
Long id = entityPairOne.getFirst();
|
||||||
assertEquals(valueOne.val, database.get(id));
|
assertEquals(valueOne.val, database.get(id));
|
||||||
assertEquals(1, cache.getKeys().size());
|
// We cache both by value and by key, so we should have 2 entries
|
||||||
|
assertEquals(2, cache.getKeys().size());
|
||||||
|
|
||||||
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
|
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
|
||||||
assertNotNull(entityPairOneCheck);
|
assertNotNull(entityPairOneCheck);
|
||||||
assertEquals(id, entityPairOneCheck.getFirst());
|
assertEquals(id, entityPairOneCheck.getFirst());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testUpdate() throws Exception
|
public void testUpdate() throws Exception
|
||||||
{
|
{
|
||||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||||
TestValue valueTwo = new TestValue(getName() + "-TWO");
|
TestValue valueTwo = new TestValue(getClass().getName() + "-TWO");
|
||||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||||
assertNotNull(entityPairOne);
|
assertNotNull(entityPairOne);
|
||||||
Long id = entityPairOne.getFirst();
|
Long id = entityPairOne.getFirst();
|
||||||
@@ -219,9 +229,10 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals(2, cache.getKeys().size());
|
assertEquals(2, cache.getKeys().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDeleteByKey() throws Exception
|
public void testDeleteByKey() throws Exception
|
||||||
{
|
{
|
||||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||||
assertNotNull(entityPairOne);
|
assertNotNull(entityPairOne);
|
||||||
Long id = entityPairOne.getFirst();
|
Long id = entityPairOne.getFirst();
|
||||||
@@ -235,9 +246,10 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals(0, cache.getKeys().size());
|
assertEquals(0, cache.getKeys().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDeleteByValue() throws Exception
|
public void testDeleteByValue() throws Exception
|
||||||
{
|
{
|
||||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||||
assertNotNull(entityPairOne);
|
assertNotNull(entityPairOne);
|
||||||
Long id = entityPairOne.getFirst();
|
Long id = entityPairOne.getFirst();
|
||||||
@@ -251,9 +263,10 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals(0, cache.getKeys().size());
|
assertEquals(0, cache.getKeys().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testClear() throws Exception
|
public void testClear() throws Exception
|
||||||
{
|
{
|
||||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||||
assertNotNull(entityPairOne);
|
assertNotNull(entityPairOne);
|
||||||
Long id = entityPairOne.getFirst();
|
Long id = entityPairOne.getFirst();
|
||||||
@@ -266,16 +279,42 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
assertEquals(0, cache.getKeys().size()); // ... but cache must be empty
|
assertEquals(0, cache.getKeys().size()); // ... but cache must be empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetCachedValue() throws Exception
|
||||||
|
{
|
||||||
|
// Create a new value
|
||||||
|
TestValue valueCached = new TestValue(getClass().getName() + "-CACHED");
|
||||||
|
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueCached, controlDAO);
|
||||||
|
assertNotNull(entityPairOne);
|
||||||
|
Long id = entityPairOne.getFirst();
|
||||||
|
// We cache both by value and by key, so we should have 2 entries
|
||||||
|
assertEquals(2, cache.getKeys().size());
|
||||||
|
|
||||||
|
// Check the cache for the previously created value
|
||||||
|
Pair<Long, Object> entityPairCacheCheck = entityLookupCacheA.getCachedEntityByValue(valueCached);
|
||||||
|
assertNotNull(entityPairCacheCheck);
|
||||||
|
assertEquals(id, entityPairCacheCheck.getFirst());
|
||||||
|
|
||||||
|
// Clear the cache and attempt to retrieve it again
|
||||||
|
entityLookupCacheA.clear();
|
||||||
|
entityPairCacheCheck = entityLookupCacheA.getCachedEntityByValue(valueCached);
|
||||||
|
|
||||||
|
// Since we are only retrieving from cache, the value should not be found
|
||||||
|
assertNull(entityPairCacheCheck);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to represent business object
|
* Helper class to represent business object
|
||||||
*/
|
*/
|
||||||
private static class TestValue
|
private static class TestValue
|
||||||
{
|
{
|
||||||
private final String val;
|
private final String val;
|
||||||
|
|
||||||
private TestValue(String val)
|
private TestValue(String val)
|
||||||
{
|
{
|
||||||
this.val = val;
|
this.val = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj)
|
public boolean equals(Object obj)
|
||||||
{
|
{
|
||||||
@@ -283,8 +322,9 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return val.equals( ((TestValue)obj).val );
|
return val.equals(((TestValue) obj).val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
{
|
{
|
||||||
@@ -297,7 +337,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
{
|
{
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
assertTrue(value instanceof TestValue);
|
assertTrue(value instanceof TestValue);
|
||||||
String dbValue = ((TestValue)value).val;
|
String dbValue = ((TestValue) value).val;
|
||||||
return dbValue;
|
return dbValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +358,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
public Pair<Long, Object> findByValue(Object value)
|
public Pair<Long, Object> findByValue(Object value)
|
||||||
{
|
{
|
||||||
assertTrue(value == null || value instanceof TestValue);
|
assertTrue(value == null || value instanceof TestValue);
|
||||||
String dbValue = (value == null) ? null : ((TestValue)value).val;
|
String dbValue = (value == null) ? null : ((TestValue) value).val;
|
||||||
|
|
||||||
for (Map.Entry<Long, String> entry : database.entrySet())
|
for (Map.Entry<Long, String> entry : database.entrySet())
|
||||||
{
|
{
|
||||||
@@ -336,7 +376,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
public Pair<Long, Object> createValue(Object value)
|
public Pair<Long, Object> createValue(Object value)
|
||||||
{
|
{
|
||||||
assertTrue(value == null || value instanceof TestValue);
|
assertTrue(value == null || value instanceof TestValue);
|
||||||
String dbValue = (value == null) ? null : ((TestValue)value).val;
|
String dbValue = (value == null) ? null : ((TestValue) value).val;
|
||||||
|
|
||||||
// Kick out any duplicate values
|
// Kick out any duplicate values
|
||||||
if (database.containsValue(dbValue))
|
if (database.containsValue(dbValue))
|
||||||
@@ -372,7 +412,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
database.put(key, ((TestValue)value).val);
|
database.put(key, ((TestValue) value).val);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user