Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.code.morphia.DatastoreImpl Maven / Gradle / Ivy
package com.google.code.morphia;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.google.code.morphia.annotations.CappedAt;
import com.google.code.morphia.annotations.Entity;
import com.google.code.morphia.annotations.Index;
import com.google.code.morphia.annotations.Indexed;
import com.google.code.morphia.annotations.Indexes;
import com.google.code.morphia.annotations.NotSaved;
import com.google.code.morphia.annotations.PostPersist;
import com.google.code.morphia.annotations.Reference;
import com.google.code.morphia.annotations.Serialized;
import com.google.code.morphia.annotations.Version;
import com.google.code.morphia.logging.Logr;
import com.google.code.morphia.logging.MorphiaLoggerFactory;
import com.google.code.morphia.mapping.MappedClass;
import com.google.code.morphia.mapping.MappedField;
import com.google.code.morphia.mapping.Mapper;
import com.google.code.morphia.mapping.MappingException;
import com.google.code.morphia.mapping.cache.EntityCache;
import com.google.code.morphia.mapping.lazy.DatastoreHolder;
import com.google.code.morphia.mapping.lazy.proxy.ProxyHelper;
import com.google.code.morphia.query.Query;
import com.google.code.morphia.query.QueryException;
import com.google.code.morphia.query.QueryImpl;
import com.google.code.morphia.query.UpdateException;
import com.google.code.morphia.query.UpdateOperations;
import com.google.code.morphia.query.UpdateOpsImpl;
import com.google.code.morphia.query.UpdateResults;
import com.google.code.morphia.utils.Assert;
import com.google.code.morphia.utils.IndexDirection;
import com.google.code.morphia.utils.IndexFieldDef;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBDecoderFactory;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import com.mongodb.MapReduceCommand;
import com.mongodb.MapReduceCommand.OutputType;
import com.mongodb.MapReduceOutput;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;
/**
* A generic (type-safe) wrapper around mongodb collections
*
* @author Scott Hernandez
*/
@SuppressWarnings({"unchecked", "deprecation"})
public class DatastoreImpl implements AdvancedDatastore {
private static final Logr LOG = MorphiaLoggerFactory.get(DatastoreImpl.class);
protected final Mapper mapper;
protected final Mongo mongo;
protected final DB db;
protected WriteConcern defConcern = WriteConcern.SAFE;
protected DBDecoderFactory decoderFactory;
public DatastoreImpl(final Mapper mapper, final Mongo mongo, final String dbName) {
this.mapper = mapper;
this.mongo = mongo;
db = mongo.getDB(dbName);
// VERY discussable
DatastoreHolder.getInstance().set(this);
}
public DatastoreImpl(final Morphia morphia, final Mongo mongo) {
this(morphia, mongo, null);
}
public DatastoreImpl(final Morphia morphia, final Mongo mongo, final String dbName, final String username, final char[] password) {
this(morphia.getMapper(), mongo, dbName);
if (username != null) {
if (!db.authenticate(username, password)) {
throw new AuthenticationException(
"User '" + username + "' cannot be authenticated with the given password for database '" + dbName + "'");
}
}
}
public DatastoreImpl(final Morphia morphia, final Mongo mongo, final String dbName) {
this(morphia.getMapper(), mongo, dbName);
}
public DatastoreImpl copy(final String database) {
return new DatastoreImpl(mapper, mongo, database);
}
public DBRef createRef(final Class clazz, final V id) {
if (id == null) {
throw new MappingException("Could not get id for " + clazz.getName());
}
return new DBRef(getDB(), getCollection(clazz).getName(), id);
}
public DBRef createRef(final T entity) {
final T wrapped = ProxyHelper.unwrap(entity);
final Object id = getId(wrapped);
if (id == null) {
throw new MappingException("Could not get id for " + wrapped.getClass().getName());
}
return createRef(wrapped.getClass(), id);
}
@Deprecated
protected Object getId(final Object entity) {
return mapper.getId(entity);
}
@Deprecated // use mapper instead.
public Key getKey(final T entity) {
return mapper.getKey(entity);
}
public WriteResult delete(final String kind, final T id) {
final DBCollection dbColl = getCollection(kind);
final WriteResult wr = dbColl.remove(BasicDBObjectBuilder.start().add(Mapper.ID_KEY, id).get());
throwOnError(null, wr);
return wr;
}
public WriteResult delete(final String kind, final Class clazz, final V id) {
return delete(find(kind, clazz).filter(Mapper.ID_KEY, id));
}
public WriteResult delete(final Class clazz, final V id, final WriteConcern wc) {
return delete(createQuery(clazz).filter(Mapper.ID_KEY, id), wc);
}
public WriteResult delete(final Class clazz, final V id) {
return delete(clazz, id, getWriteConcern(clazz));
}
public WriteResult delete(final Class clazz, final Iterable ids) {
final Query q = find(clazz).disableValidation().filter(Mapper.ID_KEY + " in", ids);
return delete(q);
}
public WriteResult delete(final T entity) {
return delete(entity, getWriteConcern(entity));
}
public WriteResult delete(final T entity, final WriteConcern wc) {
final T wrapped = ProxyHelper.unwrap(entity);
if (wrapped instanceof Class>) {
throw new MappingException("Did you mean to delete all documents? -- delete(ds.createQuery(???.class))");
}
try {
final Object id = getId(wrapped);
return delete(wrapped.getClass(), id, wc);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public WriteResult delete(final Query query) {
return delete(query, getWriteConcern(query.getEntityClass()));
}
public WriteResult delete(final Query query, final WriteConcern wc) {
final QueryImpl q = (QueryImpl) query;
DBCollection dbColl = q.getCollection();
//TODO remove this after testing.
if (dbColl == null) {
dbColl = getCollection(q.getEntityClass());
}
final WriteResult wr;
if (q.getSortObject() != null || q.getOffset() != 0 || q.getLimit() > 0) {
throw new QueryException("Delete does not allow sort/offset/limit query options.");
}
if (q.getQueryObject() != null) {
if (wc == null) {
wr = dbColl.remove(q.getQueryObject());
} else {
wr = dbColl.remove(q.getQueryObject(), wc);
}
} else if (wc == null) {
wr = dbColl.remove(new BasicDBObject());
} else {
wr = dbColl.remove(new BasicDBObject(), wc);
}
throwOnError(wc, wr);
return wr;
}
public void ensureIndex(final Class type, final String fields) {
ensureIndex(type, null, fields, false, false);
}
public void ensureIndex(final Class clazz, final String name, final IndexFieldDef[] definitions, final boolean unique,
final boolean dropDupsOnCreate) {
ensureIndex(clazz, name, definitions, unique, dropDupsOnCreate, false);
}
public void ensureIndex(final Class clazz, final String name, final String fields, final boolean unique,
final boolean dropDupsOnCreate) {
ensureIndex(clazz, name, QueryImpl.parseFieldsString(fields, clazz, mapper, true), unique, dropDupsOnCreate, false, false);
}
public void ensureIndex(final Class clazz, final String name, final String fields, final boolean unique,
final boolean dropDupsOnCreate, final boolean background) {
ensureIndex(clazz, name, QueryImpl.parseFieldsString(fields, clazz, mapper, true), unique, dropDupsOnCreate, background, false);
}
protected void ensureIndex(final Class clazz, final String name, final BasicDBObject fields, final boolean unique,
final boolean dropDupsOnCreate, final boolean background, final boolean sparse) {
final BasicDBObjectBuilder keyOpts = new BasicDBObjectBuilder();
if (name != null && name.length() != 0) {
keyOpts.add("name", name);
}
if (unique) {
keyOpts.add("unique", true);
if (dropDupsOnCreate) {
keyOpts.add("dropDups", true);
}
}
if (background) {
keyOpts.add("background", true);
}
if (sparse) {
keyOpts.add("sparse", true);
}
final DBCollection dbColl = getCollection(clazz);
final BasicDBObject opts = (BasicDBObject) keyOpts.get();
if (opts.isEmpty()) {
LOG.debug("Ensuring index for " + dbColl.getName() + " with keys:" + fields);
dbColl.ensureIndex(fields);
} else {
LOG.debug("Ensuring index for " + dbColl.getName() + " with keys:" + fields + " and opts:" + opts);
dbColl.ensureIndex(fields, opts);
}
}
@SuppressWarnings({"rawtypes"})
public void ensureIndex(final Class clazz, final String name, final IndexFieldDef[] definitions, final boolean unique,
final boolean dropDupsOnCreate, final boolean background) {
final BasicDBObjectBuilder keys = BasicDBObjectBuilder.start();
for (final IndexFieldDef def : definitions) {
final String fieldName = def.getField();
final IndexDirection dir = def.getDirection();
keys.add(fieldName, dir.toIndexValue());
}
ensureIndex(clazz, name, (BasicDBObject) keys.get(), unique, dropDupsOnCreate, background, false);
}
public void ensureIndex(final Class type, final String name, final IndexDirection dir) {
ensureIndex(type, new IndexFieldDef(name, dir));
}
public void ensureIndex(final Class type, final IndexFieldDef... fields) {
ensureIndex(type, null, fields, false, false);
}
public void ensureIndex(final Class type, final boolean background, final IndexFieldDef... fields) {
ensureIndex(type, null, fields, false, false, background);
}
protected void ensureIndexes(final MappedClass mc, final boolean background) {
ensureIndexes(mc, background, new ArrayList(), new ArrayList());
}
protected void ensureIndexes(final MappedClass mc, final boolean background, final ArrayList parentMCs,
final ArrayList parentMFs) {
if (parentMCs.contains(mc)) {
return;
}
//skip embedded types
if (mc.getEmbeddedAnnotation() != null && (parentMCs == null || parentMCs.isEmpty())) {
return;
}
//Ensure indexes from class annotation
final ArrayList indexes = mc.getAnnotations(Indexes.class);
if (indexes != null) {
for (final Annotation ann : indexes) {
final Indexes idx = (Indexes) ann;
if (idx != null && idx.value() != null && idx.value().length > 0) {
for (final Index index : idx.value()) {
final BasicDBObject fields = QueryImpl.parseFieldsString(index.value(), mc.getClazz(), mapper, !index.disableValidation());
ensureIndex(mc.getClazz(), index.name(), fields, index.unique(), index.dropDups(),
index.background() ? index.background() : background, index.sparse());
}
}
}
}
//Ensure indexes from field annotations, and embedded entities
for (final MappedField mf : mc.getPersistenceFields()) {
if (mf.hasAnnotation(Indexed.class)) {
final Indexed index = mf.getAnnotation(Indexed.class);
final StringBuilder field = new StringBuilder();
final Class> indexedClass = (parentMCs.isEmpty() ? mc : parentMCs.get(0)).getClazz();
if (!parentMCs.isEmpty()) {
for (final MappedField pmf : parentMFs) {
field.append(pmf.getNameToStore()).append(".");
}
}
field.append(mf.getNameToStore());
ensureIndex(indexedClass, index.name(), new BasicDBObject(field.toString(), index.value().toIndexValue()), index.unique(),
index.dropDups(), index.background() ? index.background() : background, index.sparse());
}
if (!mf.isTypeMongoCompatible() && !mf.hasAnnotation(Reference.class) && !mf.hasAnnotation(Serialized.class)) {
final ArrayList newParentClasses = (ArrayList) parentMCs.clone();
final ArrayList newParents = (ArrayList) parentMFs.clone();
newParentClasses.add(mc);
newParents.add(mf);
ensureIndexes(mapper.getMappedClass(mf.isSingleValue() ? mf.getType() : mf.getSubClass()), background, newParentClasses, newParents);
}
}
}
public void ensureIndexes(final Class clazz) {
ensureIndexes(clazz, false);
}
public void ensureIndexes(final Class clazz, final boolean background) {
final MappedClass mc = mapper.getMappedClass(clazz);
ensureIndexes(mc, background);
}
public void ensureIndexes() {
ensureIndexes(false);
}
public void ensureIndexes(final boolean background) {
// loops over mappedClasses and call ensureIndex for each @Entity object
// (for now)
for (final MappedClass mc : mapper.getMappedClasses()) {
ensureIndexes(mc, background);
}
}
public void ensureCaps() {
for (final MappedClass mc : mapper.getMappedClasses()) {
if (mc.getEntityAnnotation() != null && mc.getEntityAnnotation().cap().value() > 0) {
final CappedAt cap = mc.getEntityAnnotation().cap();
final String collName = mapper.getCollectionName(mc.getClazz());
final BasicDBObjectBuilder dbCapOpts = BasicDBObjectBuilder.start("capped", true);
if (cap.value() > 0) {
dbCapOpts.add("size", cap.value());
}
if (cap.count() > 0) {
dbCapOpts.add("max", cap.count());
}
final DB database = getDB();
if (database.getCollectionNames().contains(collName)) {
final DBObject dbResult = database.command(BasicDBObjectBuilder.start("collstats", collName).get());
if (dbResult.containsField("capped")) {
// TODO: check the cap options.
LOG.warning("DBCollection already exists is capped already; doing nothing. " + dbResult);
} else {
LOG.warning("DBCollection already exists with same name(" + collName + ") and is not capped; not creating capped version!");
}
} else {
getDB().createCollection(collName, dbCapOpts.get());
LOG.debug("Created capped DBCollection (" + collName + ") with opts " + dbCapOpts);
}
}
}
}
public Query queryByExample(final T ex) {
return queryByExample(getCollection(ex), ex);
}
public Query queryByExample(final String kind, final T ex) {
return queryByExample(db.getCollection(kind), ex);
}
private Query queryByExample(final DBCollection coll, final T example) {
//TODO: think about remove className from baseQuery param below.
return new QueryImpl((Class) example.getClass(), coll, this, entityToDBObj(example, new HashMap()));
}
public Query createQuery(final Class clazz) {
return new QueryImpl(clazz, getCollection(clazz), this);
}
public Query createQuery(final Class kind, final DBObject q) {
return new QueryImpl(kind, getCollection(kind), this, q);
}
public Query createQuery(final String kind, final Class clazz, final DBObject q) {
return new QueryImpl(clazz, db.getCollection(kind), this, q);
}
public Query createQuery(final String kind, final Class clazz) {
return new QueryImpl(clazz, db.getCollection(kind), this);
}
public Query find(final String kind, final Class clazz) {
return new QueryImpl(clazz, getCollection(kind), this);
}
public Query find(final Class clazz) {
return createQuery(clazz);
}
public Query find(final Class clazz, final String property, final V value) {
final Query query = createQuery(clazz);
return query.filter(property, value);
}
public Query find(final String kind, final Class clazz, final String property, final V value, final int offset,
final int size) {
return find(kind, clazz, property, value, offset, size, true);
}
public Query find(final String kind, final Class clazz, final String property, final V value, final int offset,
final int size, final boolean validate) {
final Query query = find(kind, clazz);
if (!validate) {
query.disableValidation();
}
query.offset(offset);
query.limit(size);
return query.filter(property, value).enableValidation();
}
public Query find(final Class clazz, final String property, final V value, final int offset, final int size) {
final Query query = createQuery(clazz);
query.offset(offset);
query.limit(size);
return query.filter(property, value);
}
public T get(final Class clazz, final DBRef ref) {
return (T) mapper.fromDBObject(clazz, ref.fetch(), createCache());
}
public Query get(final Class clazz, final Iterable ids) {
return find(clazz).disableValidation().filter(Mapper.ID_KEY + " in", ids).enableValidation();
}
/**
* Queries the server to check for each DBRef
*/
public List> getKeysByRefs(final List refs) {
final ArrayList> tempKeys = new ArrayList>(refs.size());
final Map> kindMap = new HashMap>();
for (final DBRef ref : refs) {
if (kindMap.containsKey(ref.getRef())) {
kindMap.get(ref.getRef()).add(ref);
} else {
kindMap.put(ref.getRef(), new ArrayList(Collections.singletonList(ref)));
}
}
for (final String kind : kindMap.keySet()) {
final List objIds = new ArrayList();
final List kindRefs = kindMap.get(kind);
for (final DBRef key : kindRefs) {
objIds.add(key.getId());
}
final List> kindResults = this.find(kind, null).disableValidation().filter("_id in", objIds).asKeyList();
tempKeys.addAll(kindResults);
}
//put them back in order, minus the missing ones.
final ArrayList> keys = new ArrayList>(refs.size());
for (final DBRef ref : refs) {
final Key testKey = mapper.refToKey(ref);
if (tempKeys.contains(testKey)) {
keys.add(testKey);
}
}
return keys;
}
public List getByKeys(final Iterable> keys) {
return getByKeys(null, keys);
}
@SuppressWarnings("rawtypes")
public List getByKeys(final Class clazz, final Iterable> keys) {
final Map> kindMap = new HashMap>();
final List entities = new ArrayList();
// String clazzKind = (clazz==null) ? null :
// getMapper().getCollectionName(clazz);
for (final Key> key : keys) {
mapper.updateKind(key);
// if (clazzKind != null && !key.getKind().equals(clazzKind))
// throw new IllegalArgumentException("Types are not equal (" +
// clazz + "!=" + key.getKindClass() +
// ") for key and method parameter clazz");
//
if (kindMap.containsKey(key.getKind())) {
kindMap.get(key.getKind()).add(key);
} else {
kindMap.put(key.getKind(), new ArrayList(Collections.singletonList((Key) key)));
}
}
for (final String kind : kindMap.keySet()) {
final List objIds = new ArrayList();
final List kindKeys = kindMap.get(kind);
for (final Key key : kindKeys) {
objIds.add(key.getId());
}
final List kindResults = find(kind, null).disableValidation().filter("_id in", objIds).asList();
entities.addAll(kindResults);
}
//TODO: order them based on the incoming Keys.
return entities;
}
public T get(final String kind, final Class clazz, final V id) {
final List results = find(kind, clazz, Mapper.ID_KEY, id, 0, 1).asList();
if (results == null || results.isEmpty()) {
return null;
}
return results.get(0);
}
public T get(final Class clazz, final V id) {
return find(getCollection(clazz).getName(), clazz, Mapper.ID_KEY, id, 0, 1, true).get();
}
public T getByKey(final Class clazz, final Key key) {
final String kind = mapper.getCollectionName(clazz);
final String keyKind = mapper.updateKind(key);
if (!kind.equals(keyKind)) {
throw new RuntimeException("collection names don't match for key and class: " + kind + " != " + keyKind);
}
return get(clazz, key.getId());
}
public T get(final T entity) {
final T unwrapped = ProxyHelper.unwrap(entity);
final Object id = getId(unwrapped);
if (id == null) {
throw new MappingException("Could not get id for " + unwrapped.getClass().getName());
}
return (T) get(unwrapped.getClass(), id);
}
public Key> exists(final Object entityOrKey) {
final Object unwrapped = ProxyHelper.unwrap(entityOrKey);
final Key> key = getKey(unwrapped);
final Object id = key.getId();
if (id == null) {
throw new MappingException("Could not get id for " + unwrapped.getClass().getName());
}
String collName = key.getKind();
if (collName == null) {
collName = getCollection(key.getKindClass()).getName();
}
return find(collName, key.getKindClass()).filter(Mapper.ID_KEY, key.getId()).getKey();
}
@SuppressWarnings("rawtypes")
public DBCollection getCollection(final Class clazz) {
final String collName = mapper.getCollectionName(clazz);
return getDB().getCollection(collName);
}
public DBCollection getCollection(final Object obj) {
if (obj == null) {
return null;
}
return getCollection(obj.getClass());
}
protected DBCollection getCollection(final String kind) {
if (kind == null) {
return null;
}
return getDB().getCollection(kind);
}
public long getCount(final T entity) {
return getCollection(ProxyHelper.unwrap(entity)).count();
}
public long getCount(final Class clazz) {
return getCollection(clazz).count();
}
public long getCount(final String kind) {
return getCollection(kind).count();
}
public long getCount(final Query query) {
return query.countAll();
}
public Mongo getMongo() {
return mongo;
}
public DB getDB() {
return db;
}
public Mapper getMapper() {
return mapper;
}
public Iterable> insert(final Iterable entities) {
//TODO: try not to create two iterators...
final Object first = entities.iterator().next();
return insert(entities, getWriteConcern(first));
}
public Iterable> insert(final String kind, final Iterable entities, final WriteConcern wc) {
final DBCollection dbColl = db.getCollection(kind);
return insert(dbColl, entities, wc);
}
public Iterable> insert(final String kind, final Iterable entities) {
return insert(kind, entities, getWriteConcern(entities.iterator().next()));
}
public Iterable> insert(final Iterable entities, final WriteConcern wc) {
//TODO: Do this without creating another iterator
final DBCollection dbColl = getCollection(entities.iterator().next());
return insert(dbColl, entities, wc);
}
private Iterable> insert(final DBCollection dbColl, final Iterable entities, final WriteConcern wc) {
final ArrayList list = entities instanceof List
? new ArrayList(((List) entities).size())
: new ArrayList();
final Map involvedObjects = new LinkedHashMap();
for (final T ent : entities) {
final MappedClass mc = mapper.getMappedClass(ent);
if (mc.getAnnotation(NotSaved.class) != null) {
throw new MappingException(
"Entity type: " + mc.getClazz().getName() + " is marked as NotSaved which means you should not try to save it!");
}
list.add(entityToDBObj(ent, involvedObjects));
}
final WriteResult wr = null;
final DBObject[] dbObjects = new DBObject[list.size()];
dbColl.insert(list.toArray(dbObjects), wc);
throwOnError(wc, wr);
final ArrayList> savedKeys = new ArrayList>();
final Iterator entitiesIT = entities.iterator();
final Iterator dbObjectsIT = list.iterator();
while (entitiesIT.hasNext()) {
final T entity = entitiesIT.next();
final DBObject dbObj = dbObjectsIT.next();
savedKeys.add(postSaveGetKey(entity, dbObj, dbColl, involvedObjects));
}
return savedKeys;
}
public Iterable> insert(final T... entities) {
return insert(Arrays.asList(entities), getWriteConcern(entities[0]));
}
public Key insert(final T entity) {
return insert(entity, getWriteConcern(entity));
}
public Key insert(final T entity, final WriteConcern wc) {
final T unwrapped = ProxyHelper.unwrap(entity);
final DBCollection dbColl = getCollection(unwrapped);
return insert(dbColl, unwrapped, wc);
}
public Key insert(final String kind, final T entity) {
final T unwrapped = ProxyHelper.unwrap(entity);
final DBCollection dbColl = getCollection(kind);
return insert(dbColl, unwrapped, getWriteConcern(unwrapped));
}
public Key insert(final String kind, final T entity, final WriteConcern wc) {
final T unwrapped = ProxyHelper.unwrap(entity);
final DBCollection dbColl = getCollection(kind);
return insert(dbColl, unwrapped, wc);
}
protected Key insert(final DBCollection dbColl, final T entity, final WriteConcern wc) {
final LinkedHashMap involvedObjects = new LinkedHashMap();
final DBObject dbObj = entityToDBObj(entity, involvedObjects);
final WriteResult wr;
if (wc == null) {
wr = dbColl.insert(dbObj);
} else {
wr = dbColl.insert(dbObj, wc);
}
throwOnError(wc, wr);
return postSaveGetKey(entity, dbObj, dbColl, involvedObjects);
}
protected DBObject entityToDBObj(final Object entity, final Map involvedObjects) {
return mapper.toDBObject(ProxyHelper.unwrap(entity), involvedObjects);
}
/**
* call postSaveOperations and returns Key for entity
*/
protected Key postSaveGetKey(final T entity, final DBObject dbObj, final DBCollection dbColl,
final Map involvedObjects) {
if (dbObj.get(Mapper.ID_KEY) == null) {
throw new MappingException("Missing _id after save!");
}
postSaveOperations(entity, dbObj, involvedObjects);
final Key key = new Key(dbColl.getName(), getId(entity));
key.setKindClass((Class extends T>) entity.getClass());
return key;
}
public Iterable> save(final Iterable entities) {
Object first = null;
try {
first = entities.iterator().next();
} catch (Exception e) {
//do nothing
}
return save(entities, getWriteConcern(first));
}
public Iterable> save(final Iterable entities, final WriteConcern wc) {
final ArrayList> savedKeys = new ArrayList>();
for (final T ent : entities) {
savedKeys.add(save(ent, wc));
}
return savedKeys;
}
public Iterable> save(final T... entities) {
final ArrayList> savedKeys = new ArrayList>();
for (final T ent : entities) {
savedKeys.add(save(ent));
}
return savedKeys;
}
protected Key save(final DBCollection dbColl, final T entity, final WriteConcern wc) {
final MappedClass mc = mapper.getMappedClass(entity);
if (mc.getAnnotation(NotSaved.class) != null) {
throw new MappingException(
"Entity type: " + mc.getClazz().getName() + " is marked as NotSaved which means you should not try to save it!");
}
WriteResult wr;
//involvedObjects is used not only as a cache but also as a list of what needs to be called for life-cycle methods at the end.
final LinkedHashMap involvedObjects = new LinkedHashMap();
final DBObject dbObj = entityToDBObj(entity, involvedObjects);
//try to do an update if there is a @Version field
wr = tryVersionedUpdate(dbColl, entity, dbObj, wc, db, mc);
if (wr == null) {
if (wc == null) {
wr = dbColl.save(dbObj);
} else {
wr = dbColl.save(dbObj, wc);
}
}
throwOnError(wc, wr);
return postSaveGetKey(entity, dbObj, dbColl, involvedObjects);
}
protected WriteResult tryVersionedUpdate(final DBCollection dbColl, final T entity, final DBObject dbObj, final WriteConcern wc,
final DB database, final MappedClass mc) {
WriteResult wr = null;
if (mc.getFieldsAnnotatedWith(Version.class).isEmpty()) {
return wr;
}
final MappedField mfVersion = mc.getFieldsAnnotatedWith(Version.class).get(0);
final String versionKeyName = mfVersion.getNameToStore();
final Long oldVersion = (Long) mfVersion.getFieldValue(entity);
final long newVersion = VersionHelper.nextValue(oldVersion);
dbObj.put(versionKeyName, newVersion);
if (oldVersion != null && oldVersion > 0) {
final Object idValue = dbObj.get(Mapper.ID_KEY);
final UpdateResults res = update(find(dbColl.getName(), (Class) entity.getClass()).filter(Mapper.ID_KEY, idValue).filter(
versionKeyName, oldVersion), dbObj, false, false, wc);
wr = res.getWriteResult();
if (res.getUpdatedCount() != 1) {
throw new ConcurrentModificationException(
"Entity of class " + entity.getClass().getName() + " (id='" + idValue + "',version='" + oldVersion
+ "') was concurrently updated.");
}
} else if (wc == null) {
wr = dbColl.save(dbObj);
} else {
wr = dbColl.save(dbObj, wc);
}
//update the version.
mfVersion.setFieldValue(entity, newVersion);
return wr;
}
protected void throwOnError(final WriteConcern wc, final WriteResult wr) {
if (wc == null && wr.getLastConcern() == null) {
final CommandResult cr = wr.getLastError();
if (cr != null && cr.getErrorMessage() != null && cr.getErrorMessage().length() != 0) {
cr.throwOnError();
}
}
}
public Key save(final String kind, final T entity) {
final T unwrapped = ProxyHelper.unwrap(entity);
final DBCollection dbColl = getCollection(kind);
return save(dbColl, unwrapped, getWriteConcern(unwrapped));
}
public Key save(final T entity) {
return save(entity, getWriteConcern(entity));
}
public Key save(final T entity, final WriteConcern wc) {
final T unwrapped = ProxyHelper.unwrap(entity);
final DBCollection dbColl = getCollection(unwrapped);
return save(dbColl, unwrapped, wc);
}
public UpdateOperations createUpdateOperations(final Class clazz) {
return new UpdateOpsImpl(clazz, getMapper());
}
public UpdateOperations createUpdateOperations(final Class kind, final DBObject ops) {
final UpdateOpsImpl upOps = (UpdateOpsImpl) createUpdateOperations(kind);
upOps.setOps(ops);
return upOps;
}
public UpdateResults update(final Query query, final UpdateOperations ops, final boolean createIfMissing) {
return update(query, ops, createIfMissing, getWriteConcern(query.getEntityClass()));
}
public UpdateResults update(final Query query, final UpdateOperations ops, final boolean createIfMissing,
final WriteConcern wc) {
return update(query, ops, createIfMissing, true, wc);
}
public UpdateResults update(final T ent, final UpdateOperations ops) {
if (ent instanceof Query) {
return update((Query) ent, ops);
}
final MappedClass mc = mapper.getMappedClass(ent);
final Query q = (Query) createQuery(mc.getClazz());
q.disableValidation().filter(Mapper.ID_KEY, getId(ent));
if (!mc.getFieldsAnnotatedWith(Version.class).isEmpty()) {
final MappedField versionMF = mc.getFieldsAnnotatedWith(Version.class).get(0);
final Long oldVer = (Long) versionMF.getFieldValue(ent);
q.filter(versionMF.getNameToStore(), oldVer);
ops.set(versionMF.getNameToStore(), VersionHelper.nextValue(oldVer));
}
return update(q, ops);
}
public UpdateResults update(final Key key, final UpdateOperations ops) {
Class clazz = (Class) key.getKindClass();
if (clazz == null) {
clazz = (Class