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

org.datanucleus.store.db4o.DB4OPersistenceHandler Maven / Gradle / Ivy

/**********************************************************************
Copyright (c) 2008 Andy Jefferson 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:
    ...
**********************************************************************/
package org.datanucleus.store.db4o;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.PersistenceConfiguration;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.exceptions.NucleusObjectNotFoundException;
import org.datanucleus.exceptions.NucleusOptimisticException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.identity.DatastoreUniqueOID;
import org.datanucleus.identity.OID;
import org.datanucleus.identity.OIDFactory;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.VersionMetaData;
import org.datanucleus.metadata.VersionStrategy;
import org.datanucleus.state.ObjectProviderFactory;
import org.datanucleus.store.AbstractPersistenceHandler;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.StoreManager;
import org.datanucleus.store.VersionHelper;
import org.datanucleus.store.connection.ManagedConnection;
import org.datanucleus.store.db4o.fieldmanager.ActivationFieldManager;
import org.datanucleus.store.fieldmanager.DeleteFieldManager;
import org.datanucleus.store.fieldmanager.PersistFieldManager;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.ext.ObjectNotStorableException;

/**
 * Persistence handler for persisting to DB4O datastores.
 */
public class DB4OPersistenceHandler extends AbstractPersistenceHandler
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER = Localiser.getInstance(
        "org.datanucleus.store.db4o.Localisation", DB4OStoreManager.class.getClassLoader());

    /** Manager for the store. */
    protected final DB4OStoreManager storeMgr;

    /** Temporary map of a list of the objects being deleted, keyed by the thread name. Used by delete() method. */
    private Map currentDeleteThreads = new ConcurrentHashMap();

    /**
     * Thread-specific state information (instances of {@link OperationInfo}) for inserting.
     * Allows us to detect the primary object to be inserted, so we can call NeoDatis with that
     * and not for any others.
     */
    private ThreadLocal insertInfoThreadLocal = new ThreadLocal()
    {
        protected Object initialValue()
        {
            return new OperationInfo();
        }
    };

    private static class OperationInfo
    {
        /** List of StateManagers to perform the operation on. */
        List smList = null;
    }

    /**
     * Constructor.
     * @param storeMgr Manager for the datastore
     */
    public DB4OPersistenceHandler(StoreManager storeMgr)
    {
        this.storeMgr = (DB4OStoreManager)storeMgr;
    }

    /**
     * Method to close the handler and release any resources.
     */
    public void close()
    {
        currentDeleteThreads.clear();
        currentDeleteThreads = null;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.AbstractPersistenceHandler#useReferentialIntegrity()
     */
    @Override
    public boolean useReferentialIntegrity()
    {
        return true;
    }

    /**
     * Inserts a persistent object into the database.
     * Provides persist-by-reachability using PersistFieldManager to go through all related PC fields.
     * If this is the principal object for this thread (the object that the user invoked makePersistent on)
     * then this will actually update the object in DB4O when all reachables have been processed. The reachability
     * process means that we assign StateManagers down the object graph, and that object states are set up.
     * Only a single DB4O "set" command is invoked per user makePersistent() call, using DB4Os ability to persist
     * by reachability.
     * @param sm The state manager of the object to be inserted.
     * @throws NucleusDataStoreException when an error occurs in the datastore communication
     */
    public void insertObject(ObjectProvider sm)
    {
        // Check if read-only so update not permitted
        storeMgr.assertReadOnlyForUpdateOfObject(sm);

        if (sm.getClassMetaData().getIdentityType() == IdentityType.APPLICATION)
        {
            // Check existence of the object since DB4O doesn't enforce application identity
            try
            {
                locateObject(sm);
                throw new NucleusUserException(LOCALISER.msg("DB4O.Insert.ObjectWithIdAlreadyExists",
                    sm.toPrintableID(), sm.getInternalObjectId()));
            }
            catch (NucleusObjectNotFoundException onfe)
            {
                // Do nothing since object with this id doesn't exist
            }
        }

        // Get the InsertInfo for this thread so we know if this is the primary object or a reachable
        OperationInfo insertInfo = (OperationInfo) insertInfoThreadLocal.get();
        boolean primaryObject = false;
        if (insertInfo.smList == null)
        {
            // Primary object
            primaryObject = true;
            insertInfo.smList = new ArrayList();
        }
        insertInfo.smList.add(sm);

        String className = sm.getObject().getClass().getName();
        if (!storeMgr.managesClass(className))
        {
            // Class is not yet registered here so register it
            storeMgr.addClass(className, sm.getExecutionContext().getClassLoaderResolver());
        }

        // Perform reachability, wrapping all reachable PCs with StateManagers (recurses through this method)
        // We don't replace all containers with SCO wrappers at this point since we don't want them in DB4O
        sm.provideFields(sm.getClassMetaData().getAllMemberPositions(), new PersistFieldManager(sm, false));

        ManagedConnection mconn = storeMgr.getConnection(sm.getExecutionContext());
        ObjectContainer cont = (ObjectContainer)mconn.getConnection();
        try
        {
            // Insert the object.
            // Note that we are persisting objects with the StateManager etc set. 
            // This doesnt matter for us since DB4O will not be persisting any transient/static fields so 
            // loses that information when entering the datastore
            long startTime = 0;
            if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
            {
                startTime = System.currentTimeMillis();
                NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("DB4O.Insert.Start", 
                    sm.toPrintableID(), sm.getInternalObjectId()));
            }

            cont.ext().store(sm.getObject(), 1);
            if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
            {
                NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("DB4O.ExecutionTime", 
                    (System.currentTimeMillis() - startTime)));
            }
            if (storeMgr.getRuntimeManager() != null)
            {
                storeMgr.getRuntimeManager().incrementInsertCount();
            }

            ObjectProvider objSM = sm.getExecutionContext().findObjectProvider(sm.getObject());
            if (objSM != null)
            {
                // Update the object with any info populated by DB4O
                AbstractClassMetaData cmd = objSM.getClassMetaData();
                if (cmd.getIdentityType() == IdentityType.DATASTORE)
                {
                    // datastore-identity is always datastore attributed for DB4O, using internal DB4O identity
                    long db4oId = cont.ext().getID(sm.getObject());
                    if (db4oId > 0)
                    {
                        objSM.setPostStoreNewObjectId(OIDFactory.getInstance(storeMgr.getNucleusContext(), db4oId));
                    }
                    else
                    {
                        NucleusLogger.DATASTORE.error(LOCALISER.msg("DB4O.Insert.ObjectPersistFailed", 
                            sm.toPrintableID()));
                    }
                }

                VersionMetaData vermd = cmd.getVersionMetaDataForClass();
                if (vermd != null && vermd.getVersionStrategy() == VersionStrategy.VERSION_NUMBER)
                {
                    // versioned object so update its version
                    long version = cont.ext().getObjectInfo(sm.getObject()).getVersion();
                    objSM.setTransactionalVersion(Long.valueOf(version));
                    NucleusLogger.DATASTORE.debug(LOCALISER.msg("DB4O.Insert.ObjectPersistedWithVersion",
                        sm.toPrintableID(), objSM.getInternalObjectId(), "" + version));
                }
                else
                {
                    if (NucleusLogger.DATASTORE.isDebugEnabled())
                    {
                        NucleusLogger.DATASTORE.debug(LOCALISER.msg("DB4O.Insert.ObjectPersisted",
                            sm.toPrintableID(), objSM.getInternalObjectId()));
                    }
                }
            }
        }
        catch (ObjectNotStorableException ex)
        {
            throw new NucleusDataStoreException(ex.getMessage(), ex, sm.getObject());
        }
        finally
        {
            mconn.release();
        }

        if (primaryObject)
        {
            // Replace all SCO fields with wrappers now that we have handled this graph
            // If we did this when processing each object we would have got DB4O trying to instantiate
            // the SCO's of the reachable objects for some reason so just leave til done all
            Iterator iter = insertInfo.smList.iterator();
            while (iter.hasNext())
            {
                ObjectProvider objSM = (ObjectProvider)iter.next();
                objSM.replaceAllLoadedSCOFieldsWithWrappers();
            }

            // Clean out the OperationInfo for inserts on this thread
            insertInfo.smList.clear();
            insertInfo.smList = null;
            insertInfoThreadLocal.remove();
        }
    }

    /**
     * Updates a persistent object in the database.
     * Provides reachability via PersistFieldManager, meaning that all PC fields that have been updated will be
     * attached/updated too. Each call to "update" here will result in a call to DB4O "set".
     * @param sm The state manager of the object to be updated.
     * @param fieldNumbers The numbers of the fields to be updated.
     * @throws NucleusDataStoreException when an error occurs in the datastore communication
     * @throws NucleusOptimisticException thrown if version checking fails
     */
    public void updateObject(ObjectProvider sm, int fieldNumbers[])
    {
        // Check if read-only so update not permitted
        storeMgr.assertReadOnlyForUpdateOfObject(sm);

        // Perform reachability, wrapping all reachable PCs with StateManagers (recurses through this method)
        sm.provideFields(fieldNumbers, new PersistFieldManager(sm, false));

        // Unwrap any SCO wrapped fields so we don't persist them
        sm.replaceAllLoadedSCOFieldsWithValues();

        ManagedConnection mconn = storeMgr.getConnection(sm.getExecutionContext());
        ObjectContainer cont = (ObjectContainer)mconn.getConnection();
        try
        {
            VersionMetaData vermd = sm.getClassMetaData().getVersionMetaDataForClass();
            if (sm.getExecutionContext().getTransaction().getOptimistic() && vermd != null)
            {
                // Optimistic transaction so perform version check before any update
                long datastoreVersion = cont.ext().getObjectInfo(sm.getObject()).getVersion();
                if (datastoreVersion > 0)
                {
                    VersionHelper.performVersionCheck(sm, Long.valueOf(datastoreVersion), vermd);
                }
            }

            long startTime = System.currentTimeMillis();
            if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
            {
                AbstractClassMetaData cmd = sm.getClassMetaData();
                StringBuffer fieldStr = new StringBuffer();
                for (int i=0;i 0)
                    {
                        fieldStr.append(",");
                    }
                    fieldStr.append(cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]).getName());
                }
                NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("DB4O.Update.Start", 
                    sm.toPrintableID(), sm.getInternalObjectId(), fieldStr.toString()));
            }

            // This method is to update just particular fields but DB4O doesn't support that
            // so instead we fetch all fields and then do the update.
            // TODO Is this really necessary, and why? Catering for DB4O inadequacies?
            // Assume here that the DB4O-deactivated related objects loaded by this call will not ever be
            // accessed, because we assume that the PC will go into a hollow state.
            // If this assumption is wrong, we should give each DB4O-deactivated child object a state mananger. 
            Object obj = sm.getObject();
            int[] dirtyFieldNumbers = sm.getDirtyFieldNumbers();
            if (dirtyFieldNumbers != null && dirtyFieldNumbers.length > 0)
            {
                // Some fields need preserving
                Object copy = storeMgr.getApiAdapter().getCopyOfPersistableObject(obj, sm, dirtyFieldNumbers);
                if (NucleusLogger.DATASTORE.isDebugEnabled())
                {
                    NucleusLogger.DATASTORE.debug(LOCALISER.msg("DB4O.Object.Refreshing", 
                        StringUtils.toJVMIDString(obj)));
                }
                cont.ext().refresh(obj, 1);
                storeMgr.getApiAdapter().copyFieldsFromPersistableObject(copy, dirtyFieldNumbers, obj);
            }
            else
            {
                // Just needs updating
                if (NucleusLogger.DATASTORE.isDebugEnabled())
                {
                    NucleusLogger.DATASTORE.debug(LOCALISER.msg("DB4O.Object.Refreshing", 
                        StringUtils.toJVMIDString(obj)));
                }
                cont.ext().refresh(obj, 1);
            }

            // Do the update in DB4O
            cont.ext().store(sm.getObject(), 1);
            if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
            {
                NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("DB4O.ExecutionTime", 
                    (System.currentTimeMillis() - startTime)));
            }
            if (storeMgr.getRuntimeManager() != null)
            {
                storeMgr.getRuntimeManager().incrementUpdateCount();
            }

            if (vermd != null && vermd.getVersionStrategy() == VersionStrategy.VERSION_NUMBER)
            {
                // versioned object so update its version now that we've persisted the changes
                long version = cont.ext().getObjectInfo(sm.getObject()).getVersion();
                sm.setTransactionalVersion(Long.valueOf(version));
            }

            // Wrap any unwrapped SCO fields so any subsequent changes are picked up
            sm.replaceAllLoadedSCOFieldsWithWrappers();
        }
        finally
        {
            mconn.release();
        }
    }

    /**
     * Deletes a persistent object from the database.
     * Performs delete-by-reachability ("cascade-delete") via DeleteFieldManager. 
     * If this is the principal object to be deleted (via the users deletePersistent call) then after 
     * processing the object graph will call DB4O "delete" for that object and use DB4Os cascade-on-delete.
     * @param sm The state manager of the object to be deleted.
     * @throws NucleusDataStoreException when an error occurs in the datastore communication
     * @throws NucleusOptimisticException thrown if version checking fails on an optimistic transaction for this object
     */
    public void deleteObject(ObjectProvider sm)
    {
        // Check if read-only so update not permitted
        storeMgr.assertReadOnlyForUpdateOfObject(sm);

        boolean principalObjectForDelete = false;
        ArrayList list = (ArrayList)currentDeleteThreads.get(Thread.currentThread().getName());
        if (list == null)
        {
            // No delete running currently for this thread so register this one
            principalObjectForDelete = true;
            list = new ArrayList();
            list.add(sm.getObject());
            currentDeleteThreads.put(Thread.currentThread().getName(), list);
        }
        else
        {
            list.add(sm.getObject());
        }

        ManagedConnection mconn = storeMgr.getConnection(sm.getExecutionContext());
        try
        {
            ObjectContainer cont = (ObjectContainer)mconn.getConnection();
            VersionMetaData vermd = sm.getClassMetaData().getVersionMetaDataForClass();
            if (sm.getExecutionContext().getTransaction().getOptimistic() && vermd != null)
            {
                // Optimistic transaction so perform version check before any delete
                long datastoreVersion = cont.ext().getObjectInfo(sm.getObject()).getVersion();
                if (datastoreVersion > 0)
                {
                    VersionHelper.performVersionCheck(sm, Long.valueOf(datastoreVersion), vermd);
                }
            }

            // Load any unloaded fields so that DeleteFieldManager has all field values to work with
            sm.loadUnloadedFields();

            try
            {
                // Delete all reachable PC objects (due to dependent-field). Updates the StateManagers to be in deleted state
                sm.provideFields(sm.getClassMetaData().getAllMemberPositions(), new DeleteFieldManager(sm));
                if (principalObjectForDelete)
                {
                    // This delete is for the root object so just persist to DB4O and it will delete all dependent objects for us
                    long startTime = System.currentTimeMillis();
                    if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
                    {
                        NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("DB4O.Delete.Start", 
                            sm.toPrintableID(), sm.getInternalObjectId()));
                    }
                    cont.delete(sm.getObject());
                    if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
                    {
                        NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("DB4O.ExecutionTime", 
                            (System.currentTimeMillis() - startTime)));
                    }
                    if (storeMgr.getRuntimeManager() != null)
                    {
                        storeMgr.getRuntimeManager().incrementDeleteCount();
                    }

                    // Debug info about the deleted objects
                    if (NucleusLogger.DATASTORE.isDebugEnabled())
                    {
                        Iterator iter = list.iterator();
                        while (iter.hasNext())
                        {
                            Object obj = iter.next();
                            ObjectProvider objSM = sm.getExecutionContext().findObjectProvider(obj);
                            if (objSM != null)
                            {
                                NucleusLogger.DATASTORE.debug(LOCALISER.msg("DB4O.Delete.ObjectDeleted", 
                                    StringUtils.toJVMIDString(obj), objSM.getInternalObjectId()));
                            }
                        }
                    }
                }
            }
            finally
            {
                if (principalObjectForDelete)
                {
                    // the delete process is complete for this thread so remove from the Map
                    currentDeleteThreads.remove(Thread.currentThread().getName());
                }
            }
        }
        finally
        {
            mconn.release();
        }
    }

    /**
     * Fetches fields of a persistent object from the database.
     * In the context of DB4O this method activates the supplied object if it is currently inactive
     * and, in doing so, handles any fields that have been made dirty in the meantime. The requested
     * fields are activated, replaced by SCO wrappers (if SCO mutable) and any PC field values
     * have StateManagers connected.
     * @param sm The state manager of the object to be fetched.
     * @param fieldNumbers The numbers of the fields to be fetched.
     * @throws NucleusDataStoreException when an error occurs in the datastore communication
     */
    public void fetchObject(ObjectProvider sm, int fieldNumbers[])
    {
        AbstractClassMetaData cmd = sm.getClassMetaData();
        if (NucleusLogger.PERSISTENCE.isDebugEnabled())
        {
            // Debug information about what we are retrieving
            StringBuffer str = new StringBuffer("Fetching object \"");
            str.append(sm.toPrintableID()).append("\" (id=");
            str.append(sm.getExecutionContext().getApiAdapter().getObjectId(sm)).append(")").append(" fields [");
            for (int i=0;i 0)
                {
                    str.append(",");
                }
                str.append(cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]).getName());
            }
            str.append("]");
            NucleusLogger.PERSISTENCE.debug(str);
        }

        ManagedConnection mconn = storeMgr.getConnection(sm.getExecutionContext());
        try
        {
            final ObjectContainer cont = (ObjectContainer)mconn.getConnection();
            long startTime = System.currentTimeMillis();
            if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled())
            {
                NucleusLogger.DATASTORE_RETRIEVE.debug(LOCALISER.msg("DB4O.Fetch.Start", 
                    sm.toPrintableID(), sm.getInternalObjectId()));
            }

            if (!cont.ext().isActive(sm.getObject()))
            {
                // Object not active so we need to update fields with what DB4O has and preserve any dirty fields here
                Object obj = sm.getObject();
                int[] dirtyFieldNumbers = sm.getDirtyFieldNumbers();
                if (dirtyFieldNumbers != null && dirtyFieldNumbers.length > 0)
                {
                    // Dirty fields, so copy values to new object, activate DB4O's object and copy the dirty values back
                    Object copy = storeMgr.getApiAdapter().getCopyOfPersistableObject(obj, sm, dirtyFieldNumbers);
                    storeMgr.activateObject(cont, obj);
                    storeMgr.getApiAdapter().copyFieldsFromPersistableObject(copy, dirtyFieldNumbers, obj);
                }
                else
                {
                    // No dirty fields so just activate the object
                    storeMgr.activateObject(cont, obj);
                }
            }

            // Process all requested fields so they are managed (loaded) and active now
            sm.replaceFields(fieldNumbers, new ActivationFieldManager(cont, sm));

            VersionMetaData vermd = cmd.getVersionMetaDataForClass();
            if (vermd != null && vermd.getVersionStrategy() == VersionStrategy.VERSION_NUMBER)
            {
                // Object needs versioning so store its current datastore version in the StateManager
                long version = cont.ext().getObjectInfo(sm.getObject()).getVersion();
                sm.setTransactionalVersion(Long.valueOf(version));
            }

            if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled())
            {
                NucleusLogger.DATASTORE_RETRIEVE.debug(LOCALISER.msg("DB4O.ExecutionTime", 
                    (System.currentTimeMillis() - startTime)));
            }
            if (storeMgr.getRuntimeManager() != null)
            {
                storeMgr.getRuntimeManager().incrementFetchCount();
            }
        }
        finally
        {
            mconn.release();
        }
    }

    /**
     * Accessor for an (at least) hollow PersistenceCapable object matching the given id.
     * In this sense, the StoreManager may be seen as a kind of level-3-cache. But this methods servers
     * an important purpose: if the StoreManager is managing the in-memory object instantiation 
     * (as part of co-managing the object lifecycle in general), then the StoreManager has to create the object 
     * during this call (if it is not already created. Most relational databases leave the in-memory object 
     * instantion to Core, but some object databases may manage the in-memory object instantion.
     *
     * Implementations may simply return null, indicating that they leave the object instantiate to us.
     * Other implementations may instantiate the object in question (whether the implementation may trust that
     * the object is not already instantiated has still to be determined). If an implementation believes that 
     * an object with the given ID should exist, but in fact does not exist, then the implementation should 
     * throw a RuntimeException. It should not silently return null in this case.
     * @param ec the ObjectManager which will manage the object
     * @param id the id of the object in question.
     * @return a persistable object with a valid state (for example: hollow) or null, indicating that the 
     *     implementation leaves the instantiation work to us.
     */
    public Object findObject(ExecutionContext ec, Object id)
    {
        Object pc = null;

        ManagedConnection mconn = storeMgr.getConnection(ec);
        try
        {
            ObjectContainer cont = (ObjectContainer)mconn.getConnection();
            if (id instanceof DatastoreUniqueOID)
            {
                // Datastore identity, so grab direct from DB4O
                long idNumber = ((DatastoreUniqueOID)id).getKey();
                pc = cont.ext().getByID(idNumber);
                if (ec.findObjectProvider(pc) == null)
                {
                    ObjectProviderFactory.newForHollowPreConstructed(ec, id, pc);
                }
            }
            else
            {
                ClassLoaderResolver clr = ec.getClassLoaderResolver();
                String className = storeMgr.getClassNameForObjectID(id, clr, ec);
                if (className == null)
                {
                    throw new NucleusObjectNotFoundException(LOCALISER.msg("DB4O.Object.IdNotFound", id));
                }
                
                AbstractClassMetaData cmd = ec.getMetaDataManager().getMetaDataForClass(className, clr);
                if (cmd.getIdentityType() == IdentityType.APPLICATION)
                {
                    // Generate a template object with these PK field values
                    Class pcClass = clr.classForName(className, (id instanceof OID) ? null : id.getClass().getClassLoader());
                    ObjectProvider sm = ObjectProviderFactory.newForHollow(ec, pcClass, id);
                    pc = sm.getObject();

                    // Use this template object to get the real object with these key values from DB4O
                    ObjectSet objectSet = cont.queryByExample(pc);
                    if (objectSet.size() == 0)
                    {
                        throw new NucleusObjectNotFoundException(LOCALISER.msg("DB4O.Object.IdNotFound", id));
                    }

                    pc = objectSet.next();
                    ObjectProvider pcSM = ec.findObjectProvider(pc);
                    if (pcSM == null)
                    {
                        // DB4O returned object has no StateManager so swap the generated SM to manage its object
                        sm.replaceManagedPC(pc);
                    }
                    // TODO Error checking on whether there are more than 1 returned etc (should be impossible)
                }
                else
                {
                    // Nondurable identity
                }
            }

            if (NucleusLogger.PERSISTENCE.isDebugEnabled())
            {
                NucleusLogger.PERSISTENCE.debug(LOCALISER.msg("DB4O.FindObject", id, StringUtils.toJVMIDString(pc)));
            }
        }
        finally
        {
            mconn.release();
        }
        return pc;
    }

    /**
     * Locates this object in the datastore.
     * @param sm The StateManager for the object to be found 
     * @throws NucleusObjectNotFoundException if the object doesnt exist
     * @throws NucleusDataStoreException when an error occurs in the datastore communication
     */
    public void locateObject(ObjectProvider sm)
    {
        ExecutionContext ec = sm.getExecutionContext();
        ClassLoaderResolver clr = ec.getClassLoaderResolver();
        AbstractClassMetaData cmd = sm.getClassMetaData();

        ManagedConnection mconn = storeMgr.getConnection(ec);
        try
        {
            ObjectContainer cont = (ObjectContainer)mconn.getConnection();
            long startTime = System.currentTimeMillis();
            if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled())
            {
                NucleusLogger.DATASTORE_RETRIEVE.debug(LOCALISER.msg("DB4O.Fetch.Start", 
                    sm.toPrintableID(), sm.getInternalObjectId()));
            }

            PersistenceConfiguration config = ec.getNucleusContext().getPersistenceConfiguration();
            boolean usedPersistenceUnit = config.getStringProperty("datanucleus.PersistenceUnitName") != null;

            // TODO This is to comment out changes for NUCDBFO-37. Remove when that is complete
            usedPersistenceUnit = false;

            boolean isStored = false;
            if (cmd.getIdentityType() == IdentityType.DATASTORE)
            {
                // Just find if this object is stored
                isStored = cont.ext().isStored(sm.getObject());
            }
            else if (cmd.getIdentityType() == IdentityType.APPLICATION)
            {
                if (usedPersistenceUnit)
                {
                    // If a "persistence-unit" was used, all the metadata was loaded at startup and 
                    // a db4o UniqueFieldValueConstraint was already set: that means, here we don't need 
                    // a check for uniqueness of identity. That will significantly speed the performance.
                    // See Issue NUCDBFO-37 for further details.
                    isStored = cont.ext().isStored(sm.getObject());
                }
                else
                {
                    // Generate a reference object with these PK field values
                    Object id = sm.getInternalObjectId();
                    Class pcClass = clr.classForName(cmd.getFullClassName(), id.getClass().getClassLoader());
                    ObjectProvider referenceSM = ObjectProviderFactory.newForHollow(ec, pcClass, id);
                    Object referenceObject = referenceSM.getObject();

                    // Use QBE to get any object with these key values from DB4O
                    ObjectSet objectSet = cont.queryByExample(referenceObject);
                    if (objectSet.hasNext())
                    {
                        isStored = true;
                    }
                }
            }
            else
            {
                // Nondurable identity
            }

            if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled())
            {
                NucleusLogger.DATASTORE_RETRIEVE.debug(LOCALISER.msg("DB4O.ExecutionTime", 
                    (System.currentTimeMillis() - startTime)));
            }

            if (!isStored)
            {
                throw new NucleusObjectNotFoundException(LOCALISER.msg("DB4O.Object.NotFound", 
                    sm.toPrintableID(), sm.getInternalObjectId()));
            }
        }
        finally
        {
            mconn.release();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy