Package org.jpox.jpa

Source Code of org.jpox.jpa.EntityManagerImpl

/**********************************************************************
Copyright (c) 2006 Erik Bengtson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
2006 Andy Jefferson - provded exception handling
2008 Andy Jefferson - change query interface to be independent of store.rdbms
    ...
**********************************************************************/
package org.jpox.jpa;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityNotFoundException;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContextType;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.TransactionRequiredException;

import org.jpox.ClassLoaderResolver;
import org.jpox.ObjectManager;
import org.jpox.ObjectManagerFactoryImpl;
import org.jpox.StateManager;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXObjectNotFoundException;
import org.jpox.jdo.AbstractPersistenceManager;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.QueryLanguage;
import org.jpox.metadata.QueryMetaData;
import org.jpox.metadata.QueryResultMetaData;
import org.jpox.metadata.TransactionType;
import org.jpox.util.Localiser;
import org.jpox.util.StringUtils;

/**
* EntityManager implementation for JPA.
*
* @version $Revision: 1.1 $
*/
public class EntityManagerImpl implements EntityManager
{
    /** Localisation utility for output messages */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.jpa.Localisation",
        JPOXJPAHelper.class.getClassLoader());

    /** Underlying PersistenceManager that provides the persistence capability. TODO Remove this when using just ObjectManager */
    protected PersistenceManager pm;

    /** The underlying ObjectManager managing the persistence. */
    protected ObjectManager om;

    /** Parent EntityManagerFactory. */
    protected EntityManagerFactory emf;

    /** Current Transaction (when using ResourceLocal). Will be null if using JTA. */
    protected EntityTransaction tx;

    /** The Flush Mode. */
    protected FlushModeType flushMode;

    /** Type of Persistence Context */
    protected PersistenceContextType persistenceContextType;

    /**
     * Constructor.
     * @param emf The parent EntityManagerFactory
     * @param pmf The underlying PersistenceManagerFactory that handles persistence
     * @param contextType The Persistence Context type
     */
    public EntityManagerImpl(EntityManagerFactory emf, PersistenceManagerFactory pmf, PersistenceContextType contextType)
    {
        this.emf = emf;

        // Allocate our ObjectManager
        // TODO Remove the PM when we disconnect StateManager from PersistenceManager
        pm = pmf.getPersistenceManager();
        om = ((AbstractPersistenceManager)pm).getObjectManager();

        ObjectManagerFactoryImpl omf = (ObjectManagerFactoryImpl)pmf;
        if (omf.getOMFContext().getPersistenceConfiguration().getStringProperty("org.jpox.TransactionType").equalsIgnoreCase(
            TransactionType.RESOURCE_LOCAL.toString()))
        {
            // Using ResourceLocal transaction so allocate a transaction
            tx = new EntityTransactionImpl(om);
        }
    }

    /**
     * Clear the persistence context, causing all managed entities to become detached.
     * Changes made to entities that have not been flushed to the database will not be persisted.
     */
    public void clear()
    {
        assertIsOpen();
        om.detachAll();
        om.clearDirty();
        om.evictAllObjects();
    }

    /**
     * Determine whether the EntityManager is open.
     * @return true until the EntityManager has been closed.
     */
    public boolean isOpen()
    {
        return !om.isClosed();
    }

    /**
     * Close an application-managed EntityManager.
     * After the close method has been invoked, all methods on the EntityManager instance and any Query objects obtained
     * from it will throw the  IllegalStateException except for getTransaction and isOpen (which will return false).
     * If this method is called when the EntityManager is associated with an active transaction, the persistence context
     * remains managed until the transaction completes.
     * @throws IllegalStateException if the EntityManager is container-managed.
     */
    public void close()
    {
        assertIsOpen();

        if (((EntityManagerFactoryImpl)emf).isContainerManaged())
        {
            //TODO is this in the spec?
            //throw new IllegalStateException(LOCALISER.msg("EM.ContainerManagedClose"));
        }

        pm.close();
    }

    /**
     * Check if the instance belongs to the current persistence context.
     * @param entity
     * @return Whether it is contained in the current context
     * @throws IllegalArgumentException if not an entity
     */
    public boolean contains(Object entity)
    {
        assertIsOpen();
        assertEntity(entity);
        if (om.getApiAdapter().getPersistenceManager(entity) != pm)
        {
            return false;
        }
        if (om.getApiAdapter().isDeleted(entity))
        {
            return false;
        }
        if (om.getApiAdapter().isDetached(entity))
        {
            return false;
        }
        return true;
    }

    /**
     * Method to find an object from its primary key.
     * @param entityClass The entity class
     * @param primaryKey The PK
     * @return the found entity instance or null if the entity does not exist
     * @throws IllegalArgumentException if the first argument does not denote an entity type or the second argument is
     *     not a valid type for that entity's primary key
     */
    @SuppressWarnings("unchecked")
    public Object find(Class entityClass, Object primaryKey)
    {
        assertIsOpen();
        assertEntity(entityClass);

        AbstractClassMetaData acmd = om.getMetaDataManager().getMetaDataForClass(entityClass, om.getClassLoaderResolver());
        if (acmd == null)
        {
            throw new EntityNotFoundException();
        }
        Object pc;
        try
        {
            if (acmd.getObjectidClass().equals(primaryKey.getClass().getName()))
            {
                pc = om.findObject(primaryKey, true, true, null);
            }
            else
            {
                Object id = null;
                try
                {
                    id = om.newObjectId(entityClass, primaryKey);
                }
                catch (JPOXException jpe)
                {
                    throw new IllegalArgumentException(jpe);
                }
                pc = om.findObject(id, true, true, null);
            }
        }
        catch (JPOXObjectNotFoundException ex)
        {
            // in JPA, if object not found return null
            return null;
        }

        if (om.getApiAdapter().isTransactional(pc))
        {
            // transactional instances are not validated, so we check if a deleted instance has been flushed
            StateManager sm = om.findStateManager(pc);
            if (om.getApiAdapter().isDeleted(pc))
            {
                try
                {
                    sm.locate();
                }
                catch (JPOXObjectNotFoundException ex)
                {
                    // the instance has been flushed, and it was not found, so we return null
                    return null;
                }
            }
        }
        return pc;
    }

    /**
     * Return the underlying provider object for the EntityManager, if available.
     * The result of this method is implementation specific.
     */
    public Object getDelegate()
    {
        assertIsOpen();

        // Assume that the "underlying provider object" is the ObjectManager since the spec doesnt define it.
        return om;
    }

    /**
     * Get an instance, whose state may be lazily fetched. If the requested
     * instance does not exist in the database, the EntityNotFoundException is
     * thrown when the instance state is first accessed. The persistence
     * provider runtime is permitted to throw the EntityNotFoundException when
     * getReference is called. The application should not expect that the
     * instance state will be available upon detachment, unless it was accessed
     * by the application while the entity manager was open.
     * @param entityClass Class of the entity
     * @param primaryKey The PK
     * @return the found entity instance
     * @throws IllegalArgumentException if the first argument does not denote an entity type or the second argument is not
     *     a valid type for that entities PK
     * @throws EntityNotFoundException if the entity state cannot be accessed
     */
    @SuppressWarnings("unchecked")
    public Object getReference(Class entityClass, Object primaryKey)
    {
        assertIsOpen();
        assertEntity(entityClass);

        Object id = null;
        try
        {
            id = om.newObjectId(entityClass, primaryKey);
        }
        catch (JPOXException jpe)
        {
            throw new IllegalArgumentException(jpe);
        }

        // TODO Handle exceptions correctly
        return om.findObject(id, true, true, null);
    }

    /**
     * Set the lock mode for an entity object contained in the persistence context.
     * @param entity The Entity
     * @param lockMode Lock mode
     * @throws PersistenceException if an unsupported lock call is made
     * @throws IllegalArgumentException if the instance is not an entity or is a detached entity
     * @throws TransactionRequiredException if there is no transaction
     */
    public void lock(Object entity, LockModeType lockMode)
    {
        assertIsOpen();
        assertIsActive();
        assertEntity(entity);
        if (om.getApiAdapter().isDetached(entity))
        {
            throw new IllegalArgumentException(LOCALISER.msg("EM.EntityIsDetached",
                StringUtils.toJVMIDString(entity), "" + om.getApiAdapter().getIdForObject(entity)));
        }
        if (!contains(entity))
        {
            // The object is not contained (the javadoc doesnt explicitly say which exception to throw here)
            throw new PersistenceException("Entity is not contained in this persistence context so cant lock it");
        }

        // TODO Implement lock()
    }

    /**
     * Make an instance managed and persistent.
     * @param entity The Entity
     * @throws EntityExistsException if the entity already exists.
     *     (The EntityExistsException may be thrown when the persist operation is invoked,
     *     or the EntityExistsException/PersistenceException may be thrown at flush/commit time.)
     * @throws IllegalArgumentException if not an entity
     * @throws TransactionRequiredException if invoked on a container-managed entity manager
     *     of type PersistenceContextType.TRANSACTION and there is no transaction.
     */
    public void persist(Object entity)
    {
        assertIsOpen();
        assertTransactionNotRequired();
        assertEntity(entity);
        if (om.exists(entity))
        {
            if (om.getApiAdapter().isDetached(entity))
            {
                // The JPA spec is very confuse about when this exception
                // is thrown, however the JPA TCK test, invokes this operation
                // multiple times over the same instance
                // Entity is already persistent. Maybe the ObjectManager.exists method isnt the best way of checking
                throw new EntityExistsException(LOCALISER.msg("EM.EntityIsPersistent", StringUtils.toJVMIDString(entity)));
            }
        }

        try
        {
            om.persistObject(entity);
        }
        catch (JPOXException jpe)
        {
            throw JPOXJPAHelper.getJPAExceptionForJPOXException(jpe);
        }
    }

    /**
     * Merge the state of the given entity into the current persistence context.
     * @param entity The Entity
     * @return the instance that the state was merged to
     * @throws IllegalArgumentException if instance is not an entity or is a removed entity
     * @throws TransactionRequiredException if invoked on a container-managed entity manager
     *     of type PersistenceContextType.TRANSACTION and there is no transaction.
     */
    @SuppressWarnings("unchecked")
    public Object merge(Object entity)
    {
        assertIsOpen();
        assertTransactionNotRequired();
        assertEntity(entity);
        if (om.getApiAdapter().isDeleted(entity))
        {
            throw new IllegalArgumentException(LOCALISER.msg("EM.EntityIsDeleted",
                StringUtils.toJVMIDString(entity), "" + om.getApiAdapter().getIdForObject(entity)));
        }

        try
        {
            return om.persistObject(entity);
        }
        catch (JPOXException jpe)
        {
            throw JPOXJPAHelper.getJPAExceptionForJPOXException(jpe);
        }
    }

    /**
     * Refresh the state of the instance from the database, overwriting changes made to the entity, if any.
     * @param entity The Entity
     * @throws IllegalArgumentException if not an entity or entity is not managed
     * @throws TransactionRequiredException if invoked on a container-managed entity manager
     *     of type PersistenceContextType.TRANSACTION and there is no transaction.
     * @throws EntityNotFoundException if the entity no longer exists in the database
     */
    public void refresh(Object entity)
    {
        assertIsOpen();
        assertTransactionNotRequired();
        assertEntity(entity);
        if (om.getApiAdapter().getPersistenceManager(entity) != pm)
        {
            throw new IllegalArgumentException(LOCALISER.msg("EM.EntityIsNotManaged", StringUtils.toJVMIDString(entity)));
        }
        if (!om.exists(entity))
        {
            throw new EntityNotFoundException(LOCALISER.msg("EM.EntityNotInDatastore", StringUtils.toJVMIDString(entity)));
        }

        try
        {
            om.refreshObject(entity);
        }
        catch (JPOXException jpe)
        {
            throw JPOXJPAHelper.getJPAExceptionForJPOXException(jpe);
        }
    }

    /**
     * Remove the entity instance.
     * @param entity The Entity
     * @throws IllegalArgumentException if not an entity or if a detached entity
     * @throws TransactionRequiredException if invoked on a container-managed entity manager
     *     of type PersistenceContextType.TRANSACTION and there is no transaction.
     */
    public void remove(Object entity)
    {
        assertIsOpen();
        assertTransactionNotRequired();
        if (entity == null)
        {
            //ignore
            return;
        }
        assertEntity(entity);

        // What if the object doesnt exist in the datastore ? IllegalArgumentException. Spec says nothing
        if (om.getApiAdapter().isDetached(entity))
        {
            throw new IllegalArgumentException(LOCALISER.msg("EM.EntityIsDetached",
                StringUtils.toJVMIDString(entity), "" + om.getApiAdapter().getIdForObject(entity)));
        }

        try
        {
            om.deleteObject(entity);
        }
        catch (JPOXException jpe)
        {
            throw JPOXJPAHelper.getJPAExceptionForJPOXException(jpe);
        }
    }

    /**
     * Synchronize the persistence context to the underlying database.
     * @throws TransactionRequiredException if there is no transaction
     * @throws PersistenceException if the flush fails
     */
    public void flush()
    {
        assertIsOpen();
        assertIsActive();

        try
        {
            om.flush();
        }
        catch (JPOXException jpe)
        {
            throw JPOXJPAHelper.getJPAExceptionForJPOXException(jpe);
        }
    }

    /**
     * Get the flush mode that applies to all objects contained in the persistence context.
     * @return flushMode
     */
    public FlushModeType getFlushMode()
    {
        assertIsOpen();
        return flushMode;
    }

    /**
     * Set the flush mode that applies to all objects contained in the persistence context.
     * @param flushMode Mode of flush
     */
    public void setFlushMode(FlushModeType flushMode)
    {
        assertIsOpen();
        this.flushMode = flushMode;
    }

    // ------------------------------------ Transactions --------------------------------------

    /**
     * Return the resource-level transaction object.
     * The EntityTransaction instance may be used serially to begin and commit multiple transactions.
     * @return EntityTransaction instance
     * @throws IllegalStateException if invoked on a JTA EntityManager.
     */
    public EntityTransaction getTransaction()
    {
        if (tx == null)
        {
            throw new IllegalStateException(LOCALISER.msg("EM.TransactionNotLocal"));
        }

        return tx;
    }

    /**
     * Indicate to the EntityManager that a JTA transaction is active.
     * This method should be called on a JTA application managed EntityManager that was created
     * outside the scope of the active transaction to associate it with the current JTA transaction.
     * @throws TransactionRequiredException if there is no transaction.
     */
    public void joinTransaction()
    {
        assertIsOpen();
        //assertIsActive();
        //TODO assertNotActive
        //assertTransactionNotRequired();
        tx = new EntityTransactionImpl(om);

        // TODO Implement joinTransaction()
    }

    // ------------------------------------ Query Methods --------------------------------------

    /**
     * Create an instance of Query for executing a named query (in JPQL or SQL).
     * @param queryName the name of a query defined in metadata
     * @return the new query instance
     * @throws IllegalArgumentException if a query has not been defined with the given name
     */
    public Query createNamedQuery(String queryName)
    {
        assertIsOpen();

        if (queryName == null)
        {
            throw new IllegalArgumentException(LOCALISER.msg("Query.NamedQueryNotFound", queryName));
        }

        // Find the Query for the specified class
        ClassLoaderResolver clr = om.getClassLoaderResolver();
        QueryMetaData qmd = om.getMetaDataManager().getMetaDataForQuery(null, clr, queryName);
        if (qmd == null)
        {
            throw new IllegalArgumentException(LOCALISER.msg("Query.NamedQueryNotFound", queryName));
        }

        // Create the Query
        try
        {
            if (qmd.getLanguage().equals(QueryLanguage.JPQL))
            {
                // "named-query" so return JPQL
                org.jpox.store.query.Query jpoxQuery = getObjectManager().getOMFContext().getQueryManager().newQuery(
                    qmd.getLanguage().toString(), getObjectManager(), qmd.getQuery());
                return new JPAQuery(this, jpoxQuery, qmd.getLanguage().toString());
            }
            else if (qmd.getLanguage().equals(QueryLanguage.SQL))
            {
                // "named-native-query" so return SQL
                org.jpox.store.query.Query jpoxQuery = getObjectManager().getOMFContext().getQueryManager().newQuery(
                    qmd.getLanguage().toString(), getObjectManager(), qmd.getQuery());
                if (qmd.getResultClass() != null)
                {
                    // Named SQL query with result class
                    String resultClassName = qmd.getResultClass();
                    Class resultClass = null;
                    try
                    {
                        resultClass = om.getClassLoaderResolver().classForName(resultClassName);
                        jpoxQuery.setResultClass(resultClass);
                        return new JPAQuery(this, jpoxQuery, qmd.getLanguage().toString());
                    }
                    catch (Exception e)
                    {
                        // Result class not found so throw exception (not defined in the JPA spec)
                        throw new IllegalArgumentException(LOCALISER.msg("Query.ResultClassNotFound", qmd.getName(), resultClassName));
                    }
                }
                else if (qmd.getResultMetaDataName() != null)
                {
                    QueryResultMetaData qrmd = om.getMetaDataManager().getMetaDataForQueryResult(qmd.getResultMetaDataName());
                    if (qrmd == null)
                    {
                        // TODO Localise this, and check if it is the correct exception to throw
                        throw new IllegalArgumentException("ResultSetMapping " + qmd.getResultMetaDataName() + " is not found");
                    }
                    jpoxQuery.setResultMetaData(qrmd);
                    return new JPAQuery(this, jpoxQuery, qmd.getLanguage().toString());
                }
                else
                {
                    return new JPAQuery(this, jpoxQuery, qmd.getLanguage().toString());
                }
            }
            else
            {
                throw new IllegalArgumentException(LOCALISER.msg("Query.LanguageNotSupportedByStore", qmd.getLanguage()));
            }
        }
        catch (JPOXException jpe)
        {
            throw new IllegalArgumentException(jpe.getMessage(), jpe);
        }
    }

    /**
     * Create an instance of Query for executing an SQL statement.
     * @param sqlString a native SQL query string
     * @return the new query instance
     */
    public Query createNativeQuery(String sqlString)
    {
        assertIsOpen();
        try
        {
            org.jpox.store.query.Query jpoxQuery = getObjectManager().getOMFContext().getQueryManager().newQuery(
                QueryLanguage.SQL.toString(), getObjectManager(), sqlString);
            return new JPAQuery(this, jpoxQuery, QueryLanguage.SQL.toString());
        }
        catch (JPOXException jpe)
        {
            throw new IllegalArgumentException(jpe.getMessage(), jpe);
        }
    }

    /**
     * Create an instance of Query for executing an SQL query.
     * @param sqlString a native SQL query string
     * @param resultClass the class of the resulting instance(s)
     * @return the new query instance
     */
    public Query createNativeQuery(String sqlString, Class resultClass)
    {
        assertIsOpen();
        try
        {
            org.jpox.store.query.Query jpoxQuery = getObjectManager().getOMFContext().getQueryManager().newQuery(
                QueryLanguage.SQL.toString(), getObjectManager(), sqlString);
            jpoxQuery.setResultClass(resultClass);
            return new JPAQuery(this, jpoxQuery, QueryLanguage.SQL.toString());
        }
        catch (JPOXException jpe)
        {
            throw new IllegalArgumentException(jpe.getMessage(), jpe);
        }
    }

    /**
     * Create an instance of Query for executing an SQL query.
     * @param sqlString a native SQL query string
     * @param resultSetMapping the name of the result set mapping
     * @return the new query instance
     */
    public Query createNativeQuery(String sqlString, String resultSetMapping)
    {
        assertIsOpen();
        try
        {
            org.jpox.store.query.Query jpoxQuery = getObjectManager().getOMFContext().getQueryManager().newQuery(
                QueryLanguage.SQL.toString(), getObjectManager(), sqlString);
            QueryResultMetaData qrmd = om.getMetaDataManager().getMetaDataForQueryResult(resultSetMapping);
            if (qrmd == null)
            {
                // TODO Localise this, and check if it is the correct exception to throw
                throw new IllegalArgumentException("ResultSetMapping " + resultSetMapping + " is not found");
            }
            jpoxQuery.setResultMetaData(qrmd);
            return new JPAQuery(this, jpoxQuery, QueryLanguage.SQL.toString());
        }
        catch (JPOXException jpe)
        {
            throw new IllegalArgumentException(jpe.getMessage(), jpe);
        }
    }

    /**
     * Create an instance of Query for executing a JPQL statement.
     * @param queryString a Java Persistence query string
     * @return the new query instance
     * @throws IllegalArgumentException if query string is not valid
     */
    public Query createQuery(String queryString)
    {
        assertIsOpen();
        try
        {
            org.jpox.store.query.Query jpoxQuery = getObjectManager().getOMFContext().getQueryManager().newQuery(
                QueryLanguage.JPQL.toString(), getObjectManager(), queryString);
            return new JPAQuery(this, jpoxQuery, QueryLanguage.JPQL.toString());
        }
        catch (JPOXException jpe)
        {
            throw new IllegalArgumentException(jpe.getMessage(), jpe);
        }
    }

    // ------------------------------------ Convenience Accessors --------------------------------------

    /**
     * Accessor for the underlying ObjectManager providing persistence capability.
     * @return The ObjectManager providing the persistence capability
     */
    public ObjectManager getObjectManager()
    {
        assertIsOpen();
        return om;
    }

    // ------------------------------------------ Assertions --------------------------------------------

    /**
     * Assert if the EntityManager is closed.
     * @throws IllegalStateException When the EntityManaged is closed
     */
    private void assertIsOpen()
    {
        if (om.isClosed())
        {
            throw new IllegalStateException(LOCALISER.msg("EM.IsClosed"));
        }
    }

    /**
     * Assert if the transaction is not active.
     * @throws TransactionRequiredException When the EntityManaged is closed
     */
    private void assertIsActive()
    {
        if (!isTransactionActive())
        {
            throw new TransactionRequiredException(LOCALISER.msg("EM.TransactionRequired"));
        }
    }

    /**
     * @return true if there is an active transaction associated to this EntityManager
     */   
    private boolean isTransactionActive()
    {
        return tx != null && tx.isActive();
    }
   
    /**
     * Assert if the passed entity is not persistable, or has no persistence information.
     * @param entity The entity (or class of the entity)
     * @throws IllegalArgumentException Thrown if not an entity
     */
    private void assertEntity(Object entity)
    {
        if (entity == null)
        {
            throw new IllegalArgumentException(LOCALISER.msg("EM.EntityNotAnEntity", entity));
        }

        Class cls = null;
        if (entity instanceof Class)
        {
            // Class passed in so just check that
            cls = (Class)entity;
        }
        else
        {
            // Object passed in so check its class
            cls = entity.getClass();
        }

        try
        {
            om.assertClassPersistable(cls);
        }
        catch (JPOXException jpe)
        {
            throw new IllegalArgumentException(LOCALISER.msg("EM.EntityNotAnEntity", cls.getName()), jpe);
        }
    }

    /**
     * Assert if the persistence context is TRANSACTION, and the transaction is not active.
     * @throws TransactionRequiredException thrown if the context requires a txn but isnt active
     */
    private void assertTransactionNotRequired()
    {
        if (persistenceContextType == PersistenceContextType.TRANSACTION && !isTransactionActive())
        {
            throw new TransactionRequiredException(LOCALISER.msg("EM.TransactionRequired"));
        }
    }
}
TOP

Related Classes of org.jpox.jpa.EntityManagerImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.