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

org.datanucleus.store.mongodb.MongoDBPersistenceHandler Maven / Gradle / Ivy

There is a newer version: 6.0.0-release
Show newest version
/**********************************************************************
Copyright (c) 2011 Andy Jefferson. 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.mongodb;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;

import org.bson.types.ObjectId;
import org.datanucleus.PropertyNames;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.exceptions.NucleusObjectNotFoundException;
import org.datanucleus.exceptions.NucleusOptimisticException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.identity.OID;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.DiscriminatorMetaData;
import org.datanucleus.metadata.DiscriminatorStrategy;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.VersionMetaData;
import org.datanucleus.metadata.VersionStrategy;
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.fieldmanager.DeleteFieldManager;
import org.datanucleus.store.mongodb.fieldmanager.FetchFieldManager;
import org.datanucleus.store.mongodb.fieldmanager.StoreFieldManager;
import org.datanucleus.store.schema.naming.ColumnType;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

public class MongoDBPersistenceHandler extends AbstractPersistenceHandler
{
    protected static final Localiser LOCALISER = Localiser.getInstance(
            "org.datanucleus.store.mongodb.Localisation", MongoDBStoreManager.class.getClassLoader());

    protected final MongoDBStoreManager storeMgr;

    public MongoDBPersistenceHandler(StoreManager storeMgr)
    {
        this.storeMgr = (MongoDBStoreManager) storeMgr;
    }

    public void close()
    {
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.AbstractPersistenceHandler#insertObjects(org.datanucleus.store.ObjectProvider[])
     */
    @Override
    public void insertObjects(ObjectProvider... ops)
    {
        // This is called by the flush() process, so groups inserts.
        List insertOps = new ArrayList();
        for (int i=0;i> opsByTable = new HashMap();
        for (int i=0;i opsForTable = opsByTable.get(tableName);
                if (opsForTable == null)
                {
                    opsForTable = new HashSet();
                    opsByTable.put(tableName, opsForTable);
                }
                opsForTable.add(ops[i]);
            }
        }

        for (String tableName : opsByTable.keySet())
        {
            Set opsForTable = opsByTable.get(tableName);
            ExecutionContext ec = ops[0].getExecutionContext();
            ManagedConnection mconn = storeMgr.getConnection(ec);
            try
            {
                DB db = (DB)mconn.getConnection();
                long startTime = System.currentTimeMillis();

                DBCollection collection = db.getCollection(tableName);
                DBObject[] dbObjects = new DBObject[opsForTable.size()];
                int i=0;
                for (ObjectProvider op : opsForTable)
                {
                    storeMgr.assertReadOnlyForUpdateOfObject(op);
                    AbstractClassMetaData cmd = op.getClassMetaData();
                    if (!storeMgr.managesClass(cmd.getFullClassName()))
                    {
                        storeMgr.addClass(cmd.getFullClassName(), op.getExecutionContext().getClassLoaderResolver());
                    }

                    if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
                    {
                        NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("MongoDB.Insert.Start", 
                            op.toPrintableID(), op.getInternalObjectId()));
                    }

                    dbObjects[i] = getDBObjectForObjectProviderToInsert(op, true);

                    if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
                    {
                        NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("MongoDB.Insert.ObjectPersisted",
                            op.toPrintableID(), op.getInternalObjectId()));
                    }

                    i++;
                }

                if (NucleusLogger.DATASTORE_NATIVE.isDebugEnabled())
                {
                    NucleusLogger.DATASTORE_NATIVE.debug("Persisting objects as " + StringUtils.objectArrayToString(dbObjects));
                }
                collection.insert(dbObjects, new WriteConcern(1));
                if (ec.getStatistics() != null)
                {
                    ec.getStatistics().incrementNumWrites();
                    for (int j=0;j 0)
                {
                    op.provideFields(fieldNumbers, fieldManager);
                    NucleusLogger.DATASTORE_NATIVE.debug("Saving object " + op + " as " + dbObject);
                    collection.save(dbObject);
                    if (ec.getStatistics() != null)
                    {
                        ec.getStatistics().incrementNumWrites();
                    }
                }
            }
            else
            {
                if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
                {
                    NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("MongoDB.Insert.ObjectPersisted",
                        op.toPrintableID(), op.getInternalObjectId()));
                }
            }

            if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
            {
                NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("MongoDB.ExecutionTime", 
                    (System.currentTimeMillis() - startTime)));
            }
            if (ec.getStatistics() != null)
            {
                ec.getStatistics().incrementInsertCount();
            }
        }
        catch (MongoException me)
        {
            NucleusLogger.PERSISTENCE.error("Exception inserting object " + op, me);
            throw new NucleusDataStoreException("Exception inserting object for " + op, me);
        }
        finally
        {
            mconn.release();
        }
    }

    /**
     * Convenience method to populate the DBObject for the object managed by the ObjectProvider.
     * @param op ObjectProvider
     * @return The DBObject to persist
     */
    private DBObject getDBObjectForObjectProviderToInsert(ObjectProvider op, boolean includeRelationFields)
    {
        DBObject dbObject = new BasicDBObject();
        AbstractClassMetaData cmd = op.getClassMetaData();

        if (cmd.getIdentityType() == IdentityType.DATASTORE && !storeMgr.isStrategyDatastoreAttributed(cmd, -1))
        {
            // Add surrogate datastore identity field (if using identity then just uses "_id" MongoDB special)
            String fieldName = storeMgr.getNamingFactory().getColumnName(cmd, ColumnType.DATASTOREID_COLUMN);
            OID oid = (OID) op.getInternalObjectId();
            Object key = oid.getKeyValue();
            dbObject.put(fieldName, key);
        }

        if (cmd.hasDiscriminatorStrategy())
        {
            // Add discriminator field
            DiscriminatorMetaData discmd = cmd.getDiscriminatorMetaData();
            String fieldName = storeMgr.getNamingFactory().getColumnName(cmd, ColumnType.DISCRIMINATOR_COLUMN);
            Object discVal = null;
            if (cmd.getDiscriminatorStrategy() == DiscriminatorStrategy.CLASS_NAME)
            {
                discVal = cmd.getFullClassName();
            }
            else
            {
                discVal = discmd.getValue();
            }
            dbObject.put(fieldName, discVal);
        }

        // Add Multi-tenancy discriminator if applicable
        if (storeMgr.getStringProperty(PropertyNames.PROPERTY_TENANT_ID) != null)
        {
            if ("true".equalsIgnoreCase(cmd.getValueForExtension("multitenancy-disable")))
            {
                // Don't bother with multitenancy for this class
            }
            else
            {
                String fieldName = storeMgr.getNamingFactory().getColumnName(cmd, ColumnType.MULTITENANCY_COLUMN);
                dbObject.put(fieldName, storeMgr.getStringProperty(PropertyNames.PROPERTY_TENANT_ID));
            }
        }

        if (cmd.isVersioned())
        {
            VersionMetaData vermd = cmd.getVersionMetaDataForClass();
            if (vermd.getVersionStrategy() == VersionStrategy.VERSION_NUMBER)
            {
                long versionNumber = 1;
                op.setTransactionalVersion(Long.valueOf(versionNumber));
                if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
                {
                    NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("MongoDB.Insert.ObjectPersistedWithVersion",
                        op.toPrintableID(), op.getInternalObjectId(), "" + versionNumber));
                }

                if (vermd.getFieldName() != null)
                {
                    // Version stored in a field
                    AbstractMemberMetaData verMmd = cmd.getMetaDataForMember(vermd.getFieldName());
                    Object verFieldValue = Long.valueOf(versionNumber);
                    if (verMmd.getType() == int.class || verMmd.getType() == Integer.class)
                    {
                        verFieldValue = Integer.valueOf((int)versionNumber);
                    }
                    op.replaceField(verMmd.getAbsoluteFieldNumber(), verFieldValue);
                }
                else
                {
                    // Surrogate version
                    String fieldName = storeMgr.getNamingFactory().getColumnName(cmd, ColumnType.VERSION_COLUMN);
                    dbObject.put(fieldName, new Long(versionNumber));
                }
            }
            else if (vermd.getVersionStrategy() == VersionStrategy.DATE_TIME)
            {
                Date date = new Date();
                Timestamp ts = new Timestamp(date.getTime());
                op.setTransactionalVersion(ts);

                if (vermd.getFieldName() != null)
                {
                    // Version field
                    AbstractMemberMetaData verMmd = cmd.getMetaDataForMember(vermd.getFieldName());
                    op.replaceField(verMmd.getAbsoluteFieldNumber(), ts);
                }
                else
                {
                    // Surrogate version
                    String fieldName = storeMgr.getNamingFactory().getColumnName(cmd, ColumnType.VERSION_COLUMN);
                    dbObject.put(fieldName, ts);
                }
            }
        }

        StoreFieldManager fieldManager = new StoreFieldManager(op, dbObject, true);
        int[] fieldNumbers = cmd.getAllMemberPositions();
        if (!includeRelationFields)
        {
            ExecutionContext ec = op.getExecutionContext();
            fieldNumbers = cmd.getNonRelationMemberPositions(ec.getClassLoaderResolver(), ec.getMetaDataManager());
        }
        op.provideFields(fieldNumbers, fieldManager);

        return dbObject;
    }

    public void updateObject(ObjectProvider op, int[] fieldNumbers)
    {
        storeMgr.assertReadOnlyForUpdateOfObject(op);

        ExecutionContext ec = op.getExecutionContext();
        ManagedConnection mconn = storeMgr.getConnection(ec);
        try
        {
            DB db = (DB)mconn.getConnection();

            long startTime = System.currentTimeMillis();
            AbstractClassMetaData cmd = op.getClassMetaData();
            if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled())
            {
                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("MongoDB.Update.Start", 
                    op.toPrintableID(), op.getInternalObjectId(), fieldStr.toString()));
            }

            DBCollection collection = db.getCollection(storeMgr.getNamingFactory().getTableName(cmd));
            DBObject dbObject = MongoDBUtils.getObjectForObjectProvider(collection, op, true, true);
            if (dbObject == null)
            {
                if (cmd.isVersioned())
                {
                    throw new NucleusOptimisticException("Object with id " + op.getObjectId() + 
                        " and version " + op.getTransactionalVersion() + " no longer present");
                }
                else
                {
                    throw new NucleusDataStoreException("Could not find object with id " + op.getObjectId());
                }
            }

            int[] updatedFieldNums = fieldNumbers;
            if (cmd.isVersioned())
            {
                // Version object so calculate version to store with
                Object currentVersion = op.getTransactionalVersion();
                VersionMetaData vermd = cmd.getVersionMetaDataForClass();
                Object nextVersion = VersionHelper.getNextVersion(vermd.getVersionStrategy(), currentVersion);
                op.setTransactionalVersion(nextVersion);

                if (vermd.getFieldName() != null)
                {
                    // Update the version field value
                    AbstractMemberMetaData verMmd = cmd.getMetaDataForMember(vermd.getFieldName());
                    op.replaceField(verMmd.getAbsoluteFieldNumber(), nextVersion);

                    boolean updatingVerField = false;
                    for (int i=0;i 0)
                    {
                        str.append(",");
                    }
                    str.append(cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]).getName());
                }
                str.append("]");
                NucleusLogger.DATASTORE_RETRIEVE.debug(str);
            }

            long startTime = System.currentTimeMillis();
            if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled())
            {
                NucleusLogger.DATASTORE_RETRIEVE.debug(LOCALISER.msg("MongoDB.Fetch.Start", 
                    op.toPrintableID(), op.getInternalObjectId()));
            }

            DBCollection collection = db.getCollection(storeMgr.getNamingFactory().getTableName(cmd));
            DBObject dbObject = MongoDBUtils.getObjectForObjectProvider(collection, op, false, false);
            if (dbObject == null)
            {
                throw new NucleusObjectNotFoundException("Could not find object with id " + op.getInternalObjectId() + " op="+op);
            }

            if (cmd.isVersioned() && op.getTransactionalVersion() == null)
            {
                // No version set, so retrieve it
                VersionMetaData vermd = cmd.getVersionMetaDataForClass();
                if (vermd.getFieldName() != null)
                {
                    // Version stored in a field
                    Object datastoreVersion =
                        op.provideField(cmd.getAbsolutePositionOfMember(vermd.getFieldName()));
                    op.setVersion(datastoreVersion);
                }
                else
                {
                    // Surrogate version
                    String fieldName = storeMgr.getNamingFactory().getColumnName(cmd, ColumnType.VERSION_COLUMN);
                    Object datastoreVersion = dbObject.get(fieldName);
                    op.setVersion(datastoreVersion);
                }
            }

            FetchFieldManager fieldManager = new FetchFieldManager(op, dbObject, cmd);
            op.replaceFields(fieldNumbers, fieldManager);

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

    public Object findObject(ExecutionContext om, Object id)
    {
        return null;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.AbstractPersistenceHandler#locateObjects(org.datanucleus.store.ObjectProvider[])
     */
    @Override
    public void locateObjects(ObjectProvider[] ops)
    {
        // TODO Implement bulk location of objects. Split into DBCollections, then do bulk get
        super.locateObjects(ops);
    }

    public void locateObject(ObjectProvider op)
    {
        final AbstractClassMetaData cmd = op.getClassMetaData();
        if (cmd.getIdentityType() == IdentityType.APPLICATION || 
            cmd.getIdentityType() == IdentityType.DATASTORE)
        {
            ExecutionContext ec = op.getExecutionContext();
            ManagedConnection mconn = storeMgr.getConnection(ec);
            try
            {
                DB db = (DB)mconn.getConnection();
                DBCollection collection = db.getCollection(storeMgr.getNamingFactory().getTableName(cmd));
                DBObject dbObject = MongoDBUtils.getObjectForObjectProvider(collection, op, false, false);
                if (dbObject == null)
                {
                    throw new NucleusObjectNotFoundException();
                }
            }
            finally
            {
                mconn.release();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy