All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.internal.jpa.CacheImpl Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 2008, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     12/04/2008 - 2.0 Darani Yallapragada
//       - 248780: Initial contribution for JPA 2.0
//     06/03/2010 - 2.1 Michael O'Brien
//       - 248780: Refactor Cache Implementation surrounding evict()
//         Fix evict() to handle non-Entity classes
//         Refactor to get IdentityMapAccessor state through EMF reference
//         Refactor dependencies to use Interfaces instead of Impl subclasses
//         Handle no CMPPolicy case for getId()
//         Handle no associated descriptor for Class parameter
//         MappedSuperclasses passed to evict() cause implementing subclasses to be evicted
//         Throw an IAE for Interfaces and Embeddable classes passed to evict()
//
package org.eclipse.persistence.internal.jpa;

import jakarta.persistence.Cache;
import jakarta.persistence.PersistenceException;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.JpaCache;
import org.eclipse.persistence.sessions.IdentityMapAccessor;
import org.eclipse.persistence.sessions.Session;

/**
 * Implements the JPA Cache interface using the EclipseLink cache API through IdentityMapAccessor.
 * @author DaraniY
 */
public class CacheImpl implements JpaCache {

    /** The EntityManagerFactory associated with this Cache */
    private EntityManagerFactoryDelegate emf;

    /**
     */
    public CacheImpl(EntityManagerFactoryDelegate emf) {
        this.emf = emf;
    }

    /**
     * Returns true if the cache contains an Object with the id and Class type, and is valid.
     * @see Cache#contains(Class, Object)
     */
    @Override
    @SuppressWarnings({"rawtypes"})
    public boolean contains(Class cls, Object id) {
        getEntityManagerFactory().verifyOpen();
        Object pk =  createPrimaryKeyFromId(cls, id);
        if(null == pk) {
            return false;
        }
        ClassDescriptor descriptor = getSession().getClassDescriptor(cls); // getDescriptor() is the same call
        /**
         * Check for no descriptor associated with the class parameter.
         * This will occur if the class represents a MappedSuperclass (concrete or abstract class),
         * an interface or Embeddable class.
         */
        if(null == descriptor) {
            // do not throw an IAException: cache_impl_class_has_no_descriptor_is_not_a_persistent_type - just return false
            return false;
        }

        // we can assume that all implementors of IdentityMapAccessor implement getCacheKeyforObject
        CacheKey key = ((org.eclipse.persistence.internal.sessions.IdentityMapAccessor)getAccessor())
            .getCacheKeyForObject(pk, cls, descriptor, false);
        return key != null && key.getObject() != null &&
            !descriptor.getCacheInvalidationPolicy().isInvalidated(key);
    }

    /**
     * INTERNAL:
     * This private method searches the map of descriptors for possible superclasses to the
     * passed in class parameter and invalidates only entities found in the cache.
     * If the class is not an Entity or MappedSuperclass (such as an Embeddable or plain java class)
     *  - nothing will be evicted
     */
    private void evictAssignableEntitySuperclass(Class possibleSuperclass, Object id) {
        // just remove the parent entity
        for(ClassDescriptor candidateAssignableDescriptor : getSession().getDescriptors().values()) {
            // In EclipseLink we need only remove the root descriptor that is assignable from this possibleSubclass because the recurse flag defaults to true in invalidateClass()
            // what if we have 2 roots (don't check for !candidateAssignableDescriptor.isChildDescriptor())
            if(!candidateAssignableDescriptor.isDescriptorTypeAggregate() && // a !Embeddable check and !EmbeddableCollection check
               possibleSuperclass.isAssignableFrom(candidateAssignableDescriptor.getJavaClass())) {
                // id will be null if this private function was called from evict(class)
                if(null == id) {
                    // set the invalidationState to -1 in the cache of a type that can be assigned to the class parameter
                    // this call will invalidate all assignable subclasses from the level of possibleSubclass] in the subtree
                    // we could either loop through each aDescriptor.getJavaClass()
                    // or
                    // let invalidateClass loop for us by passing in the higher [possibleSubclass] - all subclasses of the first parent entity descriptor will be invalidated in this first call
                    getAccessor().invalidateClass(candidateAssignableDescriptor.getJavaClass());
                } else {
                    // evict the class instance that corresponds to the id
                    // initialize the cache of a type that can be assigned to the class parameter
                    getAccessor().invalidateObject(createPrimaryKeyFromId(possibleSuperclass, id), candidateAssignableDescriptor.getJavaClass());
                }
            }
        }
    }

