@ -2,6 +2,8 @@
* Copyright 2010 - 2011 the original author or authors .
* Copyright 2010 - 2011 the original author or authors .
*
*
* Licensed under t
* Licensed under t
import org.springframework.util.ResourceUtils ;
import org.springframework.data.convert.EntityReader ;
import org.springframework.data.convert.EntityReader ;
he Apache License , Version 2 . 0 ( the "License" ) ;
he Apache License , Version 2 . 0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* you may not use this file except in compliance with the License .
@ -187,11 +189,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @param mongoConverter
* @param mongoConverter
* /
* /
public MongoTemplate ( MongoDbFactory mongoDbFactory , MongoConverter mongoConverter ) {
public MongoTemplate ( MongoDbFactory mongoDbFactory , MongoConverter mongoConverter ) {
Assert . notNull ( mongoDbFactory ) ;
Assert . notNull ( mongoDbFactory ) ;
this . mongoDbFactory = mongoDbFactory ;
this . mongoDbFactory = mongoDbFactory ;
this . mongoConverter = mongoConverter = = null ? getDefaultMongoConverter ( mongoDbFactory ) : mongoConverter ;
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
// We always have a mapping context in the converter, whether it's a simple one or not
mappingContext = this . mongoConverter . getMappingContext ( ) ;
mappingContext = this . mongoConverter . getMappingContext ( ) ;
@ -235,7 +238,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
/ * *
/ * *
* 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
* @param readPreference
* /
* /
public void setReadPreference ( ReadPreference readPreference ) {
public void setReadPreference ( ReadPreference readPreference ) {
@ -307,16 +312,32 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
public void executeQuery ( Query query , String collectionName , DocumentCallbackHandler dch ) {
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 queryObject = query . getQueryObject ( ) ;
DBObject fieldsObject = query . getFieldsObject ( ) ;
DBObject fieldsObject = query . getFieldsObject ( ) ;
if ( LOGGER . isDebugEnabled ( ) ) {
if ( LOGGER . isDebugEnabled ( ) ) {
LOGGER . debug ( "find using query: " + queryObject + " fields: " + fieldsObject + " in collection: "
LOGGER . debug ( "find using query: " + queryObject + " fields: " + fieldsObject + " in collection: "
+ collectionName ) ;
+ collectionName ) ;
}
}
this . executeQueryInternal ( new FindCallback ( queryObject , fieldsObject ) , preparer , dch , collectionName ) ;
this . executeQueryInternal ( new FindCallback ( queryObject , fieldsObject ) , preparer , dch , collectionName ) ;
}
}
@ -456,36 +477,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
public < T > List < T > find ( final Query query , Class < T > entityClass , String collectionName ) {
public < T > List < T > find ( final Query query , Class < T > entityClass , String collectionName ) {
CursorPreparer cursorPreparer = null ;
CursorPreparer cursorPreparer = query = = null ? null : new QueryCursorPreparer ( query ) ;
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 ;
}
} ;
}
return doFind ( collectionName , query . getQueryObject ( ) , query . getFieldsObject ( ) , entityClass , cursorPreparer ) ;
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 ) {
public < T > T findById ( Object id , Class < T > entityClass ) {
MongoPersistentEntity < ? > persistentEntity = mappingContext . getPersistentEntity ( entityClass ) ;
MongoPersistentEntity < ? > persistentEntity = mappingContext . getPersistentEntity ( entityClass ) ;
return findById ( id , entityClass , persistentEntity . getCollection ( ) ) ;
return findById ( id , entityClass , persistentEntity . getCollection ( ) ) ;
@ -729,7 +724,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
return execute ( collectionName , new CollectionCallback < Object > ( ) {
return execute ( collectionName , new CollectionCallback < Object > ( ) {
public Object doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
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 ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( writeConcernToUse = = null ) {
if ( writeConcernToUse = = null ) {
collection . insert ( dbDoc ) ;
collection . insert ( dbDoc ) ;
@ -751,7 +747,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
execute ( collectionName , new CollectionCallback < Void > ( ) {
execute ( collectionName , new CollectionCallback < Void > ( ) {
public Void doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
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 ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( writeConcernToUse = = null ) {
if ( writeConcernToUse = = null ) {
collection . insert ( dbDocList ) ;
collection . insert ( dbDocList ) ;
@ -781,7 +778,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
return execute ( collectionName , new CollectionCallback < Object > ( ) {
return execute ( collectionName , new CollectionCallback < Object > ( ) {
public Object doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
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 ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( writeConcernToUse = = null ) {
if ( writeConcernToUse = = null ) {
collection . save ( dbDoc ) ;
collection . save ( dbDoc ) ;
@ -793,7 +791,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} ) ;
} ) ;
}
}
public WriteResult upsert ( Query query , Update update , Class < ? > entityClass ) {
public WriteResult upsert ( Query query , Update update , Class < ? > entityClass ) {
return doUpdate ( determineCollectionName ( entityClass ) , query , update , entityClass , true , false ) ;
return doUpdate ( determineCollectionName ( entityClass ) , query , update , entityClass , true , false ) ;
}
}
@ -840,7 +837,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
WriteResult wr ;
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 ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( writeConcernToUse = = null ) {
if ( writeConcernToUse = = null ) {
wr = collection . update ( queryObj , updateObj , upsert , multi ) ;
wr = collection . update ( queryObj , updateObj , upsert , multi ) ;
@ -920,7 +918,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public Void doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
public Void doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
DBObject dboq = mapper . getMappedObject ( queryObject , entity ) ;
DBObject dboq = mapper . getMappedObject ( queryObject , entity ) ;
WriteResult wr = null ;
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 ) ;
WriteConcern writeConcernToUse = prepareWriteConcern ( mongoAction ) ;
if ( LOGGER . isDebugEnabled ( ) ) {
if ( LOGGER . isDebugEnabled ( ) ) {
LOGGER . debug ( "remove using query: " + queryObject + " in collection: " + collection . getName ( ) ) ;
LOGGER . debug ( "remove using query: " + queryObject + " in collection: " + collection . getName ( ) ) ;
@ -1017,7 +1016,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return group ( null , inputCollectionName , groupBy , 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 ( ) ;
DBObject dbo = groupBy . getGroupByObject ( ) ;
dbo . put ( "ns" , inputCollectionName ) ;
dbo . put ( "ns" , inputCollectionName ) ;
@ -1027,12 +1027,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} else {
} else {
dbo . put ( "cond" , criteria . getCriteriaObject ( ) ) ;
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" ) ) {
if ( dbo . containsField ( "initial" ) ) {
Object initialObj = dbo . get ( "initial" ) ;
Object initialObj = dbo . get ( "initial" ) ;
if ( initialObj instanceof String ) {
if ( initialObj instanceof String ) {
String initialAsString = replaceWithResourceIfNecessary ( ( String ) initialObj ) ;
String initialAsString = replaceWithResourceIfNecessary ( ( String ) initialObj ) ;
dbo . put ( "initial" , JSON . parse ( initialAsString ) ) ;
dbo . put ( "initial" , JSON . parse ( initialAsString ) ) ;
}
}
}
}
@ -1070,7 +1071,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
@SuppressWarnings ( "unchecked" )
@SuppressWarnings ( "unchecked" )
Iterable < DBObject > resultSet = ( Iterable < DBObject > ) commandResult . get ( "retval" ) ;
Iterable < DBObject > resultSet = ( Iterable < DBObject > ) commandResult . get ( "retval" ) ;
List < T > mappedResults = new ArrayList < T > ( ) ;
List < T > mappedResults = new ArrayList < T > ( ) ;
DbObjectCallback < T > callback = new ReadDbObjectCallback < T > ( mongoConverter , entityClass ) ;
DbObjectCallback < T > callback = new ReadDbObjectCallback < T > ( mongoConverter , entityClass ) ;
@ -1080,7 +1081,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
GroupByResults < T > groupByResult = new GroupByResults < T > ( mappedResults , commandResult ) ;
GroupByResults < T > groupByResult = new GroupByResults < T > ( mappedResults , commandResult ) ;
return groupByResult ;
return groupByResult ;
}
}
protected String replaceWithResourceIfNecessary ( String function ) {
protected String replaceWithResourceIfNecessary ( String function ) {
@ -1300,7 +1300,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
protected < T > T doFindAndModify ( String collectionName , DBObject query , DBObject fields , DBObject sort ,
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 ;
EntityReader < ? super T , DBObject > readerToUse = this . mongoConverter ;
@ -1316,12 +1316,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
+ entityClass + " and update: " + updateObj + " in collection: " + collectionName ) ;
+ entityClass + " and update: " + updateObj + " in collection: " + collectionName ) ;
}
}
return executeFindOneInternal ( new FindAndModifyCallback ( mapper . getMappedObject ( query , entity ) , fields , sort ,
return executeFindOneInternal ( new FindAndModifyCallback ( mapper . getMappedObject ( query , entity ) , fields , sort , updateObj , options ) ,
updateObj , options ) , new ReadDbObjectCallback < T > ( readerToUse , entityClass ) , collectionName ) ;
new ReadDbObjectCallback < T > ( readerToUse , entityClass ) , collectionName ) ;
}
}
/ * *
/ * *
* Populates the id property of the saved object , if it ' s not set already .
* Populates the id property of the saved object , if it ' s not set already .
*
*
@ -1478,7 +1476,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/ * *
/ * *
* Checks and handles any errors .
* Checks and handles any errors .
* < p / >
* < 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 ) {
private void handleAnyWriteResultErrors ( WriteResult wr , DBObject query , String operation ) {
if ( WriteResultChecking . NONE = = this . writeResultChecking ) {
if ( WriteResultChecking . NONE = = this . writeResultChecking ) {
@ -1620,7 +1619,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
private final DBObject update ;
private final DBObject update ;
private final FindAndModifyOptions options ;
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 . query = query ;
this . fields = fields ;
this . fields = fields ;
this . sort = sort ;
this . sort = sort ;
@ -1629,7 +1629,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
public DBObject doInCollection ( DBCollection collection ) throws MongoException , DataAccessException {
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 ( ) ) ;
}
}
}
}
@ -1682,6 +1683,52 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
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
* { @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 .
* a delegate and creates a { @link GeoResult } from the result .