org.datanucleus.store.mongodb.MongoDBPersistenceHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-mongodb Show documentation
Show all versions of datanucleus-mongodb Show documentation
Plugin providing persistence to MongoDB datastores.
/**********************************************************************
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();
}
}
}
}