@ -2,6 +2,8 @@
@@ -2,6 +2,8 @@
* Copyright 2010 - 2011 the original author or authors .
*
* Licensed under t
import org.springframework.util.ResourceUtils ;
import org.springframework.data.convert.EntityReader ;
he Apache License , Version 2 . 0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
@ -125,7 +127,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -125,7 +127,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* the DB or Collection .
* /
private WriteConcern writeConcern = null ;
private WriteConcernResolver writeConcernResolver = new DefaultWriteConcernResolver ( ) ;
/ *
@ -135,7 +137,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -135,7 +137,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
private WriteResultChecking writeResultChecking = WriteResultChecking . NONE ;
/ * *
* Set the ReadPreference when operating on a collection . See { @link # prepareCollection ( DBCollection ) }
* Set the ReadPreference when operating on a collection . See { @link # prepareCollection ( DBCollection ) }
* /
private ReadPreference readPreference = null ;
@ -187,11 +189,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -187,11 +189,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @param mongoConverter
* /
public MongoTemplate ( MongoDbFactory mongoDbFactory , MongoConverter mongoConverter ) {
Assert . notNull ( mongoDbFactory ) ;
this . mongoDbFactory = mongoDbFactory ;
this . mongoConverter = mongoConverter = = null ? getDefaultMongoConverter ( mongoDbFactory ) : mongoConverter ;
this . mapper = new QueryMapper ( this . mongoConverter . getConversionService ( ) ) ;
this . mapper = new QueryMapper ( this . mongoConverter ) ;
// We always have a mapping context in the converter, whether it's a simple one or not
mappingContext = this . mongoConverter . getMappingContext ( ) ;
@ -233,9 +236,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -233,9 +236,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public void setWriteConcernResolver ( WriteConcernResolver writeConcernResolver ) {
this . writeConcernResolver = writeConcernResolver ;
}
/ * *
* Used by @ { link { @link # prepareCollection ( DBCollection ) } to set the { @link ReadPreference } before any operations are performed .
* Used by @ { link { @link # prepareCollection ( DBCollection ) } to set the { @link ReadPreference } before any operations are
* performed .
*
* @param readPreference
* /
public void setReadPreference ( ReadPreference readPreference ) {
@ -307,16 +312,32 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -307,16 +312,32 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public void executeQuery ( Query query , String collectionName , DocumentCallbackHandler dch ) {
executeQuery ( query , collectionName , dch , null ) ;
executeQuery ( query , collectionName , dch , new QueryCursorPreparer ( query ) ) ;
}
public void executeQuery ( Query query , String collectionName , DocumentCallbackHandler dch , CursorPreparer preparer ) {
/ * *
* Execute a MongoDB query and iterate over the query results on a per - document basis with a
* { @link DocumentCallbackHandler } using the provided CursorPreparer .
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification , must not be { @literal null } .
* @param collectionName name of the collection to retrieve the objects from
* @param dch the handler that will extract results , one document at a time
* @param preparer allows for customization of the { @link DBCursor } used when iterating over the result set , ( apply
* limits , skips and so on ) .
* /
protected void executeQuery ( Query query , String collectionName , DocumentCallbackHandler dch , CursorPreparer preparer ) {
Assert . notNull ( query ) ;
DBObject queryObject = query . getQueryObject ( ) ;
DBObject fieldsObject = query . getFieldsObject ( ) ;
if ( LOGGER . isDebugEnabled ( ) ) {
LOGGER . debug ( "find using query: " + queryObject + " fields: " + fieldsObject + " in collection: "
+ collectionName ) ;
}
this . executeQueryInternal ( new FindCallback ( queryObject , fieldsObject ) , preparer , dch , collectionName ) ;
}
@ -456,36 +477,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -456,36 +477,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public < T > List < T > find ( final Query query , Class < T > entityClass , String collectionName ) {
CursorPreparer cursorPreparer = null ;
if ( query . getSkip ( ) > 0 | | query . getLimit ( ) > 0 | | query . getSortObject ( ) ! = null ) {
cursorPreparer = new CursorPreparer ( ) {
public DBCursor prepare ( DBCursor cursor ) {
DBCursor cursorToUse = cursor ;
try {
if ( query . getSkip ( ) > 0 ) {
cursorToUse = cursorToUse . skip ( query . getSkip ( ) ) ;
}
if ( query . getLimit ( ) > 0 ) {
cursorToUse = cursorToUse . limit ( query . getLimit ( ) ) ;
}
if ( query . getSortObject ( ) ! = null ) {
cursorToUse = cursorToUse . sort ( query . getSortObject ( ) ) ;
}
} catch ( RuntimeException e ) {
throw potentiallyConvertRuntimeException ( e ) ;
}
return cursorToUse ;
}
} ;
}
CursorPreparer cursorPreparer = query = = null ? null : new QueryCursorPreparer ( query ) ;
return doFind ( collectionName , query . getQueryObject ( ) , query . getFieldsObject ( ) , entityClass , cursorPreparer ) ;
}
public < T > List < T > find ( Query query , Class < T > entityClass , CursorPreparer preparer , String collectionName ) {
return doFind ( collectionName , query . getQueryObject ( ) , query . getFieldsObject ( ) , entityClass , preparer ) ;
}
public < T > T findById ( Object id , Class < T > entityClass ) {
MongoPersistentEntity < ? > persistentEntity = mappingContext . getPersistentEntity ( entityClass ) ;
return findById ( id , entityClass , persistentEntity . getCollection ( ) ) ;
@ -542,7 +537,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -542,7 +537,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return doFindAndModify ( collectionName , query . getQueryObject ( ) , query . getFieldsObject ( ) , query . getSortObject ( ) ,
entityClass , update , options ) ;
}
// Find methods that take a Query to express the query and that return a single object that is also removed from the
// collection in the database.
@ -563,13 +558,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -563,13 +558,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public long count ( final Query query , String collectionName ) {
return count ( query , null , collectionName ) ;
}
private long count ( Query query , Class < ? > entityClass , String collectionName ) {
Assert . hasText ( collectionName ) ;
final DBObject dbObject = query = = null ? null : mapper . getMappedObject ( query . getQueryObject ( ) ,
entityClass = = null ? null : mappingContext . getPersistentEntity ( entityClass ) ) ;
return execute ( collectionName , new CollectionCallback < Long > ( ) {
public Long doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
return collection . count ( dbObject ) ;
@ -729,7 +724,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -729,7 +724,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
return execute ( collectionName , new CollectionCallback < Object > ( ) {
public Object doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . INSERT , collectionName , entityClass , dbDoc , null ) ;
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . INSERT , collectionName ,
entityClass , dbDoc , null ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( writeConcernToUse = = null ) {
collection . insert ( dbDoc ) ;
@ -751,7 +747,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -751,7 +747,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
execute ( collectionName , new CollectionCallback < Void > ( ) {
public Void doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . INSERT_LIST , collectionName , null , null , null ) ;
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . INSERT_LIST , collectionName , null ,
null , null ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( writeConcernToUse = = null ) {
collection . insert ( dbDocList ) ;
@ -781,7 +778,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -781,7 +778,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
return execute ( collectionName , new CollectionCallback < Object > ( ) {
public Object doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . SAVE , collectionName , entityClass , dbDoc , null ) ;
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . SAVE , collectionName , entityClass ,
dbDoc , null ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( writeConcernToUse = = null ) {
collection . save ( dbDoc ) ;
@ -793,15 +791,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -793,15 +791,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} ) ;
}
public WriteResult upsert ( Query query , Update update , Class < ? > entityClass ) {
return doUpdate ( determineCollectionName ( entityClass ) , query , update , entityClass , true , false ) ;
}
public WriteResult upsert ( Query query , Update update , String collectionName ) {
return doUpdate ( collectionName , query , update , null , true , false ) ;
}
public WriteResult updateFirst ( Query query , Update update , Class < ? > entityClass ) {
return doUpdate ( determineCollectionName ( entityClass ) , query , update , entityClass , false , false ) ;
}
@ -840,7 +837,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -840,7 +837,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
WriteResult wr ;
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . UPDATE , collectionName , entityClass , updateObj , queryObj ) ;
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . UPDATE , collectionName ,
entityClass , updateObj , queryObj ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( writeConcernToUse = = null ) {
wr = collection . update ( queryObj , updateObj , upsert , multi ) ;
@ -861,18 +859,18 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -861,18 +859,18 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
remove ( getIdQueryFor ( object ) , object . getClass ( ) ) ;
}
public void remove ( Object object , String collection ) {
Assert . hasText ( collection ) ;
if ( object = = null ) {
return ;
}
remove ( getIdQueryFor ( object ) , collection ) ;
}
/ * *
* Returns a { @link Query } for the given entity by its id .
*
@ -880,9 +878,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -880,9 +878,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @return
* /
private Query getIdQueryFor ( Object object ) {
Assert . notNull ( object ) ;
MongoPersistentEntity < ? > entity = mappingContext . getPersistentEntity ( object . getClass ( ) ) ;
MongoPersistentProperty idProp = entity . getIdProperty ( ) ;
@ -894,10 +892,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -894,10 +892,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
Object idProperty = null ;
try {
idProperty = BeanWrapper . create ( object , service ) . getProperty ( idProp , Object . class , true ) ;
return new Query ( where ( idProp . getFieldName ( ) ) . is ( idProperty ) ) ;
} catch ( IllegalAccessException e ) {
throw new MappingException ( e . getMessage ( ) , e ) ;
} catch ( InvocationTargetException e ) {
@ -920,7 +918,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -920,7 +918,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public Void doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
DBObject dboq = mapper . getMappedObject ( queryObject , entity ) ;
WriteResult wr = null ;
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . REMOVE , collectionName , entityClass , null , queryObject ) ;
MongoAction mongoAction = new MongoAction ( writeConcern , MongoActionOperation . REMOVE , collectionName ,
entityClass , null , queryObject ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( LOGGER . isDebugEnabled ( ) ) {
LOGGER . debug ( "remove using query: " + queryObject + " in collection: " + collection . getName ( ) ) ;
@ -1012,28 +1011,30 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1012,28 +1011,30 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return mapReduceResult ;
}
public < T > GroupByResults < T > group ( String inputCollectionName , GroupBy groupBy , Class < T > entityClass ) {
return group ( null , inputCollectionName , groupBy , entityClass ) ;
}
public < T > GroupByResults < T > group ( Criteria criteria , String inputCollectionName , GroupBy groupBy , Class < T > entityClass ) {
public < T > GroupByResults < T > group ( Criteria criteria , String inputCollectionName , GroupBy groupBy ,
Class < T > entityClass ) {
DBObject dbo = groupBy . getGroupByObject ( ) ;
dbo . put ( "ns" , inputCollectionName ) ;
if ( criteria = = null ) {
dbo . put ( "cond" , null ) ;
} else {
dbo . put ( "cond" , criteria . getCriteriaObject ( ) ) ;
}
//If initial document was a JavaScript string, potentially loaded by Spring's Resource abstraction, load it and convert to DBObject
// If initial document was a JavaScript string, potentially loaded by Spring's Resource abstraction, load it and
// convert to DBObject
if ( dbo . containsField ( "initial" ) ) {
Object initialObj = dbo . get ( "initial" ) ;
if ( initialObj instanceof String ) {
String initialAsString = replaceWithResourceIfNecessary ( ( String ) initialObj ) ;
dbo . put ( "initial" , JSON . parse ( initialAsString ) ) ;
String initialAsString = replaceWithResourceIfNecessary ( ( String ) initialObj ) ;
dbo . put ( "initial" , JSON . parse ( initialAsString ) ) ;
}
}
@ -1046,16 +1047,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1046,16 +1047,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
if ( dbo . containsField ( "finalize" ) ) {
dbo . put ( "finalize" , replaceWithResourceIfNecessary ( dbo . get ( "finalize" ) . toString ( ) ) ) ;
}
DBObject commandObject = new BasicDBObject ( "group" , dbo ) ;
if ( LOGGER . isDebugEnabled ( ) ) {
LOGGER . debug ( "Executing Group with DBObject [" + commandObject . toString ( ) + "]" ) ;
}
CommandResult commandResult = null ;
try {
commandResult = executeCommand ( commandObject , getDb ( ) . getOptions ( ) ) ;
commandResult . throwOnError ( ) ;
commandResult = executeCommand ( commandObject , getDb ( ) . getOptions ( ) ) ;
commandResult . throwOnError ( ) ;
} catch ( RuntimeException ex ) {
this . potentiallyConvertRuntimeException ( ex ) ;
}
@ -1068,10 +1069,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1068,10 +1069,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
if ( LOGGER . isDebugEnabled ( ) ) {
LOGGER . debug ( "Group command result = [" + commandResult + "]" ) ;
}
@SuppressWarnings ( "unchecked" )
Iterable < DBObject > resultSet = ( Iterable < DBObject > ) commandResult . get ( "retval" ) ;
Iterable < DBObject > resultSet = ( Iterable < DBObject > ) commandResult . get ( "retval" ) ;
List < T > mappedResults = new ArrayList < T > ( ) ;
DbObjectCallback < T > callback = new ReadDbObjectCallback < T > ( mongoConverter , entityClass ) ;
for ( DBObject dbObject : resultSet ) {
@ -1079,7 +1080,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1079,7 +1080,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
GroupByResults < T > groupByResult = new GroupByResults < T > ( mappedResults , commandResult ) ;
return groupByResult ;
}
@ -1298,30 +1298,28 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1298,30 +1298,28 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return executeFindOneInternal ( new FindAndRemoveCallback ( mapper . getMappedObject ( query , entity ) , fields , sort ) ,
new ReadDbObjectCallback < T > ( readerToUse , entityClass ) , collectionName ) ;
}
protected < T > T doFindAndModify ( String collectionName , DBObject query , DBObject fields , DBObject sort ,
Class < T > entityClass , Update update , FindAndModifyOptions options ) {
Class < T > entityClass , Update update , FindAndModifyOptions options ) {
EntityReader < ? super T , DBObject > readerToUse = this . mongoConverter ;
MongoPersistentEntity < ? > entity = mappingContext . getPersistentEntity ( entityClass ) ;
DBObject updateObj = update . getUpdateObject ( ) ;
for ( String key : updateObj . keySet ( ) ) {
updateObj . put ( key , mongoConverter . convertToMongoType ( updateObj . get ( key ) ) ) ;
}
if ( LOGGER . isDebugEnabled ( ) ) {
LOGGER . debug ( "findAndModify using query: " + query + " fields: " + fields + " sort: " + sort + " for class: "
+ entityClass + " and update: " + updateObj + " in collection: " + collectionName ) ;
}
return executeFindOneInternal ( new FindAndModifyCallback ( mapper . getMappedObject ( query , entity ) , fields , sort , updateObj , options ) ,
new ReadDbObjectCallback < T > ( readerToUse , entityClass ) , collectionName ) ;
return executeFindOneInternal ( new FindAndModifyCallback ( mapper . getMappedObject ( query , entity ) , fields , sort ,
updateObj , options ) , new ReadDbObjectCallback < T > ( readerToUse , entityClass ) , collectionName ) ;
}
/ * *
* Populates the id property of the saved object , if it ' s not set already .
*
@ -1478,7 +1476,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1478,7 +1476,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/ * *
* Checks and handles any errors .
* < p / >
* Current implementation logs errors . Future version may make this configurable to log warning , errors or throw exception .
* Current implementation logs errors . Future version may make this configurable to log warning , errors or throw
* exception .
* /
private void handleAnyWriteResultErrors ( WriteResult wr , DBObject query , String operation ) {
if ( WriteResultChecking . NONE = = this . writeResultChecking ) {
@ -1611,7 +1610,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1611,7 +1610,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return collection . findAndModify ( query , fields , sort , true , null , false , false ) ;
}
}
private static class FindAndModifyCallback implements CollectionCallback < DBObject > {
private final DBObject query ;
@ -1620,7 +1619,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1620,7 +1619,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
private final DBObject update ;
private final FindAndModifyOptions options ;
public FindAndModifyCallback ( DBObject query , DBObject fields , DBObject sort , DBObject update , FindAndModifyOptions options ) {
public FindAndModifyCallback ( DBObject query , DBObject fields , DBObject sort , DBObject update ,
FindAndModifyOptions options ) {
this . query = query ;
this . fields = fields ;
this . sort = sort ;
@ -1629,7 +1629,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1629,7 +1629,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public DBObject doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
return collection . findAndModify ( query , fields , sort , options . isRemove ( ) , update , options . isReturnNew ( ) , options . isUpsert ( ) ) ;
return collection . findAndModify ( query , fields , sort , options . isRemove ( ) , update , options . isReturnNew ( ) ,
options . isUpsert ( ) ) ;
}
}
@ -1673,15 +1674,61 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -1673,15 +1674,61 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return source ;
}
}
private class DefaultWriteConcernResolver implements WriteConcernResolver {
public WriteConcern resolve ( MongoAction action ) {
return action . getDefaultWriteConcern ( ) ;
return action . getDefaultWriteConcern ( ) ;
}
}
class QueryCursorPreparer implements CursorPreparer {
private final Query query ;
public QueryCursorPreparer ( Query query ) {
this . query = query ;
}
/ *
* ( non - Javadoc )
* @see org . springframework . data . mongodb . core . CursorPreparer # prepare ( com . mongodb . DBCursor )
* /
public DBCursor prepare ( DBCursor cursor ) {
if ( query = = null ) {
return cursor ;
}
if ( query . getSkip ( ) < = 0 & & query . getLimit ( ) < = 0 & & query . getSortObject ( ) = = null & & ! StringUtils
. hasText ( query . getHint ( ) ) ) {
return cursor ;
}
DBCursor cursorToUse = cursor ;
try {
if ( query . getSkip ( ) > 0 ) {
cursorToUse = cursorToUse . skip ( query . getSkip ( ) ) ;
}
if ( query . getLimit ( ) > 0 ) {
cursorToUse = cursorToUse . limit ( query . getLimit ( ) ) ;
}
if ( query . getSortObject ( ) ! = null ) {
cursorToUse = cursorToUse . sort ( query . getSortObject ( ) ) ;
}
if ( StringUtils . hasText ( query . getHint ( ) ) ) {
cursorToUse = cursorToUse . hint ( query . getHint ( ) ) ;
}
} catch ( RuntimeException e ) {
throw potentiallyConvertRuntimeException ( e ) ;
}
return cursorToUse ;
}
}
/ * *
* { @link DbObjectCallback } that assumes a { @link GeoResult } to be created , delegates actual content unmarshalling to
* a delegate and creates a { @link GeoResult } from the result .