    /**
     * Sets an Object with the id and Class type to be invalid in the cache.
     * Remove the data for entities of the specified class (and its
     * subclasses) from the cache.

* If the class is a MappedSuperclass then the first entity above in the inheritance hierarchy will be evicted * along with all implementing subclasses * If the class is not an Entity or MappedSuperclass but is the root of an entity inheritance tree then * evict the subtree * If the class is not an Entity or MappedSuperclass but inherits from one then * evict up to root descriptor * @see Cache#evict(Class, Object) * @param classToEvict - class to evict - usually representing an Entity or MappedSuperclass * @param id - Primary key of the Entity or MappedSuperclass Class * A null id means invalidate the class - possibly the entire tree or subtree */ @Override @SuppressWarnings({"rawtypes"}) public void evict(Class classToEvict, Object id) { evict(classToEvict, id, false); } /** * Sets an Object with the id and Class type to be invalid in the cache. * Remove the data for entities of the specified class (and its * subclasses) from the cache.

* If the class is a MappedSuperclass then the first entity above in the inheritance hierarchy will be evicted * along with all implementing subclasses * If the class is not an Entity or MappedSuperclass but is the root of an entity inheritance tree then * evict the subtree * If the class is not an Entity or MappedSuperclass but inherits from one then * evict up to root descriptor * @see Cache#evict(Class, Object) * @param classToEvict - class to evict - usually representing an Entity or MappedSuperclass * @param id - Primary key of the Entity or MappedSuperclass Class * A null id means invalidate the class - possibly the entire tree or subtree * @param invalidateInCluster - Invalidate the object id in the cluster, this only applies to a non-null id. */ @Override public void evict(Class classToEvict, Object id, boolean invalidateInCluster) { getEntityManagerFactory().verifyOpen(); /** * The following descriptor lookup will return the Entity representing the classToEvict parameter, * or it will return the first Entity superclass of a MappedSuperclass in the hierarchy * - in this case all subclasses of the parent Entity will be evicted. * The descriptor will be null if the classToEvict represents a non-Entity or non-MappedSuperclass like an Embeddable or plain java class * - in this case nothing will be evicted. */ ClassDescriptor aPossibleSuperclassDescriptor = getSession().getClassDescriptor(classToEvict); // Do not recurse if the class to evict is below its' descriptor in the inheritance tree if(null != aPossibleSuperclassDescriptor) { // Evict all Entity or MappedSuperclass classes if(null != id) { Object cacheKey = createPrimaryKeyFromId(classToEvict, id); if(null != cacheKey) { getAccessor().invalidateObject(cacheKey, classToEvict, invalidateInCluster); } } else { // 312503: always evict all implementing entity subclasses of an entity or mappedSuperclass boolean invalidateRecursively = aPossibleSuperclassDescriptor.getJavaClass().equals(classToEvict); getAccessor().invalidateClass(classToEvict, invalidateRecursively); } } else { // Evict the first possible parent Entity superclass of a non-Entity and non-MappedSuperclass class evictAssignableEntitySuperclass(classToEvict, id); } } /** * Sets all instances of the class to be invalid. * Remove the data for entities of the specified class (and its * subclasses) from the cache.

* If the class is a MappedSuperclass then the first entity above in the inheritance hierarchy will be evicted * along with all implementing subclasses * If the class is not an Entity or MappedSuperclass (such as an Embeddable or plain java class) * - nothing will be evicted * @see Cache#evict(Class) * @param entityOrMappedSuperclassToEvict - Entity or MappedSuperclass Class */ @Override @SuppressWarnings({"rawtypes"}) public void evict(Class entityOrMappedSuperclassToEvict) { // A null id means invalidate the class - possibly the entire tree or subtree evict(entityOrMappedSuperclassToEvict, null); } /** * Sets all instances in the cache to be invalid. * @see Cache#evictAll() */ @Override public void evictAll() { getEntityManagerFactory().verifyOpen(); getEntityManagerFactory().getDatabaseSession().getIdentityMapAccessor().invalidateAll(); } /** * Return the EclipseLink cache key object from the JPA Id object. */ private Object createPrimaryKeyFromId(Class cls, Object id) { Object cacheKey = null; ClassDescriptor aDescriptor = getSession().getDescriptor(cls); // Check that we have a descriptor associated with the class (Entity or MappedSuperclass) if(null == aDescriptor) { // No descriptor found, throw exception for Embeddable or non-persistable java class throw new IllegalArgumentException(ExceptionLocalization.buildMessage( "cache_impl_class_has_no_descriptor_is_not_a_persistent_type", new Object[] {cls})); } // The policy is not set if the mapping is natively defined outside of JPA if(aDescriptor.hasCMPPolicy()) { // we assume that the PK id parameter is correct and do not throw a cache_descriptor_has_no_cmppolicy_set_cannot_create_primary_key exception // The primaryKey may be the same object as the id parameter cacheKey = aDescriptor.getCMPPolicy().createPrimaryKeyFromId(id, getEntityManagerFactory().getDatabaseSession()); } return cacheKey; } /** * ADVANCED: * Resets the entire Object cache, and the Query cache. *

NOTE: Be careful using this method. This method blows away both this session's and its parent's caches. * This includes the server cache or any other cache. This throws away any Objects that have been read in. * Extreme caution should be used before doing this because Object identity will no longer * be maintained for any Objects currently read in. This should only be called * if the application knows that it no longer has references to Objects held in the cache. */ @Override public void clear() { getEntityManagerFactory().verifyOpen(); getAccessor().initializeAllIdentityMaps(); } /** * ADVANCED: * Resets the cache for only the instances of the given Class type. * For inheritance the user must make sure that they only use the root class, * clearing a subclass cache is not allowed (as they share their parents cache). *

NOTE: Caution must be used in doing this to ensure that the Objects within the cache * are not referenced from other Objects of other classes or from the application. */ @Override public void clear(Class cls) { getEntityManagerFactory().verifyOpen(); getAccessor().initializeIdentityMap(cls); } /** * Clear all the query results caches. */ @Override public void clearQueryCache() { getEntityManagerFactory().verifyOpen(); getAccessor().clearQueryCache(); } /** * Clear the named query results cache associated with the query name. */ @Override public void clearQueryCache(String queryName) { getEntityManagerFactory().verifyOpen(); getAccessor().clearQueryCache(queryName); } /** * Clear all named query results cache associated with entity class. */ @Override public void clearQueryCache(Class entityClass) { getEntityManagerFactory().verifyOpen(); getAccessor().invalidateQueryCache(entityClass); } /** * Returns the remaining life of the given Object (in milliseconds). This method is associated with use of * cache invalidation feature and returns the difference between the next expiry * time of the Object and its read time. The method will return 0 for invalidated Objects. */ @Override public long timeToLive(Object object) { getEntityManagerFactory().verifyOpen(); return getAccessor().getRemainingValidTime(object); } /** * Returns true if the Object with the same id and Class type of the * the given Object is valid in the cache. */ @Override public boolean isValid(Object object) { getEntityManagerFactory().verifyOpen(); return getAccessor().isValid(object); } /** * Returns true if the Object with the id and Class type is valid in the cache. */ @Override public boolean isValid(Class cls, Object id) { getEntityManagerFactory().verifyOpen(); Object cacheKey = createPrimaryKeyFromId(cls, id); if(null != cacheKey) { return getAccessor().isValid(cacheKey, cls); } else { return false; } } /** * Used to print all the Objects in the cache. * The output of this method will be logged to this persistence unit's SessionLog at SEVERE level. */ @Override public void print() { getEntityManagerFactory().verifyOpen(); getAccessor().printIdentityMaps(); } /** * Used to print all the Objects in the cache of the Class type. * The output of this method will be logged to this persistence unit's SessionLog at SEVERE level. */ @Override public void print(Class cls) { getEntityManagerFactory().verifyOpen(); getAccessor().printIdentityMap(cls); } /** * Used to print all the currently locked cache keys in the cache. * The output of this method will be logged to this persistence unit's SessionLog at SEVERE level. */ @Override public void printLocks() { getEntityManagerFactory().verifyOpen(); getAccessor().printIdentityMapLocks(); } /** * This can be used to help debugging an Object identity problem. * An Object identity problem is when an Object in the cache references an * Object that is not in the cache. This method will validate that all cached * Objects are in a correct state. */ @Override public void validate() { getEntityManagerFactory().verifyOpen(); getAccessor().validateCache(); } /** * Returns the Object from the cache map with the id * and Class type. */ @Override public Object getObject(Class cls, Object id) { getEntityManagerFactory().verifyOpen(); Object cacheKey = createPrimaryKeyFromId(cls, id); return getAccessor().getFromIdentityMap(cacheKey, cls); } /** * ADVANCED: * Puts the given Object into the cache. * This is a very advanced method, and caution should be used in adding objects to the cache * as other objects may have relationships to previous object, or this object may have * relationships to other objects. */ @Override public Object putObject(Object object) { getEntityManagerFactory().verifyOpen(); return getAccessor().putInIdentityMap(object); } /** * ADVANCED: * Removes the Object from the cache. *

NOTE: Caution should be used when calling to avoid violating Object identity. * The application should only call this if its known that no references to the Object exist. */ @Override public Object removeObject(Object object) { getEntityManagerFactory().verifyOpen(); return getAccessor().removeFromIdentityMap(object); } /** * ADVANCED: * Removes the Object with the id and Class type from the cache. *

NOTE: Caution should be used when calling to avoid violating Object identity. * The application should only call this if its known that no references to the Object exist. */ @Override public Object removeObject(Class cls, Object id) { getEntityManagerFactory().verifyOpen(); Object cacheKey = createPrimaryKeyFromId(cls, id); return getAccessor().removeFromIdentityMap(cacheKey, cls); } /** * Returns true if the cache contains an Object with the same id and Class type of the given object. */ @Override public boolean contains(Object object) { return contains(object.getClass(), getId(object)); } /** * Sets the object to be invalid in the cache. * @see JpaCache#evict(Object) */ @Override public void evict(Object object) { getEntityManagerFactory().verifyOpen(); getAccessor().invalidateObject(object); } /** * Sets an Object to be invalid in the cache. * If true is passed, the object is also invalidated across cache coordination. * Cache coordination must be enabled for this to have an affect. */ @Override public void evict(Object object, boolean invalidateInCluster) { getEntityManagerFactory().verifyOpen(); getAccessor().invalidateObject(object, invalidateInCluster); } /** * INTERNAL: * Return the EntityManagerFactory associated with this CacheImpl. */ protected EntityManagerFactoryDelegate getEntityManagerFactory() { return this.emf; } /** * INTERNAL: * Return the Session associated with the EntityManagerFactory. */ protected Session getSession() { return getEntityManagerFactory().getDatabaseSession(); } /** * INTERNAL: * Return the IdentityMapAccessor associated with the session on the EntityManagerFactory on this CacheImpl. */ protected IdentityMapAccessor getAccessor() { return getSession().getIdentityMapAccessor(); } /** * This method will return the objects's Id. * If the descriptor associated with the domain object is null - an IllegalArgumentException is thrown. * If the CMPPolicy associated with the domain object's descriptor is null * the Id will be determined using the ObjectBuilder on the descriptor - which may return * the Id stored in the weaved _persistence_primaryKey field. * @see JpaCache#getId(Object) */ @Override public Object getId(Object object) { getEntityManagerFactory().verifyOpen(); ClassDescriptor aDescriptor = getSession().getDescriptor(object.getClass()); // Handle a null descriptor from a detached entity (closed EntityManager), or the entity exists in another session if(null == aDescriptor) { throw new IllegalArgumentException(ExceptionLocalization.buildMessage( "cache_impl_object_has_no_descriptor_is_not_a_persistent_type", new Object[] {object})); } // Handle a null CMPPolicy from a MappedSuperclass if(!aDescriptor.hasCMPPolicy()) { // the following code gets the key either from the weaved _persistence_primaryKey field or using valueFromObject() if not bytecode enhanced return aDescriptor.getObjectBuilder().extractPrimaryKeyFromObject(object, (AbstractSession)getSession()); } else { // Get identifier via EMF return getEntityManagerFactory().getIdentifier(object); } } @Override public T unwrap(Class cls) { if (cls.equals(JpaCache.class)){ return (T) this; } if (cls.equals(IdentityMapAccessor.class)){ return (T) getAccessor(); } throw new PersistenceException(ExceptionLocalization.buildMessage("unable_to_unwrap_jpa", new String[]{Cache.class.getName(),cls.getName()})); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy