Compare commits

...

161 Commits

Author SHA1 Message Date
Oliver Gierke
4ca16f8d15 DATAMONGO-456 - Fixed <db-factory /> element documentation in XSD. 2012-09-17 12:20:52 +02:00
Oliver Gierke
1b3d49e466 DATAMONGO-457 - Fixed links in reference documentation. 2012-09-17 12:09:05 +02:00
Oliver Gierke
bf89cce43c DATAMONGO-539 - Fixed MongoTemplate.remove(object, collectionName).
If the entity being removed using MongoTemplate.remove(object, collectionName) contained an id that could be converted into an ObjectID it wasn't removed correctly currently. This was caused by the fact that the intermediate call didn't hand over the entity type and thus the id conversion failed. This in turn caused the query not to match the previous saved object.
2012-09-17 11:44:30 +02:00
Oliver Gierke
aef493fdb8 DATAMONGO-539 - Added test case to show removing entity from explicit collection works. 2012-09-13 17:24:27 +02:00
Oliver Gierke
eabd47ae8d DATAMONGO-532 - Synchronize DB authentication.
In multithreaded environments Mongo database authentication can be triggered twice if two or more threads refer to the same db instance. This is now prevented by synchronizing calls to db.authenticate(…).
2012-09-12 13:00:46 +02:00
Oliver Gierke
1d9ee9a28f DATAMONGO-537 - Work around compiler issues with generics. 2012-09-12 10:40:19 +02:00
Oliver Gierke
e0b0792643 DATAMONGO-537 - Guard index creation tests against changing method orders. 2012-09-12 10:11:26 +02:00
Oliver Gierke
d5f8285d51 DATAMONGO-529 - Update Querydsl setup to use 1.0.4.
Raised Maven compiler plugin version to 2.5.1.
2012-09-11 18:17:20 +02:00
Oliver Gierke
4ce8da7ebf DATAMONGO-536 - Fixed package cycle introduced by SerializationUtils. 2012-09-11 18:08:56 +02:00
Oliver Gierke
c1030abe96 DATAMONGO-512 - Fixed broken handling of AND in query methods.
Apparently the fix fof DATAMONGO-469 was messed up later on.
2012-09-04 07:32:27 +02:00
Oliver Gierke
2a8f13d5d5 DATAMONGO-527 - Fixed Criteria.equals(…). 2012-09-04 07:32:27 +02:00
Spring Buildmaster
e4adc0ce23 DATAMONGO-514 - Prepare next development iteration. 2012-08-24 01:54:30 -07:00
Spring Buildmaster
beeed68873 DATAMONGO-514 - Release 1.0.4.RELEASE. 2012-08-24 01:54:26 -07:00
Oliver Gierke
22872f97dc DATAMONGO-514 - Prepare changelog for 1.0.4.RELEASE. 2012-08-24 10:43:54 +02:00
Oliver Gierke
323de58efc DATAMONGO-499 - Fixed namespace reference to repository XSD. 2012-07-31 11:00:23 +02:00
Oliver Gierke
58f12b8d8f DATAMONGO-494 - QueryMapper now forwards entity metadata into nested $(n)or criterias.
Introduced helper class to ease assertions on DBObjects as well.
2012-07-27 16:16:57 +02:00
Oliver Gierke
48cb155f6c DATAMONGO-493 - Fixed broken $ne handling in QueryMapper.
$ne expressions are now only being tried to be converted into an ObjectId in case they follow an id property. Previously they tried in every case which might have led to Strings being converted into ObjectIds that accidentally were valid ObjectIds but didn't represent an id at all.
2012-07-27 16:16:29 +02:00
Oliver Gierke
594ddbd1c1 DATAMONGO-495 - Fixed debug output in MongoTemplate.doFind(…).
Using SerializationUtils to safely output the query to be executed.
2012-07-26 10:29:11 +02:00
Spring Buildmaster
2c2bbf415b DATAMONGO-492 - Prepare next development iteration. 2012-07-24 07:01:24 -07:00
Spring Buildmaster
9375e7b981 DATAMONGO-492 - Release 1.0.3.RELEASE. 2012-07-24 07:01:21 -07:00
Oliver Gierke
30a4682369 DATAMONGO-492 - Prepare changelog for 1.0.3.RELEASE. 2012-07-24 15:34:33 +02:00
Oliver Gierke
356e6acd43 DATAMONGO-474 - Populating id's after save now inspects field only.
So far the algorithm to inspect whether an id property has to be set after a save(…) operation has used the plain BeanWrapper.getProperty(PersistentProperty property) method. This caused problems in case the getter of the id field returned something completely different (to be precise: a complex type not convertible out of the box).

We now inspect the id field only to retrieve the value.
2012-07-24 13:30:04 +02:00
Oliver Gierke
09ed4aaf24 DATAMONGO-489 - Ensure read collections get converted to appropriate target type.
When reading BasicDBLists we now make sure the resulting collection is converted into the actual target type eventually. It might be an array and thus need an additional round of massaging before being returned as value.
2012-07-23 16:41:28 +02:00
Oliver Gierke
c7995eb462 DATAMONGO-485 - Added test case to show complex id's are working. 2012-07-17 12:46:34 +02:00
Amol Nayak
2e6a9a6ee7 DATAMONGO-480 - Consider WriteResult for insert(…) and save(…) methods. 2012-07-16 18:53:38 +02:00
Oliver Gierke
a82fbade95 DATAMONGO-483 - Indexes now use the field name even if index name is defined. 2012-07-16 18:36:56 +02:00
Oliver Gierke
7a9ba3fe3e DATAMONGO-482 - Fixed typo in reference documentation. 2012-07-16 17:43:01 +02:00
Oliver Gierke
134e7762a7 DATAMONGO-474 - Fixed criteria mapping for MongoTemplate.group(…).
The criteria object handed to the group object needs to be mapped correctly to map complex values. Improved error handling on the way.
2012-07-16 16:37:18 +02:00
Oliver Gierke
e41299ff38 DATAMONGO-475 - Fixed debug output in map-reduce operations.
Using SerializationUtils.serializeToJsonSafely(…) instead of plain toString() as this might cause SerializationExceptions for complex objects.
2012-07-16 14:55:32 +02:00
Oliver Gierke
5cf7a86023 DATAMONGO-469 - Fixed parsing of And keyword in derived queries. 2012-06-26 13:44:22 +02:00
Oliver Gierke
0aacb887de DATAMONGO-470 - Implemented equals(…) and hashCode() for Query and Criteria. 2012-06-26 13:36:00 +02:00
Oliver Gierke
ba81f21aba DATAMONGO-467 - Fix identifier handling for Querydsl.
As we try to massage the value of the id property into an ObjectId if possible we need to do so as well when mapping the Querydsl query. Adapted SpringDataMongoDbSerializer accordingly.
2012-06-25 13:11:37 +02:00
Oliver Gierke
43dee69fe0 DATAMONGO-464 - Fixed resource synchronization in MongoDbUtils.
MongoDbUtils now correctly returns DB instances for others than the first one bound. So far the lookup for an alternate database resulted in the first one bound to be returned.
2012-06-25 11:16:31 +02:00
Oliver Gierke
1be1297ef9 DATAMONGO-466 - QueryMapper now only tries id conversion for top level document.
So far the QueryMapper has tried to map id properties of nested documents to ObjectIds which it shouldn't do actually.
2012-06-22 15:14:37 +02:00
Spring Buildmaster
dad0789356 DATAMONGO-463 - Prepare next development iteration. 2012-06-20 03:53:31 -07:00
Spring Buildmaster
80ee7d9553 DATAMONGO-463 - Release 1.0.2.RELEASE. 2012-06-20 03:53:28 -07:00
Oliver Gierke
7e3dfa5504 DATAMONGO-463 - Polished pom.
Upgrade to Log4J 1.2.16 to remove the need for the exclusions. Replaced ${pom.version} with ${project.version}.
2012-06-20 12:48:14 +02:00
Oliver Gierke
b5b11772b6 DATAMONGO-463 - Prepare 1.0.2 release.
Polished pom and updated changelog.
2012-06-20 12:36:48 +02:00
Oliver Gierke
416dc563f2 DATAMONGO-463 - Update reference documentation to point to 1.0.2.RELEASE JARS. 2012-06-20 12:24:27 +02:00
Oliver Gierke
a41b877081 DATAMONGO-455 - Documentation mentions BasicQuery. 2012-06-20 12:23:32 +02:00
Oliver Gierke
c4c8e368ca DATAMONGO-454 - Improvements to ServerAddressPropertyEditor.
ServerAddressPropertyEditor now only eventually fails if none of the configured addresses can be parsed correctly. Strengthened the parsing implementation to not fail for host-only parsing or accidental double commas. Cleaned up test dependency setup.
2012-06-20 11:27:37 +02:00
Oliver Gierke
11f0c515b0 DATAMONGO-378 - Fixed potential ClassCastException for MapReduceResults and upcoming MongoDB release.
The type of the value returned for the total field of the timing map in map-reduce results has changed from Integer to Long as of MongoDB version 2.1.0 apparently. Changed MapReduceResults to accommodate either Integer or Long types.
2012-06-20 10:05:53 +02:00
Oliver Gierke
25a94bc45e DATAMONGO-462 - Added custom converters for URL.
So far URL instances were treated as entities and serialized as nested document. As there was no custom converter registered to re-instantiate the objects and URL does not contain a no-arg constructor, reading the instances back in resulted in an ugly exception in ReflectionEntityInstantiator. We now register a custom Converter to serialize URL instances as their plain toString() representation. This causes the reading working out of the box as the StringToObjectConverter registered by default uses the constructor taking a String on URL accidentally. To make sure this still works we added an explicit StringToURLConverter to implement symmetric conversions.
2012-06-19 18:55:19 +02:00
Oliver Gierke
783cec0325 DATAMONGO-461 - Fixed potential NullPointerException in MappedConstructor.
Reject PersistentEntity instances that don't have a PersistenceConstructor as this indicates a mapping problem (either a constructor needed to be annotated or a custom converter registered).
2012-06-19 17:46:54 +02:00
Oliver Gierke
b02e81c481 DATAMONGO-428 - Fixed parsing of output collection for complex MapReduce result.
The raw result for a map-reduce operation might contain a complex element containing the output collection in case the original request configured an output database as option. Adapted the parsing of the output collection to accommodate both scenarios (plain String value as well as DBObject wrapper).
2012-06-19 09:29:56 +02:00
Oliver Gierke
3c90b4987d Added missing dependency declarations.
Build has worked so far because we relied on the dependencies being pulled in transitively but it's best practice not to do so.
2012-06-15 19:25:31 +02:00
Oliver Gierke
a4a03b0164 DATAMONGO-450 - Log output uses mapped query for debug logging. 2012-06-14 12:36:42 +02:00
Oliver Gierke
651255ca58 DATAMONGO-447 - Fixed broken log output in debug level.
The debug output now uses the already mapped query object when concatenating the log string. Improved applying the id after save operations by inspecting whether the object already has the id set before trying to set it. This could have caused problems in case you use a complex id and don't provide a custom converter as it can be serialized out of the box. Fixed minor glitch in MappingMongoConverter which was not really a bug as another path through the code has covered the scenario later on. Introduced SerializationUtils class that provides a method to safely serialize objects to pseudo JSON. Pseudo in the sense that it simply renders a complex object as { $java : object.toString() }. This is useful for debug output before the DBObject was mapped into Mongo-native types.
2012-06-14 12:27:19 +02:00
Maciej Walkowiak
ccf006e41b DATAMONGO-446 - Fixed bug in paging query methods returning Lists
Using List as return type for paginating methods didn't work for query methods currently. Fixed by inspecting the Pageable parameter potentially handed into them and restricting the result set accordingly.
2012-05-15 13:24:51 +02:00
Oliver Gierke
cb6a1b7110 DATAMONGO-429 - Fixed handling of nested arrays in QueryMapper.
QueryMapper now correctly transforms arrays not concreting them into BasicDBObjects anymore.
2012-04-16 15:22:00 +02:00
Oliver Gierke
ba5a764f5d DATAMONGO-423 - Fixed handling of negated regular expressions.
When using the not() method combined with the regex(…) methods on Criteria we created an invalid query so far. Fixed the regex(…) method to always transform the regex expressions and options into a Pattern instance and render that according to the $not state.
2012-04-16 15:22:00 +02:00
Oliver Gierke
3be35cba2d DATAMONGO-425 - Fixed parameter binding for Dates and manually defined queries.
Replaced manual JSON serialization for special parameters inside StringBasedMongoQuery by calling JSON.serialize(…).
2012-04-16 15:22:00 +02:00
Oliver Gierke
9421c45c5a DATAMONGO-181 - Improved resource handling for Mongo instance.
SimpleMongoDbFactory now only closes the Mongo instance if it created it itself. Removed public getter for WriteConcern and hold a UserCredentials instead of its parts.
2012-04-16 15:21:52 +02:00
Oliver Gierke
885c1b0f2c DATAMONGO-422 - Fixed invalid UUID conversion.
Removed UUIDToBinaryConverter and BinaryToUUIDConverter as the MongoDB Java driver can handle it itself. Added UUID as Mongo-simple type. Added integration test for reading and writing a UUID property.
2012-04-16 15:21:52 +02:00
Oliver Gierke
c8bb46ffb3 DATAMONGO-413 - Fixed bug in MongoQueryCreator.
MongoQueryCreator used the outdated OrQuery class to concatenate parts with OR. Refactored the class to use the Criteria.orOperator(…) method and deprecated the OrQuery class to be removed in the 1.1.x branch.
2012-04-16 15:21:51 +02:00
Oliver Gierke
f82de367c8 DATAMONGO-366 - Polished reference documentation.
Fixed link to bug tracker. Polished docbook files a bit.
2012-04-16 15:21:51 +02:00
Oliver Gierke
5e62675bae DATAMONGO-412 - Fixed duplicate invocation of getUserCredentials(). 2012-04-16 15:21:51 +02:00
Oliver Gierke
c805d9ccae DATAMONGO-273, DATAMONGO-294 - Re-enabled accidentally disabled test case. 2012-04-16 15:21:51 +02:00
Oliver Gierke
2d97288917 DATAMONGO-360 - Fixed index information creation for geo indexes.
Fixed a ClassCastException that occurred because we didn't consider index information of geo indexes (they return "2d" as direction). Introduced new IndexField abstraction that supersedes the fieldSpec Map in IndexInfo.
2012-04-16 15:21:51 +02:00
Oliver Gierke
e38448a569 DATAMONGO-382 - Fixed potential ClassCastException in MappingMongoConverter.
MappingMongoConverter's convertToMongoType(…) now deals with Sets (and more generally all Collections) correctly.
2012-04-16 15:21:50 +02:00
Oliver Gierke
b1065b8f2d DATAMONGO-408 - Added StringToWriteConverter for XML setup convenience.
When using a PropertyPlaceHolderConfigurer to set WriteConcerns on a MongoFactoryBean just like this:

<bean class="….mongodb.core.MongoFactoryBean">
  <property name="writeConcern" value="${mongodb.writeConcern}"/>
</bean>

we might create invalid WriteConcerns as the BeanFactory will use the WriteConcern's constructor taking a String to create the instance by default. To make Spring use the valueOf(…) method in advance one needs to register either our already existing WriteConcernPropertyEditor or the newly introduced StringToWriteConcernConverter in Springs ConversionService.
2012-04-16 15:21:50 +02:00
Oliver Gierke
8cac1d9368 DATAMONGO-411 - Double check type of PersistentEntity for index creation.
The Spring container does not check nested generic types of the type parameter of  ApplicationEvent<T>. As T is parameterized in our case as well (PersistentEntity<…, …>) we can code an event listener against that fully parameterized type but might run into ClassCastExceptions as we might get other implementations handed into the method at runtime. We now do an instanceof check to safely invoke checkForIndexes(…) only in case we get the correct event type.
2012-04-16 15:21:44 +02:00
Spring Buildmaster
7184950f8a Prepare next development version. 2012-02-11 06:41:56 -08:00
Spring Buildmaster
edd71cac78 Release version 1.0.1.RELEASE. 2012-02-11 06:41:53 -08:00
Oliver Gierke
82bd7a69eb DATAMONGO-395 - Updated changelog for 1.0.1. 2012-02-11 14:33:30 +01:00
Oliver Gierke
b434a0810e DATAMONGO-401 - Fixed NullPointerException in StringBasedMongoQuery. 2012-02-11 14:32:22 +01:00
Oliver Gierke
40236d4099 DATAMONGO-380 - Improved map handling for keys containing dots.
MappingMongoConverter now rejects objects that would result in field keys containing a dot as we cannot reliably escape and unescape them without potentially wrecking correct keys on reading. However I added a property mapKeyReplacement that can be set to e.g. ~ to have all dots in map keys replaced with ~. This will of course cause ~ to be transformed into dots when reading. If further customization is necessary override potentiallyEscapeMapKey(…) and potentiallyUnescapeMapKey(…).
2012-02-10 18:20:43 +01:00
Oliver Gierke
8f6d940036 DATAMONGO-397 - Replaced references to MongoTemplate with MongoOperations in repository package.
The reference in MongoRepositoryFactoryBean to MongoTemplate was unnecessary on the one hand and could cause problems in case the MongoTemplate is proxied as it can't be wired into the factory anymore then.
2012-02-10 17:11:27 +01:00
Oliver Gierke
95a92ccf5d DATAMONGO-395 - Removed invalid XML element. 2012-02-08 21:24:34 +01:00
Oliver Gierke
a6db24554f DATAMONGO-395 - Upgrade to Spring Data Commons 1.2.1. 2012-02-08 21:24:34 +01:00
Oliver Gierke
2f6c61ef9c DATAMONGO-395 - Polished pom.xml for Maven Central release. 2012-02-08 21:24:29 +01:00
Oliver Gierke
d8bf7ebf3f DATAMONGO-395 - Refer to latest core repository documentation. 2012-02-08 20:20:09 +01:00
Oliver Gierke
ce42783e73 DATACMNS-390 - Added UUIDToBinaryConverter to be able to handle UUIDs by default. 2012-02-06 15:16:24 +01:00
Oliver Gierke
69474327c6 DATAMONGO-358 - Fixed collection reading when property type is no a collection.
If you have a property of type object and it contains a collection we didn't property read it back in as creating the collection instance failed due to an invalid call to CollectionFactory. We now default the parameter handed to that call to List in case the property type is not a Collection at all.
2012-02-01 16:26:37 +01:00
Oliver Gierke
1bbe2e8247 DATAMONGO-385 - Added test case to show repositories working with Long id. 2012-02-01 15:30:02 +01:00
Oliver Gierke
94af898ae3 DATAMONGO-387 - Repository query execution for GeoPage results are now working correctly.
Added special handling of GeoPage return types for repository query methods.
2012-02-01 14:42:49 +01:00
Oliver Gierke
f6298f7005 DATAMONGO-375 - Polished XSD by removing unnecessary version numbers. 2012-02-01 10:00:19 +01:00
Oliver Gierke
d5b3c651b2 Consolidate Maven repository usage to use repo.springsource.org/libs-snapshot. 2012-01-31 18:47:52 +01:00
Oliver Gierke
33dd00f0b8 DATAMONGO-379 - Improved entity instantiation.
Huge refactoring of the way MappingMongoConverter instantiates entities. The constructor arguments now have to mirror a property exactly in terms of name. Thus we can pick up mapping information from the property to lookup the correct value from the source document. The @Value annotation can be used to either inject completely arbitrary values into the instance (e.g. by referring to a Spring bean) or simply define an expression against DBObject's fields:

class Sample {
  String foo;
  String bar;

  Sample(String foo, @Value("#root._bar") String bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

trying to create an instance of this class from

{ "foo" : "FOO" } -> new Sample("FOO", null)
{ "_bar" : "BAR" } -> new Sample(null, "BAR").
2012-01-16 18:37:00 +00:00
Oliver Gierke
3207a81555 DATAMONGO-368 - MappingMongoConverter does not remove null values from collections anymore. 2012-01-16 18:36:47 +00:00
Oliver Gierke
d231519012 DATAMONGO-376 - Fixed potential NPE in SpringDataMongodbSerializer. 2012-01-12 12:26:39 +01:00
Oliver Gierke
e052ecc9a4 Polished formatting and Javadoc. 2012-01-12 12:26:10 +01:00
Oliver Gierke
071f2934a1 DATAMONGO-369 - Fixed query mapping when a DBObject is included in the query object.
Replaced premature return with continue to break the for loop appropriately.
2012-01-12 11:20:16 +01:00
Oliver Gierke
d2a18e9b11 DATAMONGO-373 - Fixed potential ClassCastException in QueryMapper.
QueryMapper assumed finding a BasicBSONList for $(n)or operators. This is generally true if the DBObject was created through our Query abstraction. If you use the MongoDB driver QueryBuilder this will fail. We're now only insisting on an Iterable which fixes the issue.
2012-01-12 11:20:05 +01:00
Oliver Gierke
d684fa1f8e Fixed broken integration test. 2012-01-12 11:19:43 +01:00
Oliver Gierke
1a0077231d DATAMONGO-357 - Prepare 1.0.1 development branch. 2012-01-12 11:15:39 +01:00
Oliver Gierke
9cd33763b6 DATAMONGO-357 - Prepare 1.0.0 release.
Updated changelog, changed reference documentation inclusion links to point to SD Commons 1.0.0.RELEASE documentation. Updated dependency information in reference documentation.
2011-12-22 21:04:43 +01:00
Oliver Gierke
de8d2a1c74 DATAMONGO-355 - Upgraded to Spring 3.0.7. 2011-12-22 20:40:26 +01:00
Oliver Gierke
9f940cd2b6 DATAMONGO-257 - Documented TypeMapper abstraction. 2011-12-22 16:23:15 +01:00
Oliver Gierke
0d69baa32c DATAMONGO-350 - Upgraded to Querydsl 2.3.0.
Adapted changes in annotation processor API.
2011-12-22 08:27:11 +01:00
Oliver Gierke
433e5a660e DATAMONGO-260 - Fixed setup of ConversionService.
Remonde removal of generic ObjectToStringConverter as it doesn't break our converter lookup due to the changed algorithm how to involve Spring Converters in the conversion process.
2011-12-22 08:25:48 +01:00
Oliver Gierke
ee33ce1571 DATAMONGO-336 - Fixed potential NullPointerException in MongoTemplate.
The execution of MongoTemplate.geoNear(…) potentially caused NullPointerExceptions in case the actual query does not return any results. The wrapping return object returns null for the result list and general statistics which we didn't shield against.
2011-12-21 18:47:50 +01:00
Mark Pollack
9ac11e967a DATAMONGO-260 - MapReduce fails when using with Long as key-type. 2011-12-21 12:34:12 -05:00
Thomas Risberg
a89a0ac542 DATAMONGO-139 Adding test to verify that MongoTemplate does not eagerly try to connect to MongoDB 2011-12-20 12:44:10 -05:00
Oliver Gierke
0ad0dad124 DATAMONGO-330 - Updated reference documentation regarding custom converters.
Documented classpath scanning feature of custom converters (DATAMONGO-301). Documented converter disambiguation using @ReadingConverter, @WritingConverter (DATACMNS-113, DATAMONGO-342). Fixed some code formatting on the way.
2011-12-20 18:20:59 +01:00
Oliver Gierke
8f2771416e DATACMNS-110 - Narrow repository base-package reference to avoid picking up types from SD Commons. 2011-12-20 16:39:48 +01:00
Oliver Gierke
7af009cc7f DATAMONGO-347 - Added documentation to mention query parameter binding does not work with DBRefs. 2011-12-19 15:12:25 +01:00
Oliver Gierke
35ad949c18 DATAMONGO-326 - Fixed enum handling in $in, $nin and $ne criterias.
Updated Criteria implementation to consistently store collections instead of arrays internally.
2011-12-19 14:22:52 +01:00
Oliver Gierke
8206b5f950 DATAMONGO-342 - Introduced support for @ReadingConverter / @WritingConverter.
CustomConversions now evaluates @ReadingConverter / @WritingConverter when adding Converter implementations. See DATACMNS-113 and the appropriate commit for details. Added unit test to verify StringToBigIntegerConverter does not get added as writing converter.
2011-12-16 14:23:31 +01:00
Oliver Gierke
170081137a DATAMONGO-346 - Fixed id-handling in queries.
In case a query referencing an entity's id needs massaging before being executed (e.g. Strings that can be ObjectID) the massaging failed in case the query was built using _id already as we did not detect that one being an id reference as we compared it to the entity's id property name. We now also compare against it's field name.
2011-12-14 20:31:08 +01:00
Oliver Gierke
6862bd8a45 DATAMONGO-343 - Fixed registration of ServerAddressPropertyEditor.
Changed the setter parameters for ServerAddresses to use arrays instead of List. We now register the ServerAddressPropertyEditor to convert a ServerAddress[] and thus don't register a PropertyEditor for List which caused unwanted side effects before.
2011-12-12 18:03:27 +01:00
Oliver Gierke
490db7c39f DATAMONGO-341 - Eagerly reject null values in MongoTemplate.geoNear(…). 2011-12-07 18:06:25 +01:00
Oliver Gierke
f3979c3676 DATAMONGO-340 - Prepare 1.0.0.BUILD-SNAPSHOT. 2011-12-07 01:10:16 +01:00
Oliver Gierke
8d18729898 DATAMONGO-340 - Prepare 1.0.0.RC1. 2011-12-07 01:09:58 +01:00
Oliver Gierke
b5e0b2bec2 DATAMONGO-340 - Polished reference documentation.
Added section ids to generate stable URLs for HTML documentation.
2011-12-07 01:09:58 +01:00
Oliver Gierke
9eb47827c1 DATAMONGO-337 - Added Criteria.nin(…) and ….all(…) taking a Collection. 2011-12-07 01:09:57 +01:00
Oliver Gierke
f97ab25411 DATAMONGO-338 - Updated reference documentation regarding new keywords. 2011-12-07 01:09:57 +01:00
Oliver Gierke
6616761f50 DATAMONGO-322 - MongoTemplates refuses to save entities with unset id if not auto-generateable.
If an entity is handed into the template to be saved or inserted we now check that the auto-generated ObjectId can actually be applied to the id property after saving the object.
2011-12-07 01:09:57 +01:00
Mark Pollack
89de566893 Add findAndModify to docs, update test to include findAndModify with upsert 2011-12-06 13:04:24 -05:00
Mark Pollack
ea1f090b40 Add docs for index ops and clean up 'bare' references to Mongo, change to MongoDB 2011-12-06 11:09:23 -05:00
Oliver Gierke
b5958fb5cc DATAMONGO-338 - Query parser implementation go Regex, Exists, True and False keywords. 2011-12-06 17:01:56 +01:00
Oliver Gierke
75b7aff80a DATAMONGO-318 - Don't throw exceptions for updates not affecting any documents.
Throwing an exception if an update does not affect any documents doesn't make sense in all cases. Removed throwing an exception by default but made the relevant method (handleAnyWriteResultErrors(…)) protected so that subclasses might override this behavior.
2011-12-06 15:15:13 +01:00
Oliver Gierke
7da0fcdd0c DATAMONGO-199 - Fixed bug in CachingMongoPersistentProperty. 2011-12-06 14:48:25 +01:00
Oliver Gierke
c88b6d89db DATAMONGO-251 - Polishing.
JavaDoc, Formatting. Made dependencies in DefaultIndexOperations final. Reduced dependency to MongoOperations instead of depending on MongoTemplate directly. Added not-null assertion to constructor of DIO.
2011-12-06 14:33:45 +01:00
Oliver Gierke
de1540aadc DATAMONGO-234 - Polishing.
Removed unused imports, corrected whitespace, formatting.
2011-12-06 14:24:51 +01:00
Oliver Gierke
d1b24d6cfb DATAMONGO-332 - Updated reference documentation to list correct dependencies.
Fixed formatting of log output along the way.
2011-12-06 14:06:59 +01:00
Mark Pollack
e85f3d3299 DATAMONGO-251 - Support geting index information on a collection or mapped class. 2011-12-06 02:25:13 -05:00
Mark Pollack
ef6e08d3f4 DATAMONGO-234 - MongoTemplate should support the findAndModify operation to update version fields 2011-12-06 00:26:18 -05:00
Oliver Gierke
21010fbd49 DATACMNS-91 - Reject null parameters in SimpleMongoRepository.
According to the specification in CrudRepository we now reject null values for ids and entities in CRUD methods.
2011-12-02 13:34:44 +01:00
Oliver Gierke
4325d6c9fa Reactivated accidentally disabled unit tests. 2011-12-02 13:02:57 +01:00
Oliver Gierke
bc16ccfded DATACMNS-77 - Using constants from ClassTypeInformation inside MappingMongoConverter. 2011-12-02 11:33:37 +01:00
Oliver Gierke
04f5f9f662 DATACMNS-103 - Adapt changes in BeanWrapper.
Removed obsolete exception handling code.
2011-12-02 10:08:48 +01:00
Oliver Gierke
b1f1b8efaa DATAMONGO-321 - Overhaul of id handling.
Cleaned up the id handling on query mapping and mapping in general. We now only try to convert id values into an ObjectId and store it as is using potentially registered custom converters. Register BigInteger<->String converters by default now.
2011-12-01 17:50:36 +01:00
Oliver Gierke
de300e2643 DATAMONGO-328 - Set required MongoDB version to 0.
The MANIFEST.MF in current MongoDB driver version is broken in terms of not stating package versions. Thus we unfortunately cannot refer to a particular version range but have to use the generic 0 as required version.
2011-12-01 17:05:28 +01:00
Oliver Gierke
20088b83d9 Removed compiler warnings. 2011-12-01 16:47:40 +01:00
Oliver Gierke
58f200f15e DATAMONGO-335 - Set up hybrid Spring 3.1/3.0.6 build.
Also see DATACMNS-103.
2011-12-01 16:06:38 +01:00
Oliver Gierke
8718700249 DATAMONGO-334 - Switched to use http://repo.springsource.org as repository.
Fixed versions of build plugins along the way.
2011-12-01 16:05:08 +01:00
Oliver Gierke
f4063d1679 DATAMONGO-333 - Default to Object for AbstractMongoEventlistener domain type.
In case an extension of AbstractMongoEventListener does not define a parameter type we now default to Object as handled domain type as we'd cause a NullPointerException if not.
2011-12-01 12:16:27 +01:00
Oliver Gierke
ef063613c7 DATAMONGO-325 - MongoTemplate now correctly refuses not found map reduce JavaScript files.
We now check whether a URL was passed in as map and/or reduce function and throw an exception in case the file either does not exist or cannot be read.
2011-11-30 22:53:56 +01:00
Oliver Gierke
2eda0f1701 DATAMONGO-185 - Expose hints on Query.
Query now exposes a withHint(…) method which will be applied to the DBCursor on query execution. Reduced CursorPreparer's visibility to the package and removed methods exposing it from MongoOperations.
2011-11-30 22:29:59 +01:00
Oliver Gierke
ec7b65e21d DATAMONGO-331 - Fixed typo in WriteConcern enumeration for db-factory element. 2011-11-30 18:27:33 +01:00
Oliver Gierke
c7f7571f3f DATAMONGO-326 - QueryMapper now delegates type conversion to MongoConverter.
QueryMapper now delegates to a MongoConverter instead of a plain ConversionService and invokes optional conversion on it. This optional conversion now removes type information from the created DBObject.
2011-11-30 17:56:44 +01:00
Oliver Gierke
9f71af42e8 DATAMONGO-329 - Fixed Collection and Map value handling for more open properties.
The decision whether a property value was handled as Collection or Map was based on inspecting the property's type which failed for classes using very open property declarations such as:

class MyClass {

  Object something;
}

We now rather inspect the value type instead of the property.
2011-11-30 16:20:25 +01:00
Oliver Gierke
92775170e1 DATAMONGO-301 - Allow classpath-scanning for Converters.
<mongo:custom-conversions /> now has a base-package attribute that scans for Converter and GenericConverter beans. Added <tool:exports /> metadata for MappingMongoConverter.
2011-11-30 15:26:18 +01:00
Oliver Gierke
4c7e338770 Adapt refactorings in SD Commons. 2011-11-28 18:07:33 +01:00
Oliver Gierke
e3fff52d17 DATAMONGO-298 - CustomConversions now also considers sub-types of Number as simple.
CustomConversions now delegates to MongoSimpleTypes.HOLDER.isSimpleType(…) instead of maintaining an additional list of Mongo-primitive types. Added DBObject to the list of Mongo-primitive types.
2011-11-24 15:20:49 +01:00
Oliver Gierke
5477ab20b2 DATAMONGO-324 - Added shortcut in MappingMongoConverter to allow reading DBObjects without conversion.
Added check in MappingMongoConverter.read(…) to shortcut object conversion if the requested type is DBObject.
2011-11-24 12:59:47 +01:00
Oliver Gierke
4cf3567f42 DATAMONGO-310 - MappingMongoConverter now creates native Mongo types for Maps and Collections in convertToMongoType(…).
MappingMongoConverter.convertToMongoType(…) not only converts elements of collections and maps but also converts the wrapper into the appropriate MongoDB type (BasicDBList, BasicDBObject).
2011-11-23 13:15:50 +01:00
Oliver Gierke
b26bb62a63 DATAMONGO-305 - Removed synchronization from Query class.
As Query is not intended to be thread-safe at all, we can safely remove the synchronized blocks from sort() and fields().
2011-11-23 12:48:39 +01:00
Oliver Gierke
f156d7b5af DATAMONGO-312 - MappingMongoConverter handles complex enum types correctly.
If an Enum implements abstract methods, the Class object derived from ${ENUM}.getClass() does not return true for ….isEnum(). Thus we have to rather check Enum.class.isAssignableFrom(…) as this catches this scenario as well. Also see DATACMNS-99 for a related fix in simple type handling in the core infrastructure.
2011-11-23 11:58:09 +01:00
Oliver Gierke
7bf3643902 DATAMONGO-304 - Removed document subpackage from Log4jAppender module. 2011-11-23 11:07:47 +01:00
Oliver Gierke
201ae3e92d Polished unit test. 2011-11-23 10:58:35 +01:00
Oliver Gierke
07556ec58c DATAMONGO-323 - Annotated repository queries consider dynamic sort now.
Applying a Sort parameter handed into a repository query method now for string based (aka. @Query annotated) queries.
2011-11-23 10:58:22 +01:00
Oliver Gierke
39807b17e1 DATAMONGO-309 - MappingMongoConverter now correctly maps Arrays as Map values.
We now not only convert collection values of Maps into BasicDBLists but arrays as well.
2011-11-23 10:28:40 +01:00
Oliver Gierke
4913fe26ac DATAMONGO-296 - Hook into Querydsl serialization to get predicate parameters converted.
Overrode MongoDbSerializer.asDBObject(…) and delegate to our MongoConverter to potentially convert predicate parameters. Upgraded to Querydsl 2.2.5.
2011-11-23 10:15:46 +01:00
Oliver Gierke
7642a719ff Polishing.
Removed unused imports, removed compiler warnings, polished JavaDoc.
2011-11-23 09:26:19 +01:00
Oliver Gierke
6c1ce576a4 DATACMNS-98 - Reflect refactoring.
MongoQueryMethod now uses RepositoryMetadata.getReturnedDomainType(…) instead of the static method of ClassUtils.
2011-11-21 19:21:22 +01:00
Mark Pollack
c99882201d DATAMONGO-234 - MongoTemplate should support the findAndModify operation to update version fields 2011-11-17 17:38:54 -05:00
Mark Pollack
ce6a64e4a9 DATAMONGO-308 - Add support for upsert methods 2011-11-16 16:25:58 -05:00
Mark Pollack
2fcc323bcd DATAMONGO-213 - Add WriteConcern to arguments of MongoOperations.update*() methods 2011-11-16 14:55:35 -05:00
Mark Pollack
17c7b1d2b5 DATAMONGO-213 - Add WriteConcern to arguments of MongoOperations.update*() methods 2011-11-16 14:30:56 -05:00
Mark Pollack
cfefe46cd4 DATAMONGO-213 - Add WriteConcern to arguments of MongoOperations.update*() methods
DATAMONGO-320 - Remove use of slaveOk boolean option in MongoTemplate as it is deprecated. Replace with ReadPreference
2011-11-16 13:28:13 -05:00
Mark Pollack
64921ddad1 DATAMONGO-319 - WriteConcern not parsed correctly in namespace handlers
DATAMONGO-311 - Update MongoDB driver to v 2.7.x
2011-11-15 16:54:02 -05:00
Mark Pollack
edda1764fe DATAMONGO-311 - Update MongoDB driver to v 2.7.x
Still investigating write_concern compatiblity as mentioned in the ticket
2011-11-15 12:44:25 -05:00
Mark Pollack
8113b79109 DATAMONGO-315 - MongoTemplate.findOne(query) methods ignore SortOrder on query 2011-11-14 23:26:16 -05:00
Mark Pollack
9fde4dff3e DATAMONGO-195 - Add description of @Field mapping annotation to reference docs 2011-11-14 22:53:08 -05:00
Mark Pollack
d4b3e2b99d DATAMONGO-306 - NullPointerException if mongo factory created via URI with out credentials 2011-11-14 22:48:07 -05:00
Mark Pollack
68a31d75f3 DATAMONGO-313 [Refactoring] - Use MongoOperations interface instead of MongoTemplate class 2011-11-14 22:20:50 -05:00
Mark Pollack
3e15c21419 DATAMONGO-208 - Add suppoprt for group() operation on collection in MongoOperations 2011-11-14 22:08:29 -05:00
Mark Pollack
e9f253d34f DATAMONGO-316 - Replica Set configuration via properties file throws ArrayIndexOutOfBoundsException 2011-11-14 16:38:18 -05:00
Thomas Risberg
80aa057acb Preparing for snapshot builds 2011-11-04 09:24:45 -04:00
197 changed files with 8794 additions and 2625 deletions

75
pom.xml
View File

@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongo-dist</artifactId>
<artifactId>spring-data-mongodb-dist</artifactId>
<name>Spring Data MongoDB Distribution</name>
<version>1.0.0.M5</version>
<description>Spring Data project for MongoDB</description>
<url>http://www.springsource.org/spring-data/mongodb</url>
<version>1.0.5.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>spring-data-mongodb</module>
@@ -14,7 +15,18 @@
<module>spring-data-mongodb-parent</module>
</modules>
<developers>
<developers>
<developer>
<id>ogierke</id>
<name>Oliver Gierke</name>
<email>ogierke at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.springsource.com</organizationUrl>
<roles>
<role>Project lead</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>trisberg</id>
<name>Thomas Risberg</name>
@@ -39,17 +51,6 @@
</roles>
<timezone>-5</timezone>
</developer>
<developer>
<id>ogierke</id>
<name>Oliver Gierke</name>
<email>ogierke at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.springsource.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>jbrisbin</id>
<name>Jon Brisbin</name>
@@ -145,37 +146,35 @@
<htmlCustomization>${project.basedir}/src/docbkx/resources/xsl/html.xsl</htmlCustomization>
<useExtensions>1</useExtensions>
<highlightSource>1</highlightSource>
<highlightDefaultLanguage></highlightDefaultLanguage>
<highlightDefaultLanguage />
<!-- callouts -->
<entities>
<entity>
<name>version</name>
<value>${pom.version}</value>
<value>${project.version}</value>
</entity>
</entities>
<postProcess>
<copy todir="${project.basedir}/target/site/reference">
<fileset dir="${project.basedir}/target/docbkx">
<include name="**/*.html"/>
<include name="**/*.pdf"/>
<include name="**/*.html" />
<include name="**/*.pdf" />
</fileset>
</copy>
<copy todir="${project.basedir}/target/site/reference/html">
<fileset dir="${project.basedir}/src/docbkx/resources">
<include name="**/*.css"/>
<include name="**/*.png"/>
<include name="**/*.gif"/>
<include name="**/*.jpg"/>
<include name="**/*.css" />
<include name="**/*.png" />
<include name="**/*.gif" />
<include name="**/*.jpg" />
</fileset>
</copy>
<copy todir="${project.basedir}/target/site/reference/html">
<fileset dir="${project.basedir}/src/docbkx/resources/images">
<include name="*.png"/>
<include name="*.png" />
</fileset>
</copy>
<move file="${project.basedir}/target/site/reference/pdf/index.pdf"
tofile="${project.basedir}/target/site/reference/pdf/spring-data-mongo-reference.pdf"
failonerror="false"/>
<move file="${project.basedir}/target/site/reference/pdf/index.pdf" tofile="${project.basedir}/target/site/reference/pdf/spring-data-mongo-reference.pdf" failonerror="false" />
</postProcess>
</configuration>
</plugin>
@@ -184,7 +183,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.5</version>
<configuration>
<javadoc:aggregate>true</javadoc:aggregate>
<aggregate>true</aggregate>
<breakiterator>true</breakiterator>
<header>Spring Data Document</header>
<source>1.5</source>
@@ -234,7 +233,7 @@
<configuration>
<tasks>
<ant antfile="${basedir}/src/ant/upload-dist.xml">
<target name="upload-dist"/>
<target name="upload-dist" />
</ant>
</tasks>
</configuration>
@@ -270,9 +269,9 @@
<url>http://repository.springsource.com/maven/bundles/release</url>
</pluginRepository>
<pluginRepository>
<id>repository.springframework.maven.release</id>
<id>spring-libs-release</id>
<name>Spring Framework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
<url>http://repo.springsource.org/libs-release</url>
</pluginRepository>
</pluginRepositories>
@@ -282,13 +281,13 @@
<site>
<id>static.springframework.org</id>
<url>
scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/data-document/snapshot-site/
scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/data-mongodb/snapshot-site
</url>
</site>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>s3://maven.springframework.org/milestone</url>
<id>spring-release</id>
<name>Spring Release Repository</name>
<url>s3://maven.springframework.org/release</url>
</repository>
<snapshotRepository>
<id>spring-snapshot</id>
@@ -296,5 +295,7 @@
<url>s3://maven.springframework.org/snapshot</url>
</snapshotRepository>
</distributionManagement>
<scm>
<url>https://github.com/SpringSource/spring-data-mongodb</url>
</scm>
</project>

View File

@@ -1,10 +1,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.0.M5</version>
<version>1.0.5.BUILD-SNAPSHOT</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-cross-store</artifactId>
@@ -23,7 +23,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@@ -68,24 +68,6 @@
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
@@ -95,17 +77,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
@@ -151,25 +122,7 @@
</dependency>
</dependencies>
<repositories>
<repository>
<id>jboss-repository</id>
<name>JBoss Public Repository</name>
<url>http://repository.jboss.org/nexus/content/groups/public-jboss</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-maven-milestones</id>
<name>Springframework Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</pluginRepository>
<pluginRepository>
<id>spring-maven-release</id>
<name>Springframework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>

View File

@@ -5,7 +5,7 @@ and connects directly to the MongoDB server using the driver. It has no dependen
To use it, configure a host, port, (optionally) applicationId, and database property in your Log4J configuration:
log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout=org.springframework.data.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.stdout.host = localhost
@@ -32,7 +32,7 @@ An example log entry might look like:
{
"_id" : ObjectId("4d89341a8ef397e06940d5cd"),
"applicationId" : "my.application",
"name" : "org.springframework.data.document.mongodb.log4j.AppenderTest",
"name" : "org.springframework.data.mongodb.log4j.AppenderTest",
"level" : "DEBUG",
"timestamp" : ISODate("2011-03-23T16:53:46.778Z"),
"properties" : {

View File

@@ -1,11 +1,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.0.M5</version>
<version>1.0.5.BUILD-SNAPSHOT</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-log4j</artifactId>
@@ -28,47 +27,9 @@
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>compile</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.data.document.mongodb.log4j;
package org.springframework.data.mongodb.log4j;
import java.net.UnknownHostException;
import java.util.Arrays;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.data.document.mongodb.log4j;
package org.springframework.data.mongodb.log4j;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

View File

@@ -1,6 +1,6 @@
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout=org.springframework.data.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.stdout.host = localhost

View File

@@ -1,61 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<name>Spring Data MongoDB Parent</name>
<description>Spring Data project for MongoDB</description>
<url>http://www.springsource.org/spring-data/mongodb</url>
<version>1.0.0.M5</version>
<version>1.0.5.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- versions for commonly-used dependencies -->
<junit.version>4.8.1</junit.version>
<log4j.version>1.2.15</log4j.version>
<junit.version>4.10</junit.version>
<log4j.version>1.2.16</log4j.version>
<org.mockito.version>1.8.4</org.mockito.version>
<hamcrest.version>1.2.1</hamcrest.version>
<org.slf4j.version>1.5.10</org.slf4j.version>
<org.codehaus.jackson.version>1.6.1</org.codehaus.jackson.version>
<org.springframework.version>3.0.6.RELEASE</org.springframework.version>
<data.commons.version>1.2.0.M2</data.commons.version>
<org.springframework.version.30>3.0.7.RELEASE</org.springframework.version.30>
<org.springframework.version.40>4.0.0.RELEASE</org.springframework.version.40>
<org.springframework.version.range>[${org.springframework.version.30}, ${org.springframework.version.40})</org.springframework.version.range>
<data.commons.version>1.2.1.RELEASE</data.commons.version>
<aspectj.version>1.6.11.RELEASE</aspectj.version>
</properties>
<profiles>
<profile>
<id>strict</id>
<properties>
<maven.test.failure.ignore>false</maven.test.failure.ignore>
</properties>
</profile>
<profile>
<id>fast</id>
<properties>
<maven.test.skip>true</maven.test.skip>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
</profile>
<profile>
<id>staging</id>
<distributionManagement>
<site>
<id>spring-site-staging</id>
<url>file:///${java.io.tmpdir}/spring-data/mongodb/docs</url>
</site>
<repository>
<id>spring-milestone-staging</id>
<url>file:///${java.io.tmpdir}/spring-data/mongodb/milestone</url>
</repository>
<snapshotRepository>
<id>spring-snapshot-staging</id>
<url>file:///${java.io.tmpdir}/spring-data/mongodb/snapshot</url>
</snapshotRepository>
</distributionManagement>
</profile>
<profile>
<id>bootstrap</id>
<!-- TODO: move the repositories in here before release -->
</profile>
</profiles>
<developers>
<developer>
<id>ogierke</id>
<name>Oliver Gierke</name>
<email>ogierke at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.springsource.com</organizationUrl>
<roles>
<role>Project lead</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>trisberg</id>
<name>Thomas Risberg</name>
<email>trisberg at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.SpringSource.com</organizationUrl>
<roles>
<role>Project Admin</role>
<role>Developer</role>
</roles>
<timezone>-5</timezone>
</developer>
<developer>
<id>mpollack</id>
<name>Mark Pollack</name>
<email>mpollack at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.SpringSource.com</organizationUrl>
<roles>
<role>Project Admin</role>
<role>Developer</role>
</roles>
<timezone>-5</timezone>
</developer>
<developer>
<id>jbrisbin</id>
<name>Jon Brisbin</name>
<email>jbrisbin at vmware.com</email>
<organization>SpringSource</organization>
<organizationUrl>http://www.springsource.com</organizationUrl>
<roles>
<role>Developer</role>
</roles>
<timezone>-6</timezone>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<comments>
Copyright 2010 the original author or authors.
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.
</comments>
</license>
</licenses>
<distributionManagement>
<!-- see 'staging' profile for dry-run deployment settings -->
<downloadUrl>http://www.springsource.com/download/community
@@ -63,13 +103,13 @@
<site>
<id>static.springframework.org</id>
<url>
scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/mongodb/docs/${project.version}
scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/data-mongodb/snapshot-site
</url>
</site>
<repository>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>s3://maven.springframework.org/milestone</url>
<id>spring-release</id>
<name>Spring Release Repository</name>
<url>s3://maven.springframework.org/release</url>
</repository>
<snapshotRepository>
<id>spring-snapshot</id>
@@ -77,6 +117,9 @@
<url>s3://maven.springframework.org/snapshot</url>
</snapshotRepository>
</distributionManagement>
<scm>
<url>https://github.com/SpringSource/spring-data-mongodb</url>
</scm>
<dependencyManagement>
<!--
inheritable <dependency> declarations for child poms. children still
@@ -92,42 +135,47 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<version>${org.springframework.version.range}</version>
<scope>test</scope>
</dependency>
@@ -150,7 +198,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>${project.version}</version>
<version>1.0.5.BUILD-SNAPSHOT</version>
</dependency>
<!-- Logging -->
@@ -176,24 +224,6 @@
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
@@ -206,14 +236,14 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<artifactId>mockito-core</artifactId>
<version>${org.mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<artifactId>junit-dep</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
@@ -221,19 +251,34 @@
</dependencies>
</dependencyManagement>
<dependencies>
<!--
dependency definitions to be inherited by child poms. any
<dependency> declarations here will automatically show up on child
project classpaths. only items that are truly common across all
projects (modules and samples) should go here. otherwise, consider
<dependencyManagement> above
-->
<!-- Test dependencies -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@@ -248,47 +293,11 @@
<version>3.1.0.RELEASE</version>
</extension>
</extensions>
<resources>
<resource>
<directory>${project.basedir}/src/main/java</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/java</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
@@ -308,19 +317,18 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
<junitArtifactName>junit:junit</junitArtifactName>
<junitArtifactName>junit:junit-dep</junitArtifactName>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -367,32 +375,18 @@
</build>
<pluginRepositories>
<pluginRepository>
<!-- necessary for bundlor and utils -->
<id>repository.plugin.springsource.release</id>
<name>SpringSource Maven Repository</name>
<url>http://repository.springsource.com/maven/bundles/release</url>
<id>spring-plugins-release</id>
<url>http://repo.springsource.org/plugins-release</url>
</pluginRepository>
<pluginRepository>
<id>repository.springframework.maven.release</id>
<name>Spring Framework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
<id>querydsl</id>
<url>http://source.mysema.com/maven2/releases</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>repository.springframework.maven.release</id>
<name>Spring Framework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</repository>
<repository>
<id>repository.springframework.maven.milestone</id>
<name>Spring Framework Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
<repository>
<id>repository.springframework.maven.snapshot</id>
<name>Spring Framework Maven Snapshot Repository</name>
<url>http://maven.springframework.org/snapshot</url>
<id>spring-libs-release</id>
<url>http://repo.springsource.org/libs-release</url>
</repository>
</repositories>
<reporting>

View File

@@ -1,28 +1,39 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.0.M5</version>
<version>1.0.5.BUILD-SNAPSHOT</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb</artifactId>
<name>Spring Data MongoDB</name>
<properties>
<mongo.version>2.6.5</mongo.version>
<querydsl.version>2.2.4</querydsl.version>
<mongo.version>2.7.1</mongo.version>
<querydsl.version>2.3.0</querydsl.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
@@ -58,19 +69,6 @@
<artifactId>querydsl-mongodb</artifactId>
<version>${querydsl.version}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>com.google.code.morphia</groupId>
<artifactId>morphia</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -80,25 +78,6 @@
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
@@ -109,8 +88,8 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
@@ -128,6 +107,7 @@
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
@@ -138,7 +118,14 @@
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>maven-apt-plugin</artifactId>
<version>1.0.2</version>
<version>1.0.4</version>
<dependencies>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>generate-test-sources</phase>
@@ -154,15 +141,5 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>querydsl</id>
<name>Mysema QueryDsl</name>
<url>http://source.mysema.com/maven2/releases</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2011 the original author or authors.
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.data.mongodb.config;
import java.util.HashSet;
import java.util.Set;
import com.mongodb.Mongo;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -35,6 +34,14 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.mongodb.Mongo;
/**
* Abstract base class to ease JavaConfig setup for Spring Data MongoDB.
*
* @author Mark Pollack
* @author Oliver Gierke
*/
@Configuration
public abstract class AbstractMongoConfiguration {
@@ -50,10 +57,11 @@ public abstract class AbstractMongoConfiguration {
@Bean
public MongoDbFactory mongoDbFactory() throws Exception {
if (getUserCredentials() == null) {
UserCredentials credentials = getUserCredentials();
if (credentials == null) {
return new SimpleMongoDbFactory(mongo(), getDatabaseName());
} else {
return new SimpleMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials());
return new SimpleMongoDbFactory(mongo(), getDatabaseName(), credentials);
}
}

View File

@@ -18,6 +18,9 @@ package org.springframework.data.mongodb.config;
import static org.springframework.data.mongodb.config.BeanNames.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -36,13 +39,20 @@ import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@@ -133,15 +143,29 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
List<Element> customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters");
if (customConvertersElements.size() == 1) {
Element customerConvertersElement = customConvertersElements.get(0);
ManagedList<BeanMetadataElement> converterBeans = new ManagedList<BeanMetadataElement>();
List<Element> converterElements = DomUtils.getChildElementsByTagName(customerConvertersElement, "converter");
if (converterElements != null) {
for (Element listenerElement : converterElements) {
converterBeans.add(parseConverter(listenerElement, parserContext));
}
}
// Scan for Converter and GenericConverter beans in the given base-package
String packageToScan = customerConvertersElement.getAttribute(BASE_PACKAGE);
if (StringUtils.hasText(packageToScan)) {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.addExcludeFilter(new NegatingFilter(new AssignableTypeFilter(Converter.class),
new AssignableTypeFilter(GenericConverter.class)));
for (BeanDefinition candidate : provider.findCandidateComponents(packageToScan)) {
converterBeans.add(candidate);
}
}
BeanDefinitionBuilder conversionsBuilder = BeanDefinitionBuilder.rootBeanDefinition(CustomConversions.class);
conversionsBuilder.addConstructorArgValue(converterBeans);
@@ -194,4 +218,39 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
"Element <converter> must specify 'ref' or contain a bean definition for the converter", element);
return null;
}
/**
* {@link TypeFilter} that returns {@literal false} in case any of the given delegates matches.
*
* @author Oliver Gierke
*/
private static class NegatingFilter implements TypeFilter {
private final Set<TypeFilter> delegates;
/**
* Creates a new {@link NegatingFilter} with the given delegates.
*
* @param filters
*/
public NegatingFilter(TypeFilter... filters) {
Assert.notNull(filters);
this.delegates = new HashSet<TypeFilter>(Arrays.asList(filters));
}
/*
* (non-Javadoc)
* @see org.springframework.core.type.filter.TypeFilter#match(org.springframework.core.type.classreading.MetadataReader, org.springframework.core.type.classreading.MetadataReaderFactory)
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
for (TypeFilter delegate : delegates) {
if (delegate.match(metadataReader, metadataReaderFactory)) {
return false;
}
}
return true;
}
}
}

View File

@@ -56,7 +56,7 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
String uri = element.getAttribute("uri");
String mongoRef = element.getAttribute("mongo-ref");
String dbname = element.getAttribute("dbname");
@@ -65,20 +65,21 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
// Common setup
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class);
ParsingUtils.setPropertyValue(element, dbFactoryBuilder, "write-concern", "writeConcern");
if (StringUtils.hasText(uri)) {
if(StringUtils.hasText(mongoRef) || StringUtils.hasText(dbname) || userCredentials != null) {
parserContext.getReaderContext().error("Configure either Mongo URI or details individually!", parserContext.extractSource(element));
if (StringUtils.hasText(mongoRef) || StringUtils.hasText(dbname) || userCredentials != null) {
parserContext.getReaderContext().error("Configure either Mongo URI or details individually!",
parserContext.extractSource(element));
}
dbFactoryBuilder.addConstructorArgValue(getMongoUri(uri));
return getSourceBeanDefinition(dbFactoryBuilder, parserContext, element);
}
// Defaulting
mongoRef = StringUtils.hasText(mongoRef) ? mongoRef : registerMongoBeanDefinition(element, parserContext);
dbname = StringUtils.hasText(dbname) ? dbname : "db";
dbFactoryBuilder.addConstructorArgValue(new RuntimeBeanReference(mongoRef));
dbFactoryBuilder.addConstructorArgValue(dbname);
@@ -86,6 +87,8 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
dbFactoryBuilder.addConstructorArgValue(userCredentials);
}
ParsingUtils.registerWriteConcernPropertyEditor(parserContext.getRegistry());
return getSourceBeanDefinition(dbFactoryBuilder, parserContext, element);
}
@@ -128,7 +131,7 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
return getSourceBeanDefinition(userCredentialsBuilder, context, element);
}
/**
* Creates a {@link BeanDefinition} for a {@link MongoURI}.
*
@@ -136,10 +139,10 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
* @return
*/
private BeanDefinition getMongoUri(String uri) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoURI.class);
builder.addConstructorArgValue(uri);
return builder.getBeanDefinition();
}
}

View File

@@ -16,9 +16,15 @@
package org.springframework.data.mongodb.config;
import java.util.Map;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.mongodb.core.MongoFactoryBean;
@@ -26,7 +32,7 @@ import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for &lt;mongo;gt; definitions. If no name
* Parser for &lt;mongo;gt; definitions.
*
* @author Mark Pollack
*/
@@ -39,15 +45,35 @@ public class MongoParser extends AbstractSingleBeanDefinitionParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, builder);
ParsingUtils.setPropertyValue(element, builder, "port", "port");
ParsingUtils.setPropertyValue(element, builder, "host", "host");
ParsingUtils.setPropertyValue(element, builder, "write-concern", "writeConcern");
ParsingUtils.parseMongoOptions(parserContext, element, builder);
ParsingUtils.parseReplicaSet(parserContext, element, builder);
ParsingUtils.parseMongoOptions(element, builder);
ParsingUtils.parseReplicaSet(element, builder);
registerServerAddressPropertyEditor(parserContext.getRegistry());
ParsingUtils.registerWriteConcernPropertyEditor(parserContext.getRegistry());
}
/**
* One should only register one bean definition but want to have the convenience of using
* AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the
* container.
*
* @param parserContext the ParserContext to
*/
private void registerServerAddressPropertyEditor(BeanDefinitionRegistry registry) {
BeanDefinitionBuilder customEditorConfigurer = BeanDefinitionBuilder
.genericBeanDefinition(CustomEditorConfigurer.class);
Map<String, String> customEditors = new ManagedMap<String, String>();
customEditors.put("com.mongodb.ServerAddress[]",
"org.springframework.data.mongodb.config.ServerAddressPropertyEditor");
customEditorConfigurer.addPropertyValue("customEditors", customEditors);
BeanDefinitionReaderUtils.registerWithGeneratedName(customEditorConfigurer.getBeanDefinition(), registry);
}
@Override

View File

@@ -16,18 +16,22 @@
package org.springframework.data.mongodb.config;
import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.mongodb.core.MongoOptionsFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import com.mongodb.ServerAddress;
abstract class ParsingUtils {
/**
@@ -38,22 +42,11 @@ abstract class ParsingUtils {
* @param mongoBuilder the bean definition builder to populate
* @return true if parsing actually occured, false otherwise
*/
static boolean parseReplicaSet(ParserContext parserContext, Element element, BeanDefinitionBuilder mongoBuilder) {
static boolean parseReplicaSet(Element element, BeanDefinitionBuilder mongoBuilder) {
String replicaSetString = element.getAttribute("replica-set");
if (StringUtils.hasText(replicaSetString)) {
ManagedList<Object> serverAddresses = new ManagedList<Object>();
String[] replicaSetStringArray = StringUtils.commaDelimitedListToStringArray(replicaSetString);
for (String element2 : replicaSetStringArray) {
String[] hostAndPort = StringUtils.delimitedListToStringArray(element2, ":");
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(ServerAddress.class);
defBuilder.addConstructorArgValue(hostAndPort[0]);
defBuilder.addConstructorArgValue(hostAndPort[1]);
serverAddresses.add(defBuilder.getBeanDefinition());
}
if (!serverAddresses.isEmpty()) {
mongoBuilder.addPropertyValue("replicaSetSeeds", serverAddresses);
}
mongoBuilder.addPropertyValue("replicaSetSeeds", replicaSetString);
}
return true;
@@ -64,7 +57,7 @@ abstract class ParsingUtils {
*
* @return true if parsing actually occured, false otherwise
*/
static boolean parseMongoOptions(ParserContext parserContext, Element element, BeanDefinitionBuilder mongoBuilder) {
static boolean parseMongoOptions(Element element, BeanDefinitionBuilder mongoBuilder) {
Element optionsElement = DomUtils.getChildElementByTagName(element, "options");
if (optionsElement == null) {
return false;
@@ -122,9 +115,27 @@ abstract class ParsingUtils {
* @param element must not be {@literal null}.
* @return
*/
static AbstractBeanDefinition getSourceBeanDefinition(BeanDefinitionBuilder builder, ParserContext context, Element element) {
static AbstractBeanDefinition getSourceBeanDefinition(BeanDefinitionBuilder builder, ParserContext context,
Element element) {
AbstractBeanDefinition definition = builder.getBeanDefinition();
definition.setSource(context.extractSource(element));
return definition;
}
/**
* Registers a {@link WriteConcernPropertyEditor} in the given {@link BeanDefinitionRegistry}.
*
* @param registry must not be {@literal null}.
*/
static void registerWriteConcernPropertyEditor(BeanDefinitionRegistry registry) {
Assert.notNull(registry);
BeanDefinitionBuilder customEditorConfigurer = BeanDefinitionBuilder
.genericBeanDefinition(CustomEditorConfigurer.class);
Map<String, Class<?>> customEditors = new ManagedMap<String, Class<?>>();
customEditors.put("com.mongodb.WriteConcern", WriteConcernPropertyEditor.class);
customEditorConfigurer.addPropertyValue("customEditors", customEditors);
BeanDefinitionReaderUtils.registerWithGeneratedName(customEditorConfigurer.getBeanDefinition(), registry);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
import com.mongodb.ServerAddress;
/**
* Parse a {@link String} to a {@link ServerAddress} array. The format is host1:port1,host2:port2,host3:port3.
*
* @author Mark Pollack
* @author Oliver Gierke
*/
public class ServerAddressPropertyEditor extends PropertyEditorSupport {
private static final Log LOG = LogFactory.getLog(ServerAddressPropertyEditor.class);
/*
* (non-Javadoc)
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
@Override
public void setAsText(String replicaSetString) {
String[] replicaSetStringArray = StringUtils.commaDelimitedListToStringArray(replicaSetString);
Set<ServerAddress> serverAddresses = new HashSet<ServerAddress>(replicaSetStringArray.length);
for (String element : replicaSetStringArray) {
ServerAddress address = parseServerAddress(element);
if (address != null) {
serverAddresses.add(address);
}
}
if (serverAddresses.isEmpty()) {
throw new IllegalArgumentException(
"Could not resolve at least one server of the replica set configuration! Validate your config!");
}
setValue(serverAddresses.toArray(new ServerAddress[serverAddresses.size()]));
}
/**
* Parses the given source into a {@link ServerAddress}.
*
* @param source
* @return the
*/
private ServerAddress parseServerAddress(String source) {
String[] hostAndPort = StringUtils.delimitedListToStringArray(source.trim(), ":");
if (!StringUtils.hasText(source) || hostAndPort.length > 2) {
LOG.warn(String.format("Could not parse address source '%s'. Check your replica set configuration!", source));
return null;
}
try {
return hostAndPort.length == 1 ? new ServerAddress(hostAndPort[0]) : new ServerAddress(hostAndPort[0],
Integer.parseInt(hostAndPort[1]));
} catch (UnknownHostException e) {
LOG.warn(String.format("Could not parse host '%s'. Check your replica set configuration!", hostAndPort[0]));
} catch (NumberFormatException e) {
LOG.warn(String.format("Could not parse port '%s'. Check your replica set configuration!", hostAndPort[1]));
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.config;
import org.springframework.core.convert.converter.Converter;
import com.mongodb.WriteConcern;
/**
* Converter to create {@link WriteConcern} instances from String representations.
*
* @author Oliver Gierke
*/
public class StringToWriteConcernConverter implements Converter<String, WriteConcern> {
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
public WriteConcern convert(String source) {
WriteConcern writeConcern = WriteConcern.valueOf(source);
return writeConcern != null ? writeConcern : new WriteConcern(source);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import com.mongodb.WriteConcern;
/**
* Parse a string to a {@link WriteConcern}. If it is a well know {@link String} as identified by the
* {@link WriteConcern#valueOf(String)}, use the well known {@link WriteConcern} value, otherwise pass the string as is
* to the constructor of the write concern. There is no support for other constructor signatures when parsing from a
* string value.
*
* @author Mark Pollack
*/
public class WriteConcernPropertyEditor extends PropertyEditorSupport {
/**
* Parse a string to a List<ServerAddress>
*/
@Override
public void setAsText(String writeConcernString) {
WriteConcern writeConcern = WriteConcern.valueOf(writeConcernString);
if (writeConcern != null) {
// have a well known string
setValue(writeConcern);
} else {
// pass on the string to the constructor
setValue(new WriteConcern(writeConcernString));
}
}
}

View File

@@ -22,7 +22,7 @@ import com.mongodb.DBCursor;
*
* @author Oliver Gierke
*/
public interface CursorPreparer {
interface CursorPreparer {
/**
* Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor.

View File

@@ -0,0 +1,165 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.query.Order;
import org.springframework.util.Assert;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
/**
* Default implementation of {@link IndexOperations}.
*
* @author Mark Pollack
* @author Oliver Gierke
*/
public class DefaultIndexOperations implements IndexOperations {
private final MongoOperations mongoOperations;
private final String collectionName;
/**
* Creates a new {@link DefaultIndexOperations}.
*
* @param mongoOperations must not be {@literal null}.
* @param collectionName must not be {@literal null}.
*/
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName) {
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
Assert.notNull(collectionName, "Collection name can not be null!");
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
*/
public void ensureIndex(final IndexDefinition indexDefinition) {
mongoOperations.execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject indexOptions = indexDefinition.getIndexOptions();
if (indexOptions != null) {
collection.ensureIndex(indexDefinition.getIndexKeys(), indexOptions);
} else {
collection.ensureIndex(indexDefinition.getIndexKeys());
}
return null;
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#dropIndex(java.lang.String)
*/
public void dropIndex(final String name) {
mongoOperations.execute(collectionName, new CollectionCallback<Void>() {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
collection.dropIndex(name);
return null;
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#dropAllIndexes()
*/
public void dropAllIndexes() {
dropIndex("*");
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#resetIndexCache()
*/
public void resetIndexCache() {
mongoOperations.execute(collectionName, new CollectionCallback<Void>() {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
collection.resetIndexCache();
return null;
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#getIndexInfo()
*/
public List<IndexInfo> getIndexInfo() {
return mongoOperations.execute(collectionName, new CollectionCallback<List<IndexInfo>>() {
public List<IndexInfo> doInCollection(DBCollection collection) throws MongoException, DataAccessException {
List<DBObject> dbObjectList = collection.getIndexInfo();
return getIndexData(dbObjectList);
}
private List<IndexInfo> getIndexData(List<DBObject> dbObjectList) {
List<IndexInfo> indexInfoList = new ArrayList<IndexInfo>();
for (DBObject ix : dbObjectList) {
DBObject keyDbObject = (DBObject) ix.get("key");
int numberOfElements = keyDbObject.keySet().size();
Map<String, Order> keyOrderMap = new HashMap<String, Order>();
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
for (String key : keyDbObject.keySet()) {
Object value = keyDbObject.get(key);
if (Integer.valueOf(1).equals(value)) {
keyOrderMap.put(key, Order.ASCENDING);
indexFields.add(IndexField.create(key, Order.ASCENDING));
} else if (Integer.valueOf(-1).equals(value)) {
keyOrderMap.put(key, Order.DESCENDING);
indexFields.add(IndexField.create(key, Order.DESCENDING));
} else if ("2d".equals(value)) {
indexFields.add(IndexField.geo(key));
}
}
String name = ix.get("name").toString();
boolean unique = ix.containsField("unique") ? (Boolean) ix.get("unique") : false;
boolean dropDuplicates = ix.containsField("dropDups") ? (Boolean) ix.get("dropDups") : false;
boolean sparse = ix.containsField("sparse") ? (Boolean) ix.get("sparse") : false;
indexInfoList.add(new IndexInfo(keyOrderMap, indexFields, name, unique, dropDuplicates, sparse));
}
return indexInfoList;
}
});
}
}

View File

@@ -21,15 +21,16 @@ import com.mongodb.DBObject;
import com.mongodb.MongoException;
/**
* An interface used by {@link MongoTemplate} for processing documents returned from a MongoDB query on a per-document basis.
* Implementations of this interface perform the actual work of prcoessing each document but don't need to worry about
* exception handling. {@MongoException}s will be caught and translated by the calling MongoTemplate
* An interface used by {@link MongoTemplate} for processing documents returned from a MongoDB query on a per-document
* basis. Implementations of this interface perform the actual work of prcoessing each document but don't need to worry
* about exception handling. {@MongoException}s will be caught and translated by the calling
* MongoTemplate
*
* An DocumentCallbackHandler is typically stateful: It keeps the result state within the object, to be available later for later
* inspection.
* An DocumentCallbackHandler is typically stateful: It keeps the result state within the object, to be available later
* for later inspection.
*
* @author Mark Pollack
*
*
*/
public interface DocumentCallbackHandler {

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2010-2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core;
public class FindAndModifyOptions {
boolean returnNew;
boolean upsert;
boolean remove;
/**
* Static factory method to create a FindAndModifyOptions instance
*
* @return a new instance
*/
public static FindAndModifyOptions options() {
return new FindAndModifyOptions();
}
public FindAndModifyOptions returnNew(boolean returnNew) {
this.returnNew = returnNew;
return this;
}
public FindAndModifyOptions upsert(boolean upsert) {
this.upsert = upsert;
return this;
}
public FindAndModifyOptions remove(boolean remove) {
this.remove = remove;
return this;
}
public boolean isReturnNew() {
return returnNew;
}
public boolean isUpsert() {
return upsert;
}
public boolean isRemove() {
return remove;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core;
import java.util.List;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
/**
* Index operations on a collection.
*
* @author Mark Pollack
* @author Oliver Gierke
*/
public interface IndexOperations {
/**
* Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity
* class. If not it will be created.
*
* @param indexDefinition must not be {@literal null}.
*/
void ensureIndex(IndexDefinition indexDefinition);
/**
* Drops an index from this collection.
*
* @param name name of index to drop
*/
void dropIndex(String name);
/**
* Drops all indices from this collection.
*/
void dropAllIndexes();
/**
* Clears all indices that have not yet been applied to this collection.
*/
void resetIndexCache();
/**
* Returns the index information on the collection.
*
* @return index information on the collection
*/
List<IndexInfo> getIndexInfo();
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;
/**
* Represents an action taken against the collection. Used by {@link WriteConcernResolver} to determine a custom
* WriteConcern based on this information.
*
* Properties that will always be not-null are collectionName and defaultWriteConcern. The EntityClass is null only for
* the MongoActionOperaton.INSERT_LIST.
*
* <ul>
* <li>INSERT, SAVE have null query</li>
* <li>REMOVE has null document</li>
* <li>INSERT_LIST has null entityClass, document, and query</li>
* </ul>
*
* @author Mark Pollack
*
*/
public class MongoAction {
private String collectionName;
private WriteConcern defaultWriteConcern;
private Class<?> entityClass;
private MongoActionOperation mongoActionOperation;
private DBObject query;
private DBObject document;
/**
* Create an instance of a MongoAction
*
* @param defaultWriteConcern the default write concern
* @param mongoActionOperation action being taken against the collection
* @param collectionName the collection name
* @param entityClass the POJO that is being operated against
* @param document the converted DBObject from the POJO or Spring Update object
* @param query the converted DBOjbect from the Spring Query object
*/
public MongoAction(WriteConcern defaultWriteConcern, MongoActionOperation mongoActionOperation,
String collectionName, Class<?> entityClass, DBObject document, DBObject query) {
super();
this.defaultWriteConcern = defaultWriteConcern;
this.mongoActionOperation = mongoActionOperation;
this.collectionName = collectionName;
this.entityClass = entityClass;
this.query = query;
this.document = document;
}
public String getCollectionName() {
return collectionName;
}
public WriteConcern getDefaultWriteConcern() {
return defaultWriteConcern;
}
public Class<?> getEntityClass() {
return entityClass;
}
public MongoActionOperation getMongoActionOperation() {
return mongoActionOperation;
}
public DBObject getQuery() {
return query;
}
public DBObject getDocument() {
return document;
}
}

View File

@@ -13,21 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import org.springframework.data.convert.TypeMapper;
package org.springframework.data.mongodb.core;
/**
* Interfaces for components being able to provide a {@link TypeMapper}.
* Enumeration for operations on a collection. Used with {@link MongoAction} to help determine the WriteConcern to use
* for a given mutating operation
*
* @author Mark Pollack
* @see MongoAction
*
* @author Oliver Gierke
*/
public interface TypeKeyAware {
public enum MongoActionOperation {
/**
* Returns the {@link TypeMapper}.
*
* @return the {@link TypeMapper} or {@literal null} if none available.
*/
boolean isTypeKey(String key);
REMOVE, UPDATE, INSERT, INSERT_LIST, SAVE
}

View File

@@ -15,14 +15,15 @@
*/
package org.springframework.data.mongodb.core;
import com.mongodb.DB;
import com.mongodb.Mongo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import com.mongodb.DB;
import com.mongodb.Mongo;
/**
* Helper class featuring helper methods for internal MongoDb classes.
* <p/>
@@ -78,7 +79,7 @@ public abstract class MongoDbUtils {
DB db = null;
if (TransactionSynchronizationManager.isSynchronizationActive() && dbHolder.doesNotHoldNonDefaultDB()) {
// Spring transaction management is active ->
db = dbHolder.getDB();
db = dbHolder.getDB(databaseName);
if (db != null && !dbHolder.isSynchronizedWithTransaction()) {
LOGGER.debug("Registering Spring transaction synchronization for existing Mongo DB");
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(dbHolder, mongo));
@@ -96,10 +97,12 @@ public abstract class MongoDbUtils {
boolean credentialsGiven = username != null && password != null;
if (credentialsGiven && !db.isAuthenticated()) {
// Note, can only authenticate once against the same com.mongodb.DB object.
if (!db.authenticate(username, password)) {
throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName
+ "], username = [" + username + "], password = [" + new String(password) + "]", databaseName, username,
password);
synchronized (db) {
if (!db.authenticate(username, password)) {
throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName
+ "], username = [" + username + "], password = [" + new String(password) + "]", databaseName, username,
password);
}
}
}
@@ -110,9 +113,9 @@ public abstract class MongoDbUtils {
LOGGER.debug("Registering Spring transaction synchronization for new Hibernate Session");
DbHolder holderToUse = dbHolder;
if (holderToUse == null) {
holderToUse = new DbHolder(db);
holderToUse = new DbHolder(databaseName, db);
} else {
holderToUse.addDB(db);
holderToUse.addDB(databaseName, db);
}
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
holderToUse.setSynchronizedWithTransaction(true);
@@ -143,7 +146,7 @@ public abstract class MongoDbUtils {
return false;
}
DbHolder dbHolder = (DbHolder) TransactionSynchronizationManager.getResource(mongo);
return (dbHolder != null && dbHolder.containsDB(db));
return dbHolder != null && dbHolder.containsDB(db);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2011 the original author or authors.
* Copyright 2010-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,17 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
import com.mongodb.Mongo;
import com.mongodb.MongoOptions;
import com.mongodb.ServerAddress;
@@ -37,12 +38,10 @@ import com.mongodb.WriteConcern;
* @author Oliver Gierke
* @since 1.0
*/
public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptionTranslator {
public class MongoFactoryBean implements FactoryBean<Mongo>, InitializingBean, DisposableBean,
PersistenceExceptionTranslator {
/**
* Logger, available to subclasses.
*/
protected final Log logger = LogFactory.getLog(getClass());
private Mongo mongo;
private MongoOptions mongoOptions;
private String host;
@@ -57,12 +56,12 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptio
this.mongoOptions = mongoOptions;
}
public void setReplicaSetSeeds(List<ServerAddress> replicaSetSeeds) {
this.replicaSetSeeds = replicaSetSeeds;
public void setReplicaSetSeeds(ServerAddress[] replicaSetSeeds) {
this.replicaSetSeeds = Arrays.asList(replicaSetSeeds);
}
public void setReplicaPair(List<ServerAddress> replicaPair) {
this.replicaPair = replicaPair;
public void setReplicaPair(ServerAddress[] replicaPair) {
this.replicaPair = Arrays.asList(replicaPair);
}
public void setHost(String host) {
@@ -87,34 +86,7 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptio
}
public Mongo getObject() throws Exception {
Mongo mongo;
ServerAddress defaultOptions = new ServerAddress();
if (mongoOptions == null) {
mongoOptions = new MongoOptions();
}
if (replicaPair != null) {
if (replicaPair.size() < 2) {
throw new CannotGetMongoDbConnectionException("A replica pair must have two server entries");
}
mongo = new Mongo(replicaPair.get(0), replicaPair.get(1), mongoOptions);
} else if (replicaSetSeeds != null) {
mongo = new Mongo(replicaSetSeeds, mongoOptions);
} else {
String mongoHost = host != null ? host : defaultOptions.getHost();
mongo = port != null ? new Mongo(new ServerAddress(mongoHost, port), mongoOptions) : new Mongo(mongoHost,
mongoOptions);
}
if (writeConcern != null) {
mongo.setWriteConcern(writeConcern);
}
return mongo;
}
/*
@@ -140,4 +112,45 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptio
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Mongo mongo;
ServerAddress defaultOptions = new ServerAddress();
if (mongoOptions == null) {
mongoOptions = new MongoOptions();
}
if (replicaPair != null) {
if (replicaPair.size() < 2) {
throw new CannotGetMongoDbConnectionException("A replica pair must have two server entries");
}
mongo = new Mongo(replicaPair.get(0), replicaPair.get(1), mongoOptions);
} else if (replicaSetSeeds != null) {
mongo = new Mongo(replicaSetSeeds, mongoOptions);
} else {
String mongoHost = host != null ? host : defaultOptions.getHost();
mongo = port != null ? new Mongo(new ServerAddress(mongoHost, port), mongoOptions) : new Mongo(mongoHost,
mongoOptions);
}
if (writeConcern != null) {
mongo.setWriteConcern(writeConcern);
}
this.mongo = mongo;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
this.mongo.close();
}
}

View File

@@ -19,21 +19,24 @@ import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.GeoResult;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import com.mongodb.CommandResult;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.WriteResult;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.GeoResult;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
/**
* Interface that specifies a basic set of MongoDB operations. Implemented by {@link MongoTemplate}. Not often used but
* a useful option for extensibility and testability (as it can be easily mocked, stubbed, or be the target of a JDK
@@ -48,6 +51,7 @@ public interface MongoOperations {
/**
* The collection name used for the specified class by this template.
*
* @param entityClass must not be {@literal null}.
* @return
*/
String getCollectionName(Class<?> entityClass);
@@ -68,7 +72,7 @@ public interface MongoOperations {
* @param command a MongoDB command
*/
CommandResult executeCommand(DBObject command);
/**
* Execute a MongoDB command. Any errors that result from executing this command will be converted into Spring's DAO
* exception hierarchy.
@@ -77,7 +81,7 @@ public interface MongoOperations {
* @param options query options to use
*/
CommandResult executeCommand(DBObject command, int options);
/**
* Execute a MongoDB query and iterate over the query results on a per-document basis with a DocumentCallbackHandler.
*
@@ -87,18 +91,6 @@ public interface MongoOperations {
* @param dch the handler that will extract results, one document at a time
*/
void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch);
/**
* Execute a MongoDB query and iterate over the query results on a per-document basis with a 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
* @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 DBCursor used when iterating over the result set, (apply limits,
* skips and so on).
*/
void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch, CursorPreparer preparer);
/**
* Executes a {@link DbCallback} translating any exceptions as necessary.
@@ -237,6 +229,20 @@ public interface MongoOperations {
*/
void dropCollection(String collectionName);
/**
* Returns the operations that can be performed on indexes
*
* @return index operations on the named collection
*/
IndexOperations indexOps(String collectionName);
/**
* Returns the operations that can be performed on indexes
*
* @return index operations on the named collection associated with the given entity class
*/
IndexOperations indexOps(Class<?> entityClass);
/**
* Query for a list of objects of type T from the collection used by the entity class.
* <p/>
@@ -265,11 +271,39 @@ public interface MongoOperations {
* @return the converted collection
*/
<T> List<T> findAll(Class<T> entityClass, String collectionName);
/**
* Execute a map-reduce operation. The map-reduce operation will be formed with an output type of INLINE
* Execute a group operation over the entire collection. The group operation entity class should match the 'shape' of
* the returned object that takes int account the initial document structure as well as any finalize functions.
*
* @param criteria The criteria that restricts the row that are considered for grouping. If not specified all rows are
* considered.
* @param inputCollectionName the collection where the group operation will read from
* @param groupBy the conditions under which the group operation will be performed, e.g. keys, initial document,
* reduce function.
* @param entityClass The parameterized type of the returned list
* @return The results of the group operation
*/
<T> GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> entityClass);
/**
* Execute a group operation restricting the rows to those which match the provided Criteria. The group operation
* entity class should match the 'shape' of the returned object that takes int account the initial document structure
* as well as any finalize functions.
*
* @param criteria The criteria that restricts the row that are considered for grouping. If not specified all rows are
* considered.
* @param inputCollectionName the collection where the group operation will read from
* @param groupBy the conditions under which the group operation will be performed, e.g. keys, initial document,
* reduce function.
* @param entityClass The parameterized type of the returned list
* @return The results of the group operation
*/
<T> GroupByResults<T> group(Criteria criteria, String inputCollectionName, GroupBy groupBy, Class<T> entityClass);
/**
* Execute a map-reduce operation. The map-reduce operation will be formed with an output type of INLINE
*
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
* @param reduceFunction The JavaScript reduce function
@@ -277,11 +311,12 @@ public interface MongoOperations {
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction, Class<T> entityClass );
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,
Class<T> entityClass);
/**
* Execute a map-reduce operation that takes additional map-reduce options.
*
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
* @param reduceFunction The JavaScript reduce function
@@ -289,12 +324,13 @@ public interface MongoOperations {
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction, MapReduceOptions mapReduceOptions, Class<T> entityClass );
<T> MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,
MapReduceOptions mapReduceOptions, Class<T> entityClass);
/**
* Execute a map-reduce operation that takes a query. The map-reduce operation will be formed with an output type of INLINE
* Execute a map-reduce operation that takes a query. The map-reduce operation will be formed with an output type of
* INLINE
*
* @param query The query to use to select the data for the map phase
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
@@ -303,10 +339,12 @@ public interface MongoOperations {
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction, Class<T> entityClass );
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction,
Class<T> entityClass);
/**
* Execute a map-reduce operation that takes a query and additional map-reduce options
*
* @param query The query to use to select the data for the map phase
* @param inputCollectionName the collection where the map-reduce will read from
* @param mapFunction The JavaScript map function
@@ -315,7 +353,8 @@ public interface MongoOperations {
* @param entityClass The parameterized type of the returned list
* @return The results of the map reduce operation
*/
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction, MapReduceOptions mapReduceOptions, Class<T> entityClass );
<T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, String mapFunction, String reduceFunction,
MapReduceOptions mapReduceOptions, Class<T> entityClass);
/**
* Returns {@link GeoResult} for all entities matching the given {@link NearQuery}. Will consider entity mapping
@@ -338,23 +377,6 @@ public interface MongoOperations {
*/
<T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass, String collectionName);
/**
* Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity
* class. If not it will be created.
*
* @param indexDefinition
* @param entityClass class that determines the collection to use
*/
void ensureIndex(IndexDefinition indexDefinition, Class<?> entityClass);
/**
* Ensure that an index for the provided {@link IndexDefinition} exists. If not it will be created.
*
* @param index
* @param collectionName
*/
void ensureIndex(IndexDefinition indexDefinition, String collectionName);
/**
* Map the results of an ad-hoc query on the collection for the entity class to a single instance of an object of the
* specified type.
@@ -425,26 +447,6 @@ public interface MongoOperations {
*/
<T> List<T> find(Query query, Class<T> entityClass, String collectionName);
/**
* Map the results of an ad-hoc query on the specified collection to a List of the specified type.
* <p/>
* The object is converted from the MongoDB native representation using an instance of {@see MongoConverter}. Unless
* configured otherwise, an instance of SimpleMongoConverter will be used.
* <p/>
* The query is specified as a {@link Query} which can be created either using the {@link BasicQuery} or the more
* feature rich {@link Query}.
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification
* @param entityClass the parameterized type of the returned list.
* @param preparer allows for customization of the DBCursor used when iterating over the result set, (apply limits,
* skips and so on).
* @param collectionName name of the collection to retrieve the objects from
*
* @return the List of converted objects.
*/
<T> List<T> find(Query query, Class<T> entityClass, CursorPreparer preparer, String collectionName);
/**
* Returns a document with the given id mapped onto the given class. The collection the query is ran against will be
* derived from the given target class as well.
@@ -468,6 +470,15 @@ public interface MongoOperations {
*/
<T> T findById(Object id, Class<T> entityClass, String collectionName);
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
String collectionName);
/**
* Map the results of an ad-hoc query on the collection for the entity type to a single instance of an object of the
* specified type. The first document that matches the query is returned and also removed from the collection in the
@@ -512,7 +523,7 @@ public interface MongoOperations {
* @return
*/
long count(Query query, Class<?> entityClass);
/**
* Returns the number of documents for the given {@link Query} querying the given collection.
*
@@ -521,7 +532,7 @@ public interface MongoOperations {
* @return
*/
long count(Query query, String collectionName);
/**
* Insert the object into the collection for the entity type of the object to save.
* <p/>
@@ -612,6 +623,29 @@ public interface MongoOperations {
*/
void save(Object objectToSave, String collectionName);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
*
* @param query the query document that specifies the criteria used to select a record to be upserted
* @param update the update document that contains the updated object or $ operators to manipulate the existing object
* @param entityClass class that determines the collection to use
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult upsert(Query query, Update update, Class<?> entityClass);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
*
* @param query the query document that specifies the criteria used to select a record to be updated
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult upsert(Query query, Update update, String collectionName);
/**
* Updates the first object that is found in the collection of the entity class that matches the query document with
* the provided update document.
@@ -620,6 +654,7 @@ public interface MongoOperations {
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param entityClass class that determines the collection to use
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateFirst(Query query, Update update, Class<?> entityClass);
@@ -631,6 +666,7 @@ public interface MongoOperations {
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateFirst(Query query, Update update, String collectionName);
@@ -642,6 +678,7 @@ public interface MongoOperations {
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param entityClass class that determines the collection to use
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateMulti(Query query, Update update, Class<?> entityClass);
@@ -653,6 +690,7 @@ public interface MongoOperations {
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object.
* @param collectionName name of the collection to update the object in
* @return the WriteResult which lets you access the results of the previous write.
*/
WriteResult updateMulti(Query query, Update update, String collectionName);
@@ -662,7 +700,7 @@ public interface MongoOperations {
* @param object
*/
void remove(Object object);
/**
* Removes the given object from the given collection.
*

View File

@@ -96,6 +96,7 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
*
* Defaults to false
*/
@SuppressWarnings("deprecation")
private boolean slaveOk = MONGO_OPTIONS.slaveOk;
/**
@@ -204,6 +205,7 @@ public class MongoOptionsFactoryBean implements FactoryBean<MongoOptions>, Initi
this.slaveOk = slaveOk;
}
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
MONGO_OPTIONS.connectionsPerHost = connectionsPerHost;
MONGO_OPTIONS.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;

View File

@@ -1,13 +1,11 @@
/*
* Copyright 2010-2011 the original author or authors.
* Copyright 2010-2012 the original author or authors.
*
* Licensed under t
import org.springframework.data.convert.EntityReader;
he Apache License, Version 2.0 (the "License");
* 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
* 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,
@@ -18,7 +16,9 @@ he Apache License, Version 2.0 (the "License");
package org.springframework.data.mongodb.core;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
@@ -30,20 +30,6 @@ import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MapReduceCommand;
import com.mongodb.MapReduceOutput;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.ObjectId;
@@ -61,6 +47,7 @@ import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.convert.EntityReader;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.MappingException;
@@ -72,27 +59,46 @@ import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.GeoResult;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.geo.Metric;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent;
import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.jca.cci.core.ConnectionCallback;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MapReduceCommand;
import com.mongodb.MapReduceOutput;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
/**
* Primary implementation of {@link MongoOperations}.
*
@@ -100,6 +106,7 @@ import org.springframework.util.StringUtils;
* @author Graeme Rocher
* @author Mark Pollack
* @author Oliver Gierke
* @author Amol Nayak
*/
public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -122,16 +129,18 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
*/
private WriteConcern writeConcern = null;
private WriteConcernResolver writeConcernResolver = new DefaultWriteConcernResolver();
/*
* WriteResultChecking to be used for write operations if it has been
* specified. Otherwise we should not do any checking.
*/
private WriteResultChecking writeResultChecking = WriteResultChecking.NONE;
/*
* Flag used to indicate use of slaveOk() for any operations on collections.
/**
* Set the ReadPreference when operating on a collection. See {@link #prepareCollection(DBCollection)}
*/
private boolean slaveOk = false;
private ReadPreference readPreference = null;
private final MongoConverter mongoConverter;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
@@ -181,11 +190,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();
@@ -220,12 +230,22 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
/**
* TODO: document properly
* Configures the {@link WriteConcernResolver} to be used with the template.
*
* @param slaveOk
* @param writeConcernResolver
*/
public void setSlaveOk(boolean slaveOk) {
this.slaveOk = slaveOk;
public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) {
this.writeConcernResolver = writeConcernResolver;
}
/**
* Used by @{link {@link #prepareCollection(DBCollection)} to set the {@link ReadPreference} before any operations are
* performed.
*
* @param readPreference
*/
public void setReadPreference(ReadPreference readPreference) {
this.readPreference = readPreference;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
@@ -293,16 +313,33 @@ 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 sortObject = query.getSortObject();
DBObject fieldsObject = query.getFieldsObject();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("find using query: " + queryObject + " fields: " + fieldsObject + " in collection: "
+ collectionName);
LOGGER.debug(String.format("Executing query: %s sort: %s fields: %s in collection: $s",
serializeToJsonSafely(queryObject), sortObject, fieldsObject, collectionName));
}
this.executeQueryInternal(new FindCallback(queryObject, fieldsObject), preparer, dch, collectionName);
}
@@ -399,24 +436,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
});
}
// Indexing methods
public void ensureIndex(IndexDefinition indexDefinition, Class<?> entityClass) {
ensureIndex(indexDefinition, determineCollectionName(entityClass));
public IndexOperations indexOps(String collectionName) {
return new DefaultIndexOperations(this, collectionName);
}
public void ensureIndex(final IndexDefinition indexDefinition, String collectionName) {
execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject indexOptions = indexDefinition.getIndexOptions();
if (indexOptions != null) {
collection.ensureIndex(indexDefinition.getIndexKeys(), indexOptions);
} else {
collection.ensureIndex(indexDefinition.getIndexKeys());
}
return null;
}
});
public IndexOperations indexOps(Class<?> entityClass) {
return new DefaultIndexOperations(this, determineCollectionName(entityClass));
}
// Find methods that take a Query to express the query and that return a single object.
@@ -426,7 +451,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public <T> T findOne(Query query, Class<T> entityClass, String collectionName) {
return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass);
if (query.getSortObject() == null) {
return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass);
} else {
query.limit(1);
List<T> results = find(query, entityClass, collectionName);
return results.isEmpty() ? null : results.get(0);
}
}
// Find methods that take a Query to express the query and that return a List of objects.
@@ -436,36 +467,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());
@@ -482,14 +487,25 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return geoNear(near, entityClass, determineCollectionName(entityClass));
}
@SuppressWarnings("unchecked")
public <T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass, String collectionName) {
if (near == null) {
throw new InvalidDataAccessApiUsageException("NearQuery must not be null!");
}
if (entityClass == null) {
throw new InvalidDataAccessApiUsageException("Entity class must not be null!");
}
String collection = StringUtils.hasText(collectionName) ? collectionName : determineCollectionName(entityClass);
BasicDBObject command = new BasicDBObject("geoNear", collection);
command.putAll(near.toDBObject());
CommandResult commandResult = executeCommand(command);
BasicDBList results = (BasicDBList) commandResult.get("results");
List<Object> results = (List<Object>) commandResult.get("results");
results = results == null ? Collections.emptyList() : results;
DbObjectCallback<GeoResult<T>> callback = new GeoNearResultDbObjectCallback<T>(new ReadDbObjectCallback<T>(
mongoConverter, entityClass), near.getMetric());
List<GeoResult<T>> result = new ArrayList<GeoResult<T>>(results.size());
@@ -498,10 +514,29 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
result.add(callback.doWith((DBObject) element));
}
double averageDistance = (Double) ((DBObject) commandResult.get("stats")).get("avgDistance");
DBObject stats = (DBObject) commandResult.get("stats");
double averageDistance = stats == null ? 0 : (Double) stats.get("avgDistance");
return new GeoResults<T>(result, new Distance(averageDistance, near.getMetric()));
}
public <T> T findAndModify(Query query, Update update, Class<T> entityClass) {
return findAndModify(query, update, new FindAndModifyOptions(), entityClass, determineCollectionName(entityClass));
}
public <T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName) {
return findAndModify(query, update, new FindAndModifyOptions(), entityClass, collectionName);
}
public <T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass) {
return findAndModify(query, update, options, entityClass, determineCollectionName(entityClass));
}
public <T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
String collectionName) {
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.
@@ -522,13 +557,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);
@@ -569,8 +604,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @param collection
*/
protected void prepareCollection(DBCollection collection) {
if (this.slaveOk) {
collection.slaveOk();
if (this.readPreference != null) {
collection.setReadPreference(readPreference);
}
}
@@ -581,18 +616,21 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* @param writeConcern any WriteConcern already configured or null
* @return The prepared WriteConcern or null
*/
protected WriteConcern prepareWriteConcern(WriteConcern writeConcern) {
return writeConcern;
protected WriteConcern prepareWriteConcern(MongoAction mongoAction) {
return writeConcernResolver.resolve(mongoAction);
}
protected <T> void doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {
assertUpdateableIdIfNotSet(objectToSave);
BasicDBObject dbDoc = new BasicDBObject();
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave));
writer.write(objectToSave, dbDoc);
maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbDoc));
Object id = insertDBObject(collectionName, dbDoc);
Object id = insertDBObject(collectionName, dbDoc, objectToSave.getClass());
populateIdIfNecessary(objectToSave, id);
maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbDoc));
@@ -670,36 +708,43 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
protected <T> void doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
assertUpdateableIdIfNotSet(objectToSave);
BasicDBObject dbDoc = new BasicDBObject();
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave));
writer.write(objectToSave, dbDoc);
maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbDoc));
Object id = saveDBObject(collectionName, dbDoc);
Object id = saveDBObject(collectionName, dbDoc, objectToSave.getClass());
populateIdIfNecessary(objectToSave, id);
maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbDoc));
}
protected Object insertDBObject(String collectionName, final DBObject dbDoc) {
protected Object insertDBObject(final String collectionName, final DBObject dbDoc, final Class<?> entityClass) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("insert DBObject containing fields: " + dbDoc.keySet() + " in collection: " + collectionName);
}
return execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName,
entityClass, dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) {
collection.insert(dbDoc);
wr = collection.insert(dbDoc);
} else {
collection.insert(dbDoc, writeConcernToUse);
wr = collection.insert(dbDoc, writeConcernToUse);
}
handleAnyWriteResultErrors(wr, dbDoc, "insert");
return dbDoc.get(ID);
}
});
}
protected List<ObjectId> insertDBObjectList(String collectionName, final List<DBObject> dbDocList) {
protected List<ObjectId> insertDBObjectList(final String collectionName, final List<DBObject> dbDocList) {
if (dbDocList.isEmpty()) {
return Collections.emptyList();
}
@@ -709,12 +754,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
execute(collectionName, new CollectionCallback<Void>() {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null,
null, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) {
collection.insert(dbDocList);
wr = collection.insert(dbDocList);
} else {
collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse);
wr = collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse);
}
handleAnyWriteResultErrors(wr, null, "insert_list");
return null;
}
});
@@ -732,23 +781,35 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return ids;
}
protected Object saveDBObject(String collectionName, final DBObject dbDoc) {
protected Object saveDBObject(final String collectionName, final DBObject dbDoc, final Class<?> entityClass) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("save DBObject containing fields: " + dbDoc.keySet());
}
return execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass,
dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) {
collection.save(dbDoc);
wr = collection.save(dbDoc);
} else {
collection.save(dbDoc, writeConcernToUse);
wr = collection.save(dbDoc, writeConcernToUse);
}
handleAnyWriteResultErrors(wr, dbDoc, "save");
return dbDoc.get(ID);
}
});
}
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);
}
@@ -787,13 +848,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
WriteResult wr;
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.UPDATE, collectionName,
entityClass, updateObj, queryObj);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (writeConcernToUse == null) {
if (multi) {
wr = collection.updateMulti(queryObj, updateObj);
} else {
wr = collection.update(queryObj, updateObj);
}
wr = collection.update(queryObj, updateObj, upsert, multi);
} else {
wr = collection.update(queryObj, updateObj, upsert, multi, writeConcernToUse);
}
@@ -811,18 +870,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);
doRemove(collection, getIdQueryFor(object), object.getClass());
}
/**
* Returns a {@link Query} for the given entity by its id.
*
@@ -830,9 +889,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();
@@ -843,15 +902,26 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
ConversionService service = mongoConverter.getConversionService();
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) {
throw new MappingException(e.getMessage(), e);
idProperty = BeanWrapper.create(object, service).getProperty(idProp, Object.class, true);
return new Query(where(idProp.getFieldName()).is(idProperty));
}
private void assertUpdateableIdIfNotSet(Object entity) {
MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entity.getClass());
MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
if (idProperty == null) {
return;
}
ConversionService service = mongoConverter.getConversionService();
Object idValue = BeanWrapper.create(entity, service).getProperty(idProperty, Object.class, true);
if (idValue == null && !MongoSimpleTypes.AUTOGENERATED_ID_TYPES.contains(idProperty.getType())) {
throw new InvalidDataAccessApiUsageException(String.format(
"Cannot autogenerate id of type %s for entity of type %s!", idProperty.getType().getName(), entity.getClass()
.getName()));
}
}
@@ -860,7 +930,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
doRemove(determineCollectionName(entityClass), query, entityClass);
}
protected <T> void doRemove(String collectionName, final Query query, Class<T> entityClass) {
protected <T> void doRemove(final String collectionName, final Query query, final Class<T> entityClass) {
if (query == null) {
throw new InvalidDataAccessApiUsageException("Query passed in to remove can't be null");
}
@@ -870,9 +940,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject dboq = mapper.getMappedObject(queryObject, entity);
WriteResult wr = null;
WriteConcern writeConcernToUse = prepareWriteConcern(writeConcern);
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());
LOGGER.debug("remove using query: " + dboq + " in collection: " + collection.getName());
}
if (writeConcernToUse == null) {
wr = collection.remove(dboq);
@@ -930,26 +1002,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
LOGGER.debug("Executing MapReduce on collection [" + command.getInput() + "], mapFunction [" + mapFunc
+ "], reduceFunction [" + reduceFunc + "]");
}
CommandResult commandResult = null;
try {
if (command.getOutputType() == MapReduceCommand.OutputType.INLINE) {
commandResult = executeCommand(commandObject, getDb().getOptions());
} else {
commandResult = executeCommand(commandObject);
}
commandResult.throwOnError();
} catch (RuntimeException ex) {
this.potentiallyConvertRuntimeException(ex);
}
String error = commandResult.getErrorMessage();
if (error != null) {
throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = "
+ commandObject);
}
CommandResult commandResult = command.getOutputType() == MapReduceCommand.OutputType.INLINE ? executeCommand(
commandObject, getDb().getOptions()) : executeCommand(commandObject);
handleCommandError(commandResult, commandObject);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MapReduce command result = [" + commandResult + "]");
LOGGER.debug(String.format("MapReduce command result = [%s]", serializeToJsonSafely(commandObject)));
}
MapReduceOutput mapReduceOutput = new MapReduceOutput(inputCollection, commandObject, commandResult);
List<T> mappedResults = new ArrayList<T>();
DbObjectCallback<T> callback = new ReadDbObjectCallback<T>(mongoConverter, entityClass);
@@ -959,21 +1020,89 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MapReduceResults<T> mapReduceResult = new MapReduceResults<T>(mappedResults, commandResult);
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) {
DBObject dbo = groupBy.getGroupByObject();
dbo.put("ns", inputCollectionName);
if (criteria == null) {
dbo.put("cond", null);
} else {
dbo.put("cond", mapper.getMappedObject(criteria.getCriteriaObject(), null));
}
// 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));
}
}
if (dbo.containsField("$reduce")) {
dbo.put("$reduce", replaceWithResourceIfNecessary(dbo.get("$reduce").toString()));
}
if (dbo.containsField("$keyf")) {
dbo.put("$keyf", replaceWithResourceIfNecessary(dbo.get("$keyf").toString()));
}
if (dbo.containsField("finalize")) {
dbo.put("finalize", replaceWithResourceIfNecessary(dbo.get("finalize").toString()));
}
DBObject commandObject = new BasicDBObject("group", dbo);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Executing Group with DBObject [%s]", serializeToJsonSafely(commandObject)));
}
CommandResult commandResult = executeCommand(commandObject, getDb().getOptions());
handleCommandError(commandResult, commandObject);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Group command result = [" + commandResult + "]");
}
@SuppressWarnings("unchecked")
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) {
mappedResults.add(callback.doWith(dbObject));
}
GroupByResults<T> groupByResult = new GroupByResults<T>(mappedResults, commandResult);
return groupByResult;
}
protected String replaceWithResourceIfNecessary(String function) {
String func = function;
if (this.resourceLoader != null) {
if (this.resourceLoader != null && ResourceUtils.isUrl(function)) {
Resource functionResource = resourceLoader.getResource(func);
if (!functionResource.exists()) {
throw new InvalidDataAccessApiUsageException(String.format("Resource %s not found!", function));
}
try {
Resource functionResource = resourceLoader.getResource(func);
if (functionResource.exists()) {
return new Scanner(functionResource.getInputStream()).useDelimiter("\\A").next();
}
} catch (Exception e) {
// ignore - could be embedded JavaScript text
return new Scanner(functionResource.getInputStream()).useDelimiter("\\A").next();
} catch (IOException e) {
throw new InvalidDataAccessApiUsageException(String.format("Cannot read map-reduce file %s!", function), e);
}
}
return func;
}
@@ -1108,11 +1237,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
protected <S, T> List<T> doFind(String collectionName, DBObject query, DBObject fields, Class<S> entityClass,
CursorPreparer preparer, DbObjectCallback<T> objectCallback) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + entityClass
+ " in collection: " + collectionName);
LOGGER.debug(String.format("find using query: %s fields: %s for class: %s in collection: %s",
serializeToJsonSafely(query), fields, entityClass, collectionName));
}
return executeFindMultiInternal(new FindCallback(mapper.getMappedObject(query, entity), fields), preparer,
objectCallback, collectionName);
}
@@ -1178,6 +1310,33 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
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) {
EntityReader<? super T, DBObject> readerToUse = this.mongoConverter;
if (options == null) {
options = new FindAndModifyOptions();
}
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
DBObject updateObj = update.getUpdateObject();
for (String key : updateObj.keySet()) {
updateObj.put(key, mongoConverter.convertToMongoType(updateObj.get(key)));
}
DBObject mappedQuery = mapper.getMappedObject(query, entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("findAndModify using query: " + mappedQuery + " fields: " + fields + " sort: " + sort
+ " for class: " + entityClass + " and update: " + updateObj + " in collection: " + collectionName);
}
return executeFindOneInternal(new FindAndModifyCallback(mappedQuery, fields, sort, updateObj, options),
new ReadDbObjectCallback<T>(readerToUse, entityClass), collectionName);
}
/**
* Populates the id property of the saved object, if it's not set already.
*
@@ -1196,9 +1355,19 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return;
}
ConversionService conversionService = mongoConverter.getConversionService();
BeanWrapper<PersistentEntity<Object, ?>, Object> wrapper = BeanWrapper.create(savedObject, conversionService);
try {
BeanWrapper.create(savedObject, mongoConverter.getConversionService()).setProperty(idProp, id);
return;
Object idValue = wrapper.getProperty(idProp, idProp.getType(), true);
if (idValue != null) {
return;
}
wrapper.setProperty(idProp, id);
} catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) {
@@ -1316,7 +1485,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return null;
}
private String determineCollectionName(Class<?> entityClass) {
String determineCollectionName(Class<?> entityClass) {
if (entityClass == null) {
throw new InvalidDataAccessApiUsageException(
@@ -1334,30 +1503,34 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Checks and handles any errors.
* <p/>
* TODO: current implementation logs errors - will be configurable to log warning, errors or throw exception in later
* versions
* 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) {
protected void handleAnyWriteResultErrors(WriteResult wr, DBObject query, String operation) {
if (WriteResultChecking.NONE == this.writeResultChecking) {
return;
}
String error = wr.getError();
int n = wr.getN();
if (error != null) {
String message = "Execution of '" + operation + (query == null ? "" : "' using '" + query.toString() + "' query")
+ " failed: " + error;
String message;
if (operation.equals("insert") || operation.equals("save")) {
// assuming the insert operations will begin with insert string
message = String.format("Insert/Save for %s failed: %s", query, error);
} else if (operation.equals("insert_list")) {
message = String.format("Insert list failed: %s", error);
} else {
message = String.format("Execution of %s%s failed: %s", operation,
query == null ? "" : "' using '" + query.toString() + "' query", error);
}
if (WriteResultChecking.EXCEPTION == this.writeResultChecking) {
throw new DataIntegrityViolationException(message);
} else {
LOGGER.error(message);
}
} else if (n == 0) {
String message = "Execution of '" + operation + (query == null ? "" : "' using '" + query.toString() + "' query")
+ " did not succeed: 0 documents updated";
if (WriteResultChecking.EXCEPTION == this.writeResultChecking) {
throw new DataIntegrityViolationException(message);
} else {
LOGGER.warn(message);
return;
}
}
}
@@ -1374,6 +1547,27 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return resolved == null ? ex : resolved;
}
/**
* Inspects the given {@link CommandResult} for erros and potentially throws an
* {@link InvalidDataAccessApiUsageException} for that error.
*
* @param result must not be {@literal null}.
* @param source must not be {@literal null}.
*/
private void handleCommandError(CommandResult result, DBObject source) {
try {
result.throwOnError();
} catch (MongoException ex) {
String error = result.getErrorMessage();
error = error == null ? "NO MESSAGE" : error;
throw new InvalidDataAccessApiUsageException("Command execution failed: Error [" + error + "], Command = "
+ source, ex);
}
}
private static final MongoConverter getDefaultMongoConverter(MongoDbFactory factory) {
MappingMongoConverter converter = new MappingMongoConverter(factory, new MongoMappingContext());
converter.afterPropertiesSet();
@@ -1469,6 +1663,29 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
private static class FindAndModifyCallback implements CollectionCallback<DBObject> {
private final DBObject query;
private final DBObject fields;
private final DBObject sort;
private final DBObject update;
private final FindAndModifyOptions options;
public FindAndModifyCallback(DBObject query, DBObject fields, DBObject sort, DBObject update,
FindAndModifyOptions options) {
this.query = query;
this.fields = fields;
this.sort = sort;
this.update = update;
this.options = options;
}
public DBObject doInCollection(DBCollection collection) throws MongoException, DataAccessException {
return collection.findAndModify(query, fields, sort, options.isRemove(), update, options.isReturnNew(),
options.isUpsert());
}
}
/**
* Simple internal callback to allow operations on a {@link DBObject}.
*
@@ -1510,6 +1727,60 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
}
private class DefaultWriteConcernResolver implements WriteConcernResolver {
public WriteConcern resolve(MongoAction action) {
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.
@@ -1543,4 +1814,5 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return new GeoResult<T>(doWith, new Distance(distance, metric));
}
}
}

View File

@@ -25,9 +25,12 @@ import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.util.Assert;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -39,100 +42,131 @@ import com.mongodb.DBObject;
*/
public class QueryMapper {
private static final List<String> DEFAULT_ID_NAMES = Arrays.asList("id", "_id");
private static final String N_OR_PATTERN = "\\$.*or";
private final ConversionService conversionService;
private final MongoConverter converter;
/**
* Creates a new {@link QueryMapper} with the given {@link ConversionService}.
* Creates a new {@link QueryMapper} with the given {@link MongoConverter}.
*
* @param conversionService must not be {@literal null}.
* @param converter must not be {@literal null}.
*/
public QueryMapper(ConversionService conversionService) {
Assert.notNull(conversionService);
this.conversionService = conversionService;
public QueryMapper(MongoConverter converter) {
Assert.notNull(converter);
this.conversionService = converter.getConversionService();
this.converter = converter;
}
/**
* Replaces the property keys used in the given {@link DBObject} with the appropriate keys by using the
* {@link PersistentEntity} metadata.
*
* @param query
* @param entity
* @param query must not be {@literal null}.
* @param entity can be {@literal null}.
* @return
*/
public DBObject getMappedObject(DBObject query, MongoPersistentEntity<?> entity) {
String idKey = null;
if (null != entity && entity.getIdProperty() != null) {
idKey = entity.getIdProperty().getName();
} else if (query.containsField("id")) {
idKey = "id";
} else if (query.containsField("_id")) {
idKey = "_id";
}
DBObject newDbo = new BasicDBObject();
for (String key : query.keySet()) {
String newKey = key;
Object value = query.get(key);
if (key.equals(idKey)) {
if (isIdKey(key, entity)) {
if (value instanceof DBObject) {
DBObject valueDbo = (DBObject) value;
if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) {
String inKey = valueDbo.containsField("$in") ? "$in" : "$nin";
List<Object> ids = new ArrayList<Object>();
for (Object id : (Object[]) valueDbo.get(inKey)) {
for (Object id : (Iterable<?>) valueDbo.get(inKey)) {
ids.add(convertId(id));
}
valueDbo.put(inKey, ids.toArray(new Object[ids.size()]));
} else if (valueDbo.containsField("$ne")) {
valueDbo.put("$ne", convertId(valueDbo.get("$ne")));
} else {
value = getMappedObject((DBObject) value, entity);
value = getMappedObject((DBObject) value, null);
}
} else {
value = convertId(value);
}
newKey = "_id";
} else if (key.startsWith("$") && key.endsWith("or")) {
} else if (key.matches(N_OR_PATTERN)) {
// $or/$nor
BasicBSONList conditions = (BasicBSONList) value;
Iterable<?> conditions = (Iterable<?>) value;
BasicBSONList newConditions = new BasicBSONList();
Iterator<Object> iter = conditions.iterator();
Iterator<?> iter = conditions.iterator();
while (iter.hasNext()) {
newConditions.add(getMappedObject((DBObject) iter.next(), entity));
}
value = newConditions;
} else if (key.equals("$ne")) {
value = convertId(value);
}
newDbo.put(newKey, value);
newDbo.put(newKey, convertSimpleOrDBObject(value, null));
}
return newDbo;
}
/**
* Retriggers mapping if the given source is a {@link DBObject} or simply invokes the
*
* @param source
* @param entity
* @return
*/
private Object convertSimpleOrDBObject(Object source, MongoPersistentEntity<?> entity) {
if (source instanceof BasicDBList) {
return converter.convertToMongoType(source);
}
if (source instanceof DBObject) {
return getMappedObject((DBObject) source, entity);
}
return converter.convertToMongoType(source);
}
/**
* Returns whether the given key will be considered an id key.
*
* @param key
* @param entity
* @return
*/
private boolean isIdKey(String key, MongoPersistentEntity<?> entity) {
if (entity == null) {
return false;
}
if (entity.getIdProperty() != null) {
MongoPersistentProperty idProperty = entity.getIdProperty();
return idProperty.getName().equals(key) || idProperty.getFieldName().equals(key);
}
return DEFAULT_ID_NAMES.contains(key);
}
/**
* Converts the given raw id value into either {@link ObjectId} or {@link String}.
*
* @param id
* @return
*/
@SuppressWarnings("unchecked")
public Object convertId(Object id) {
for (Class<?> type : Arrays.asList(ObjectId.class, String.class)) {
if (id.getClass().isAssignableFrom(type)) {
return id;
}
try {
return conversionService.convert(id, type);
} catch (ConversionException e) {
// Ignore
}
try {
return conversionService.convert(id, ObjectId.class);
} catch (ConversionException e) {
// Ignore
}
return id;
return converter.convertToMongoType(id);
}
}

View File

@@ -17,8 +17,6 @@ package org.springframework.data.mongodb.core;
import java.net.UnknownHostException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.dao.DataAccessException;
import org.springframework.data.authentication.UserCredentials;
@@ -39,12 +37,10 @@ import com.mongodb.WriteConcern;
*/
public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
protected final Log logger = LogFactory.getLog(getClass());
private final Mongo mongo;
private final String databaseName;
private String username;
private String password;
private final boolean mongoInstanceCreated;
private final UserCredentials credentials;
private WriteConcern writeConcern;
/**
@@ -54,11 +50,7 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
* @param databaseName database name, not be {@literal null}.
*/
public SimpleMongoDbFactory(Mongo mongo, String databaseName) {
Assert.notNull(mongo, "Mongo must not be null");
Assert.hasText(databaseName, "Database name must not be empty");
Assert.isTrue(databaseName.matches("[\\w-]+"), "Database name must only contain letters, numbers, underscores and dashes!");
this.mongo = mongo;
this.databaseName = databaseName;
this(mongo, databaseName, new UserCredentials(), false);
}
/**
@@ -66,14 +58,12 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
*
* @param mongo Mongo instance, must not be {@literal null}.
* @param databaseName Database name, must not be {@literal null}.
* @param userCredentials username and password must not be {@literal null}.
* @param credentials username and password.
*/
public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials userCredentials) {
this(mongo, databaseName);
this.username = userCredentials.getUsername();
this.password = userCredentials.getPassword();
public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials) {
this(mongo, databaseName, credentials, false);
}
/**
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoURI}.
*
@@ -83,8 +73,21 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
* @see MongoURI
*/
public SimpleMongoDbFactory(MongoURI uri) throws MongoException, UnknownHostException {
this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), String.valueOf(uri.getPassword())));
this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), parseChars(uri.getPassword())), true);
}
private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials,
boolean mongoInstanceCreated) {
Assert.notNull(mongo, "Mongo must not be null");
Assert.hasText(databaseName, "Database name must not be empty");
Assert.isTrue(databaseName.matches("[\\w-]+"),
"Database name must only contain letters, numbers, underscores and dashes!");
this.mongo = mongo;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.credentials = credentials == null ? new UserCredentials() : credentials;
}
/**
@@ -112,6 +115,9 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
Assert.hasText(dbName, "Database name must not be empty.");
String username = credentials.getUsername();
String password = credentials.getPassword();
DB db = MongoDbUtils.getDB(mongo, dbName, username, password == null ? null : password.toCharArray());
if (writeConcern != null) {
@@ -122,9 +128,17 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
}
/**
* Clean up the Mongo instance.
* Clean up the Mongo instance if it was created by the factory itself.
*
* @see DisposableBean#destroy()
*/
public void destroy() throws Exception {
mongo.close();
if (mongoInstanceCreated) {
mongo.close();
}
}
private static String parseChars(char[] chars) {
return chars == null ? null : String.valueOf(chars);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core;
import com.mongodb.WriteConcern;
/**
* A strategy interface to determine the WriteConcern to use for a given MongoDbAction.
*
* Return the passed in default WriteConcern (a property on MongoAction) if no determination can be made.
*
* @author Mark Pollack
*
*/
public interface WriteConcernResolver {
/**
* Resolve the WriteConcern given the MongoAction
*
* @param action describes the context of the Mongo action. Contains a default WriteConcern to use if one should not
* be resolved.
* @return a WriteConcern based on the passed in MongoAction value, maybe null
*/
WriteConcern resolve(MongoAction action);
}

View File

@@ -17,16 +17,15 @@
package org.springframework.data.mongodb.core.convert;
import java.math.BigInteger;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToBigIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToObjectIdConverter;
/**
@@ -46,10 +45,10 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
*
* @param conversionService
*/
@SuppressWarnings("deprecation")
public AbstractMongoConverter(GenericConversionService conversionService) {
this.conversionService = conversionService == null ? ConversionServiceFactory.createDefaultConversionService()
: conversionService;
this.conversionService.removeConvertible(Object.class, String.class);
}
/**
@@ -80,12 +79,6 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
if (!conversionService.canConvert(BigInteger.class, ObjectId.class)) {
conversionService.addConverter(BigIntegerToObjectIdConverter.INSTANCE);
}
if (!conversionService.canConvert(BigInteger.class, String.class)) {
conversionService.addConverter(BigIntegerToStringConverter.INSTANCE);
}
if (!conversionService.canConvert(String.class, BigInteger.class)) {
conversionService.addConverter(StringToBigIntegerConverter.INSTANCE);
}
conversions.registerConvertersIn(conversionService);
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core.convert;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.util.Assert;
/**
* Conversion registration information.
*
* @author Oliver Gierke
*/
class ConverterRegistration {
private final ConvertiblePair convertiblePair;
private final boolean reading;
private final boolean writing;
/**
* Creates a new {@link ConverterRegistration}.
*
* @param convertiblePair must not be {@literal null}.
* @param isReading whether to force to consider the converter for reading.
* @param isWritingwhether to force to consider the converter for reading.
*/
public ConverterRegistration(ConvertiblePair convertiblePair, boolean isReading, boolean isWriting) {
Assert.notNull(convertiblePair);
this.convertiblePair = convertiblePair;
this.reading = isReading;
this.writing = isWriting;
}
/**
* Creates a new {@link ConverterRegistration} from the given source and target type and read/write flags.
*
* @param source the source type to be converted from, must not be {@literal null}.
* @param target the target type to be converted to, must not be {@literal null}.
* @param isReading whether to force to consider the converter for reading.
* @param isWriting whether to force to consider the converter for writing.
*/
public ConverterRegistration(Class<?> source, Class<?> target, boolean isReading, boolean isWriting) {
this(new ConvertiblePair(source, target), isReading, isWriting);
}
/**
* Returns whether the converter shall be used for writing.
*
* @return
*/
public boolean isWriting() {
return writing == true || (!reading && isSimpleTargetType());
}
/**
* Returns whether the converter shall be used for reading.
*
* @return
*/
public boolean isReading() {
return reading == true || (!writing && isSimpleSourceType());
}
/**
* Returns the actual conversion pair.
*
* @return
*/
public ConvertiblePair getConvertiblePair() {
return convertiblePair;
}
/**
* Returns whether the source type is a Mongo simple one.
*
* @return
*/
public boolean isSimpleSourceType() {
return isMongoBasicType(convertiblePair.getSourceType());
}
/**
* Returns whether the target type is a Mongo simple one.
*
* @return
*/
public boolean isSimpleTargetType() {
return isMongoBasicType(convertiblePair.getTargetType());
}
/**
* Returns whether the given type is a type that Mongo can handle basically.
*
* @param type
* @return
*/
private static boolean isMongoBasicType(Class<?> type) {
return MongoSimpleTypes.HOLDER.isSimpleType(type);
}
}

View File

@@ -17,13 +17,13 @@ package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.bson.types.ObjectId;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
@@ -31,14 +31,18 @@ import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigDecimalToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigDecimalConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToURLConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.URLToStringConverter;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* Value object to capture custom conversion. That is essentially a {@link List} of converters and some additional logic
* around them. The converters are pretty much builds up two sets of types which Mongo basic types {@see #MONGO_TYPES}
@@ -50,9 +54,9 @@ import com.mongodb.DBObject;
*/
public class CustomConversions {
@SuppressWarnings({ "unchecked" })
private static final List<Class<?>> MONGO_TYPES = Arrays.asList(Number.class, Date.class, ObjectId.class,
String.class, DBObject.class);
private static final Log LOG = LogFactory.getLog(CustomConversions.class);
private static final String READ_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as reading converter although it doesn't convert from a Mongo supported type! You might wanna check you annotation setup at the converter implementation.";
private static final String WRITE_CONVERTER_NOT_SIMPLE = "Registering converter from %s to %s as writing converter although it doesn't convert to a Mongo supported type! You might wanna check you annotation setup at the converter implementation.";
private final Set<ConvertiblePair> readingPairs;
private final Set<ConvertiblePair> writingPairs;
@@ -85,6 +89,10 @@ public class CustomConversions {
this.converters.add(CustomToStringConverter.INSTANCE);
this.converters.add(BigDecimalToStringConverter.INSTANCE);
this.converters.add(StringToBigDecimalConverter.INSTANCE);
this.converters.add(BigIntegerToStringConverter.INSTANCE);
this.converters.add(StringToBigIntegerConverter.INSTANCE);
this.converters.add(URLToStringConverter.INSTANCE);
this.converters.add(StringToURLConverter.INSTANCE);
this.converters.addAll(converters);
for (Object c : this.converters) {
@@ -155,14 +163,18 @@ public class CustomConversions {
*/
private void registerConversion(Object converter) {
Class<?> type = converter.getClass();
boolean isWriting = type.isAnnotationPresent(WritingConverter.class);
boolean isReading = type.isAnnotationPresent(ReadingConverter.class);
if (converter instanceof GenericConverter) {
GenericConverter genericConverter = (GenericConverter) converter;
for (ConvertiblePair pair : genericConverter.getConvertibleTypes()) {
register(pair);
register(new ConverterRegistration(pair, isReading, isWriting));
}
} else if (converter instanceof Converter) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), Converter.class);
register(new ConvertiblePair(arguments[0], arguments[1]));
register(new ConverterRegistration(arguments[0], arguments[1], isReading, isWriting));
} else {
throw new IllegalArgumentException("Unsupported Converter type!");
}
@@ -174,15 +186,27 @@ public class CustomConversions {
*
* @param pair
*/
private void register(ConvertiblePair pair) {
private void register(ConverterRegistration context) {
ConvertiblePair pair = context.getConvertiblePair();
if (context.isReading()) {
if (isMongoBasicType(pair.getSourceType())) {
readingPairs.add(pair);
if (LOG.isWarnEnabled() && !context.isSimpleSourceType()) {
LOG.warn(String.format(READ_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType()));
}
}
if (isMongoBasicType(pair.getTargetType())) {
if (context.isWriting()) {
writingPairs.add(pair);
customSimpleTypes.add(pair.getSourceType());
if (LOG.isWarnEnabled() && !context.isSimpleTargetType()) {
LOG.warn(String.format(WRITE_CONVERTER_NOT_SIMPLE, pair.getSourceType(), pair.getTargetType()));
}
}
}
@@ -273,16 +297,7 @@ public class CustomConversions {
return null;
}
/**
* Returns whether the given type is a type that Mongo can handle basically.
*
* @param type
* @return
*/
private static boolean isMongoBasicType(Class<?> type) {
return MONGO_TYPES.contains(type);
}
@WritingConverter
private enum CustomToStringConverter implements GenericConverter {
INSTANCE;

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core.convert;
import java.util.Map;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import com.mongodb.DBObject;
/**
* {@link PropertyAccessor} to allow entity based field access to {@link DBObject}s.
*
* @author Oliver Gierke
*/
class DBObjectPropertyAccessor extends MapAccessor {
static MapAccessor INSTANCE = new DBObjectPropertyAccessor();
@Override
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { DBObject.class };
}
@Override
public boolean canRead(EvaluationContext context, Object target, String name) {
return true;
}
@Override
@SuppressWarnings("unchecked")
public TypedValue read(EvaluationContext context, Object target, String name) {
Map<String, Object> source = (Map<String, Object>) target;
Object value = source.get(name);
return value == null ? TypedValue.NULL : new TypedValue(value);
}
}

View File

@@ -50,23 +50,22 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<DBObject> implemen
public DefaultMongoTypeMapper() {
this(DEFAULT_TYPE_KEY, Arrays.asList(SimpleTypeInformationMapper.INSTANCE));
}
public DefaultMongoTypeMapper(String typeKey) {
super(new DBObjectTypeAliasAccessor(typeKey));
this.typeKey = typeKey;
}
public DefaultMongoTypeMapper(String typeKey, MappingContext<? extends PersistentEntity<?,?>, ?> mappingContext) {
public DefaultMongoTypeMapper(String typeKey, MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext) {
super(new DBObjectTypeAliasAccessor(typeKey), mappingContext, Arrays.asList(SimpleTypeInformationMapper.INSTANCE));
this.typeKey = typeKey;
}
public DefaultMongoTypeMapper(String typeKey, List<? extends TypeInformationMapper> mappers) {
super(new DBObjectTypeAliasAccessor(typeKey), mappers);
this.typeKey = typeKey;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.MongoTypeMapper#isTypeKey(java.lang.String)
@@ -75,7 +74,6 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<DBObject> implemen
return typeKey == null ? false : typeKey.equals(key);
}
/* (non-Javadoc)
* @see org.springframework.data.convert.DefaultTypeMapper#getFallbackTypeFor(java.lang.Object)
*/
@@ -83,29 +81,29 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper<DBObject> implemen
protected TypeInformation<?> getFallbackTypeFor(DBObject source) {
return source instanceof BasicDBList ? LIST_TYPE_INFO : MAP_TYPE_INFO;
}
/**
*
* @author Oliver Gierke
*/
public static final class DBObjectTypeAliasAccessor implements TypeAliasAccessor<DBObject> {
private final String typeKey;
public DBObjectTypeAliasAccessor(String typeKey) {
this.typeKey = typeKey;
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeAliasAccessor#readAliasFrom(java.lang.Object)
*/
public Object readAliasFrom(DBObject source) {
if (source instanceof BasicDBList) {
return null;
}
return source.get(typeKey);
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core.convert;
import java.util.HashSet;
import java.util.Set;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* Abstraction for a {@link PreferredConstructor} alongside mapping information.
*
* @author Oliver Gierke
*/
class MappedConstructor {
private static final String REJECT_CONSTRUCTOR = String.format("Entity doesn't have a usable constructor, either "
+ "provide a custom converter or annotate a constructor with @%s!", PersistenceConstructor.class.getSimpleName());
private final Set<MappedConstructor.MappedParameter> parameters;
/**
* Creates a new {@link MappedConstructor} from the given {@link MongoPersistentEntity} and {@link MappingContext}.
*
* @param entity must not be {@literal null}.
* @param context must not be {@literal null}.
* @throws MappingException in case the {@link MongoPersistentEntity} handed in does not have a
* {@link PreferredConstructor}.
*/
public MappedConstructor(MongoPersistentEntity<?> entity,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context) throws MappingException {
Assert.notNull(entity);
Assert.notNull(context);
if (entity.getPreferredConstructor() == null) {
throw new MappingException(REJECT_CONSTRUCTOR);
}
this.parameters = new HashSet<MappedConstructor.MappedParameter>();
for (Parameter<?> parameter : entity.getPreferredConstructor().getParameters()) {
parameters.add(new MappedParameter(parameter, entity, context));
}
}
/**
* Returns whether the given {@link PersistentProperty} is referenced in a constructor argument of the
* {@link PersistentEntity} backing this {@link MappedConstructor}.
*
* @param property must not be {@literal null}.
* @return
*/
public boolean isConstructorParameter(PersistentProperty<?> property) {
Assert.notNull(property);
for (MappedConstructor.MappedParameter parameter : parameters) {
if (parameter.maps(property)) {
return true;
}
}
return false;
}
/**
* Returns the {@link MappedParameter} for the given {@link Parameter}.
*
* @param parameter must not be {@literal null}.
* @return
* @throws MappingException in case no {@link MappedParameter} can be found for the given {@link Parameter}.
*/
public MappedParameter getFor(Parameter<?> parameter) {
for (MappedParameter mappedParameter : parameters) {
if (mappedParameter.parameter.equals(parameter)) {
return mappedParameter;
}
}
throw new MappingException(String.format("Didn't find a MappedParameter for %s!", parameter.toString()));
}
/**
* Abstraction of a {@link Parameter} alongside mapping information.
*
* @author Oliver Gierke
*/
static class MappedParameter {
private final MongoPersistentProperty property;
private final Parameter<?> parameter;
/**
* Creates a new {@link MappedParameter} for the given {@link Parameter}, {@link MongoPersistentProperty} and
* {@link MappingContext}.
*
* @param parameter must not be {@literal null}.
* @param entity must not be {@literal null}.
* @param context must not be {@literal null}.
*/
public MappedParameter(Parameter<?> parameter, MongoPersistentEntity<?> entity,
MappingContext<? extends MongoPersistentEntity<?>, ? extends MongoPersistentProperty> context) {
Assert.notNull(parameter);
Assert.notNull(entity);
Assert.notNull(context);
this.parameter = parameter;
PropertyPath propertyPath = PropertyPath.from(parameter.getName(), entity.getType());
PersistentPropertyPath<? extends MongoPersistentProperty> path = context.getPersistentPropertyPath(propertyPath);
this.property = path == null ? null : path.getLeafProperty();
}
/**
* Returns whether there is a SpEL expression configured for this parameter.
*
* @return
*/
public boolean hasSpELExpression() {
return parameter.getKey() != null;
}
/**
* Returns the field name to be used to lookup the value which in turn shall be converted into the constructor
* parameter.
*
* @return
*/
public String getFieldName() {
return property.getFieldName();
}
/**
* Returns the type of the property backing the {@link Parameter}.
*
* @return
*/
public TypeInformation<?> getPropertyTypeInformation() {
return property.getTypeInformation();
}
/**
* Returns whether the given {@link PersistentProperty} is mapped by the parameter.
*
* @param property
* @return
*/
public boolean maps(PersistentProperty<?> property) {
return this.property.equals(property);
}
}
}

View File

@@ -17,20 +17,16 @@ package org.springframework.data.mongodb.core.convert;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.ObjectId;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -41,7 +37,7 @@ import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.data.convert.TypeMapper;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
@@ -74,16 +70,7 @@ import com.mongodb.DBRef;
* @author Oliver Gierke
* @author Jon Brisbin
*/
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware, TypeKeyAware {
@SuppressWarnings("rawtypes")
private static final TypeInformation<Map> MAP_TYPE_INFORMATION = ClassTypeInformation.from(Map.class);
@SuppressWarnings("rawtypes")
private static final TypeInformation<Collection> COLLECTION_TYPE_INFORMATION = ClassTypeInformation
.from(Collection.class);
private static final List<Class<?>> VALID_ID_TYPES = Arrays.asList(new Class<?>[] { ObjectId.class, String.class,
BigInteger.class, byte[].class });
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
protected static final Log log = LogFactory.getLog(MappingMongoConverter.class);
@@ -94,6 +81,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
protected ApplicationContext applicationContext;
protected boolean useFieldAccessOnly = true;
protected MongoTypeMapper typeMapper;
protected String mapKeyDotReplacement = null;
/**
* Creates a new {@link MappingMongoConverter} given the new {@link MongoDbFactory} and {@link MappingContext}.
@@ -101,6 +89,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @param mongoDbFactory must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
*/
@SuppressWarnings("deprecation")
public MappingMongoConverter(MongoDbFactory mongoDbFactory,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
@@ -112,7 +101,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
this.mongoDbFactory = mongoDbFactory;
this.mappingContext = mappingContext;
this.typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext);
this.idMapper = new QueryMapper(conversionService);
this.idMapper = new QueryMapper(this);
}
/**
@@ -128,12 +117,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
mappingContext) : typeMapper;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.TypeKeyAware#isTypeKey(java.lang.String)
/**
* Configure the characters dots potentially contained in a {@link Map} shall be replaced with. By default we don't do
* any translation but rather reject a {@link Map} with keys containing dots causing the conversion for the entire
* object to fail. If further customization of the translation is needed, have a look at
* {@link #potentiallyEscapeMapKey(String)} as well as {@link #potentiallyUnescapeMapKey(String)}.
*
* @param mapKeyDotReplacement the mapKeyDotReplacement to set
*/
public boolean isTypeKey(String key) {
return typeMapper.isTypeKey(key);
public void setMapKeyDotReplacement(String mapKeyDotReplacement) {
this.mapKeyDotReplacement = mapKeyDotReplacement;
}
/*
@@ -185,6 +178,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return conversionService.convert(dbo, rawType);
}
if (DBObject.class.isAssignableFrom(rawType)) {
return (S) dbo;
}
if (typeToUse.isCollectionLike() && dbo instanceof BasicDBList) {
return (S) readCollectionOrArray(typeToUse, (BasicDBList) dbo);
}
@@ -205,52 +202,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo) {
final StandardEvaluationContext spelCtx = new StandardEvaluationContext();
if (null != applicationContext) {
final StandardEvaluationContext spelCtx = new StandardEvaluationContext(dbo);
spelCtx.addPropertyAccessor(DBObjectPropertyAccessor.INSTANCE);
if (applicationContext != null) {
spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext));
}
if (!(dbo instanceof BasicDBList)) {
String[] keySet = dbo.keySet().toArray(new String[dbo.keySet().size()]);
for (String key : keySet) {
spelCtx.setVariable(key, dbo.get(key));
}
}
final List<String> ctorParamNames = new ArrayList<String>();
final MongoPersistentProperty idProperty = entity.getIdProperty();
final MappedConstructor constructor = new MappedConstructor(entity, mappingContext);
ParameterValueProvider provider = new SpELAwareParameterValueProvider(spelExpressionParser, spelCtx) {
@Override
@SuppressWarnings("unchecked")
public <T> T getParameterValue(PreferredConstructor.Parameter<T> parameter) {
if (parameter.getKey() != null) {
return super.getParameterValue(parameter);
}
String name = parameter.getName();
TypeInformation<T> type = parameter.getType();
Class<T> rawType = parameter.getRawType();
String key = idProperty == null ? name : idProperty.getName().equals(name) ? idProperty.getFieldName() : name;
Object obj = dbo.get(key);
ctorParamNames.add(name);
if (obj instanceof DBRef) {
return read(type, ((DBRef) obj).fetch());
} else if (obj instanceof BasicDBList) {
BasicDBList objAsDbList = (BasicDBList) obj;
return conversionService.convert(readCollectionOrArray(type, objAsDbList), rawType);
} else if (obj instanceof DBObject) {
return read(type, ((DBObject) obj));
} else if (null != obj && obj.getClass().isAssignableFrom(rawType)) {
return (T) obj;
} else if (null != obj) {
return conversionService.convert(obj, rawType);
}
return null;
}
};
SpELAwareParameterValueProvider delegate = new SpELAwareParameterValueProvider(spelExpressionParser, spelCtx);
ParameterValueProvider provider = new DelegatingParameterValueProvider(constructor, dbo, delegate);
final BeanWrapper<MongoPersistentEntity<S>, S> wrapper = BeanWrapper.create(entity, provider, conversionService);
@@ -258,7 +220,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
public void doWithPersistentProperty(MongoPersistentProperty prop) {
boolean isConstructorProperty = ctorParamNames.contains(prop.getName());
boolean isConstructorProperty = constructor.isConstructorParameter(prop);
boolean hasValueForProperty = dbo.containsField(prop.getFieldName());
if (!hasValueForProperty || isConstructorProperty) {
@@ -266,13 +228,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
Object obj = getValueInternal(prop, dbo, spelCtx, prop.getSpelExpression());
try {
wrapper.setProperty(prop, obj, useFieldAccessOnly);
} catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new MappingException(e.getMessage(), e);
}
wrapper.setProperty(prop, obj, useFieldAccessOnly);
}
});
@@ -308,7 +264,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
boolean handledByCustomConverter = conversions.getCustomWriteTarget(obj.getClass(), DBObject.class) != null;
TypeInformation<? extends Object> type = ClassTypeInformation.from(obj.getClass());
if (!handledByCustomConverter && !(dbo instanceof BasicDBList)) {
typeMapper.writeType(type, dbo);
}
@@ -338,12 +294,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
if (Map.class.isAssignableFrom(obj.getClass())) {
writeMapInternal((Map<Object, Object>) obj, dbo, MAP_TYPE_INFORMATION);
writeMapInternal((Map<Object, Object>) obj, dbo, ClassTypeInformation.MAP);
return;
}
if (Collection.class.isAssignableFrom(obj.getClass())) {
writeCollectionInternal((Collection<?>) obj, COLLECTION_TYPE_INFORMATION, (BasicDBList) dbo);
writeCollectionInternal((Collection<?>) obj, ClassTypeInformation.LIST, (BasicDBList) dbo);
return;
}
@@ -367,46 +323,24 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
// Write the ID
final MongoPersistentProperty idProperty = entity.getIdProperty();
if (!dbo.containsField("_id") && null != idProperty) {
Object idObj = null;
Class<?>[] targetClasses = new Class<?>[] { ObjectId.class, String.class, Object.class };
for (Class<?> targetClass : targetClasses) {
try {
idObj = wrapper.getProperty(idProperty, targetClass, useFieldAccessOnly);
if (null != idObj) {
break;
}
} catch (ConversionException ignored) {
} catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new MappingException(e.getMessage(), e);
}
}
if (null != idObj) {
dbo.put("_id", idObj);
} else {
if (!VALID_ID_TYPES.contains(idProperty.getType())) {
throw new MappingException("Invalid data type " + idProperty.getType().getName()
+ " for Id property. Should be one of " + VALID_ID_TYPES);
}
try {
Object id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);
dbo.put("_id", idMapper.convertId(id));
} catch (ConversionException ignored) {
}
}
// Write the properties
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
public void doWithPersistentProperty(MongoPersistentProperty prop) {
if (prop.equals(idProperty)) {
return;
}
Object propertyObj;
try {
propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
} catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new MappingException(e.getMessage(), e);
}
Object propertyObj = wrapper.getProperty(prop, prop.getType(), useFieldAccessOnly);
if (null != propertyObj) {
if (!conversions.isSimpleType(propertyObj.getClass())) {
writePropertyInternal(propertyObj, dbo, prop);
@@ -421,14 +355,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
public void doWithAssociation(Association<MongoPersistentProperty> association) {
MongoPersistentProperty inverseProp = association.getInverse();
Class<?> type = inverseProp.getType();
Object propertyObj;
try {
propertyObj = wrapper.getProperty(inverseProp, type, useFieldAccessOnly);
} catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new MappingException(e.getMessage(), e);
}
Object propertyObj = wrapper.getProperty(inverseProp, type, useFieldAccessOnly);
if (null != propertyObj) {
writePropertyInternal(propertyObj, dbo, inverseProp);
}
@@ -444,16 +371,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
String name = prop.getFieldName();
TypeInformation<?> valueType = ClassTypeInformation.from(obj.getClass());
TypeInformation<?> type = prop.getTypeInformation();
if (prop.isCollection()) {
if (valueType.isCollectionLike()) {
DBObject collectionInternal = createCollection(asCollection(obj), prop);
dbo.put(name, collectionInternal);
return;
}
TypeInformation<?> type = prop.getTypeInformation();
if (prop.isMap()) {
if (valueType.isMap()) {
BasicDBObject mapDbObj = new BasicDBObject();
writeMapInternal((Map<Object, Object>) obj, mapDbObj, type);
dbo.put(name, mapDbObj);
@@ -512,7 +439,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
*
* @param collection must not be {@literal null}.
* @param property must not be {@literal null}.
*
* @return
*/
protected DBObject createCollection(Collection<?> collection, MongoPersistentProperty property) {
@@ -550,13 +476,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
for (Object element : source) {
if (element == null) {
continue;
}
Class<?> elementType = element == null ? null : element.getClass();
Class<?> elementType = element.getClass();
if (conversions.isSimpleType(elementType)) {
if (elementType == null || conversions.isSimpleType(elementType)) {
sink.add(getPotentiallyConvertedSimpleWrite(element));
} else if (element instanceof Collection || elementType.isArray()) {
sink.add(writeCollectionInternal(asCollection(element), componentType, new BasicDBList()));
@@ -579,32 +501,67 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @return
*/
protected DBObject writeMapInternal(Map<Object, Object> obj, DBObject dbo, TypeInformation<?> propertyType) {
for (Map.Entry<Object, Object> entry : obj.entrySet()) {
Object key = entry.getKey();
Object val = entry.getValue();
if (conversions.isSimpleType(key.getClass())) {
// Don't use conversion service here as removal of ObjectToString converter results in some primitive types not
// being convertable
String simpleKey = key.toString();
String simpleKey = potentiallyEscapeMapKey(key.toString());
if (val == null || conversions.isSimpleType(val.getClass())) {
writeSimpleInternal(val, dbo, simpleKey);
} else if (val instanceof Collection) {
} else if (val instanceof Collection || val.getClass().isArray()) {
dbo.put(simpleKey,
writeCollectionInternal((Collection<?>) val, propertyType.getMapValueType(), new BasicDBList()));
writeCollectionInternal(asCollection(val), propertyType.getMapValueType(), new BasicDBList()));
} else {
DBObject newDbo = new BasicDBObject();
writeInternal(val, newDbo, propertyType);
TypeInformation<?> valueTypeInfo = propertyType.isMap() ? propertyType.getMapValueType()
: ClassTypeInformation.OBJECT;
writeInternal(val, newDbo, valueTypeInfo);
dbo.put(simpleKey, newDbo);
}
} else {
throw new MappingException("Cannot use a complex object as a key value.");
}
}
return dbo;
}
/**
* Potentially replaces dots in the given map key with the configured map key replacement if configured or aborts
* conversion if none is configured.
*
* @see #setMapKeyDotReplacement(String)
* @param source
* @return
*/
protected String potentiallyEscapeMapKey(String source) {
if (!source.contains(".")) {
return source;
}
if (mapKeyDotReplacement == null) {
throw new MappingException(String.format("Map key %s contains dots but no replacement was configured! Make "
+ "sure map keys don't contain dots in the first place or configure an appropriate replacement!", source));
}
return source.replaceAll("\\.", mapKeyDotReplacement);
}
/**
* Translates the map key replacements in the given key just read with a dot in case a map key replacement has been
* configured.
*
* @param source
* @return
*/
protected String potentiallyUnescapeMapKey(String source) {
return mapKeyDotReplacement == null ? source : source.replaceAll(mapKeyDotReplacement, "\\.");
}
/**
* Adds custom type information to the given {@link DBObject} if necessary. That is if the value is not the same as
* the one given. This is usually the case if you store a subtype of the actual declared type of the property.
@@ -653,7 +610,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (customTarget != null) {
return conversionService.convert(value, customTarget);
} else {
return value.getClass().isEnum() ? ((Enum<?>) value).name() : value;
return Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value;
}
}
@@ -676,7 +633,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return conversionService.convert(value, target);
}
if (target.isEnum()) {
if (Enum.class.isAssignableFrom(target)) {
return Enum.valueOf((Class<Enum>) target, value.toString());
}
@@ -691,18 +648,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
MongoPersistentProperty idProperty = targetEntity.getIdProperty();
Object id = null;
BeanWrapper<MongoPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(target, conversionService);
Object id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);
try {
id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);
if (null == id) {
throw new MappingException("Cannot create a reference to an object with a NULL id.");
}
} catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new MappingException(e.getMessage(), e);
if (null == id) {
throw new MappingException("Cannot create a reference to an object with a NULL id.");
}
String collection = dbref.collection();
@@ -748,12 +698,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
&& ((DBObject) sourceValue).keySet().size() == 0) {
// It's empty
return Array.newInstance(prop.getComponentType(), 0);
} else if (prop.isCollection() && sourceValue instanceof BasicDBList) {
} else if (prop.isCollectionLike() && sourceValue instanceof BasicDBList) {
return readCollectionOrArray((TypeInformation<? extends Collection<?>>) prop.getTypeInformation(),
(BasicDBList) sourceValue);
}
TypeInformation<?> toType = typeMapper.readType((DBObject) sourceValue);
TypeInformation<?> toType = typeMapper.readType((DBObject) sourceValue, prop.getTypeInformation());
// It's a complex object, have to read it in
if (toType != null) {
@@ -778,12 +728,15 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @return the converted {@link Collections}, will never be {@literal null}.
*/
@SuppressWarnings("unchecked")
private Collection<?> readCollectionOrArray(TypeInformation<?> targetType, BasicDBList sourceValue) {
private Object readCollectionOrArray(TypeInformation<?> targetType, BasicDBList sourceValue) {
Assert.notNull(targetType);
Class<?> collectionType = targetType.getType();
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>() : CollectionFactory
.createCollection(targetType.getType(), sourceValue.size());
.createCollection(collectionType, sourceValue.size());
for (int i = 0; i < sourceValue.size(); i++) {
Object dbObjItem = sourceValue.get(i);
@@ -792,11 +745,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
} else if (dbObjItem instanceof DBObject) {
items.add(read(targetType.getComponentType(), (DBObject) dbObjItem));
} else {
items.add(getPotentiallyConvertedSimpleRead(dbObjItem, targetType.getComponentType().getType()));
TypeInformation<?> componentType = targetType.getComponentType();
items.add(getPotentiallyConvertedSimpleRead(dbObjItem, componentType == null ? null : componentType.getType()));
}
}
return items;
return getPotentiallyConvertedSimpleRead(items, targetType.getType());
}
/**
@@ -820,12 +774,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
continue;
}
Object key = entry.getKey();
Object key = potentiallyUnescapeMapKey(entry.getKey());
TypeInformation<?> keyTypeInformation = type.getComponentType();
if (keyTypeInformation != null) {
Class<?> keyType = keyTypeInformation.getType();
key = conversionService.convert(entry.getKey(), keyType);
key = conversionService.convert(key, keyType);
}
Object value = entry.getValue();
@@ -864,7 +818,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return null;
}
Class<?> target = conversions.getCustomWriteTarget(getClass());
Class<?> target = conversions.getCustomWriteTarget(obj.getClass());
if (target != null) {
return conversionService.convert(obj, target);
}
@@ -888,46 +842,119 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
if (obj instanceof Map) {
Map<Object, Object> m = new HashMap<Object, Object>();
DBObject result = new BasicDBObject();
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
m.put(entry.getKey(), convertToMongoType(entry.getValue()));
result.put(entry.getKey().toString(), convertToMongoType(entry.getValue()));
}
return m;
}
if (obj instanceof List) {
List<?> l = (List<?>) obj;
List<Object> newList = new ArrayList<Object>();
for (Object o : l) {
newList.add(convertToMongoType(o));
}
return newList;
return result;
}
if (obj.getClass().isArray()) {
return maybeConvertArray((Object[]) obj);
return maybeConvertList(Arrays.asList((Object[]) obj));
}
if (obj instanceof Collection) {
return maybeConvertList((Collection<?>) obj);
}
DBObject newDbo = new BasicDBObject();
this.write(obj, newDbo);
return newDbo;
return removeTypeInfoRecursively(newDbo);
}
public Object[] maybeConvertArray(Object[] src) {
Object[] newArr = new Object[src.length];
for (int i = 0; i < src.length; i++) {
newArr[i] = convertToMongoType(src[i]);
}
return newArr;
}
public BasicDBList maybeConvertList(BasicDBList dbl) {
public BasicDBList maybeConvertList(Iterable<?> source) {
BasicDBList newDbl = new BasicDBList();
Iterator<?> iter = dbl.iterator();
while (iter.hasNext()) {
Object o = iter.next();
newDbl.add(convertToMongoType(o));
for (Object element : source) {
newDbl.add(convertToMongoType(element));
}
return newDbl;
}
/**
* Removes the type information from the conversion result.
*
* @param object
* @return
*/
private Object removeTypeInfoRecursively(Object object) {
if (!(object instanceof DBObject)) {
return object;
}
DBObject dbObject = (DBObject) object;
String keyToRemove = null;
for (String key : dbObject.keySet()) {
if (typeMapper.isTypeKey(key)) {
keyToRemove = key;
}
Object value = dbObject.get(key);
if (value instanceof BasicDBList) {
for (Object element : (BasicDBList) value) {
removeTypeInfoRecursively(element);
}
} else {
removeTypeInfoRecursively(value);
}
}
if (keyToRemove != null) {
dbObject.removeField(keyToRemove);
}
return dbObject;
}
private class DelegatingParameterValueProvider implements ParameterValueProvider {
private final DBObject source;
private final ParameterValueProvider delegate;
private final MappedConstructor constructor;
/**
* {@link ParameterValueProvider} to delegate source object lookup to a {@link SpELAwareParameterValueProvider} in
* case a MappCon
*
* @param constructor must not be {@literal null}.
* @param source must not be {@literal null}.
* @param delegate must not be {@literal null}.
*/
public DelegatingParameterValueProvider(MappedConstructor constructor, DBObject source,
SpELAwareParameterValueProvider delegate) {
Assert.notNull(constructor);
Assert.notNull(source);
Assert.notNull(delegate);
this.constructor = constructor;
this.source = source;
this.delegate = delegate;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
*/
@SuppressWarnings("unchecked")
public <T> T getParameterValue(Parameter<T> parameter) {
MappedConstructor.MappedParameter mappedParameter = constructor.getFor(parameter);
Object value = mappedParameter.hasSpELExpression() ? delegate.getParameterValue(parameter) : source
.get(mappedParameter.getFieldName());
TypeInformation<?> type = mappedParameter.getPropertyTypeInformation();
if (value instanceof DBRef) {
return (T) read(type, ((DBRef) value).fetch());
} else if (value instanceof BasicDBList) {
return (T) getPotentiallyConvertedSimpleRead(readCollectionOrArray(type, (BasicDBList) value), type.getType());
} else if (value instanceof DBObject) {
return (T) read(type, (DBObject) value);
} else {
return (T) getPotentiallyConvertedSimpleRead(value, type.getType());
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,12 @@ package org.springframework.data.mongodb.core.convert;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
@@ -119,4 +123,28 @@ abstract class MongoConverters {
return StringUtils.hasText(source) ? new BigInteger(source) : null;
}
}
public static enum URLToStringConverter implements Converter<URL, String> {
INSTANCE;
public String convert(URL source) {
return source == null ? null : source.toString();
}
}
public static enum StringToURLConverter implements Converter<String, URL> {
INSTANCE;
private static final TypeDescriptor SOURCE = TypeDescriptor.valueOf(String.class);
private static final TypeDescriptor TARGET = TypeDescriptor.valueOf(URL.class);
public URL convert(String source) {
try {
return source == null ? null : new URL(source);
} catch (MalformedURLException e) {
throw new ConversionFailedException(SOURCE, TARGET, source, e);
}
}
}
}

View File

@@ -20,11 +20,16 @@ import org.springframework.data.convert.TypeMapper;
import com.mongodb.DBObject;
/**
* Combining interface to express Mongo specific {@link TypeMapper} implementations will be {@link TypeKeyAware} as
* well.
* Mongo-specific {@link TypeMapper} exposing that {@link DBObject}s might contain a type key.
*
* @author Oliver Gierke
*/
public interface MongoTypeMapper extends TypeMapper<DBObject>, TypeKeyAware {
public interface MongoTypeMapper extends TypeMapper<DBObject> {
/**
* Returns whether the given key is the type key.
*
* @return
*/
boolean isTypeKey(String key);
}

View File

@@ -66,7 +66,7 @@ public class Box implements Shape {
list.add(getUpperRight().asList());
return list;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.Shape#getCommand()
@@ -74,7 +74,7 @@ public class Box implements Shape {
public String getCommand() {
return "$box";
}
@Override
public String toString() {
return String.format("Box [%s, %s]", first, second);

View File

@@ -40,10 +40,10 @@ public class Circle implements Shape {
*/
@PersistenceConstructor
public Circle(Point center, double radius) {
Assert.notNull(center);
Assert.isTrue(radius >= 0, "Radius must not be negative!");
this.center = center;
this.radius = radius;
}
@@ -76,7 +76,7 @@ public class Circle implements Shape {
public double getRadius() {
return radius;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.Shape#asList()
@@ -87,7 +87,7 @@ public class Circle implements Shape {
result.add(getRadius());
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.Shape#getCommand()
@@ -104,26 +104,26 @@ public class Circle implements Shape {
public String toString() {
return String.format("Circle [center=%s, radius=%f]", center, radius);
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
Circle that = (Circle) obj;
return this.center.equals(that.center) && this.radius == that.radius;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()

View File

@@ -50,22 +50,22 @@ public class Polygon implements Shape, Iterable<Point> {
this.points.addAll(Arrays.asList(x, y, z));
this.points.addAll(Arrays.asList(others));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.Shape#asList()
*/
public List<List<Double>> asList() {
List<List<Double>> result = new ArrayList<List<Double>>();
for (Point point : points) {
result.add(point.asList());
}
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.Shape#getCommand()
@@ -73,7 +73,7 @@ public class Polygon implements Shape, Iterable<Point> {
public String getCommand() {
return "$polygon";
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
@@ -88,20 +88,20 @@ public class Polygon implements Shape, Iterable<Point> {
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
Polygon that = (Polygon) obj;
return this.points.equals(that.points);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()

View File

@@ -19,7 +19,7 @@ import java.util.List;
/**
* Common interface for all shapes. Allows building MongoDB representations of them.
*
*
* @author Oliver Gierke
*/
public interface Shape {
@@ -31,7 +31,7 @@ public interface Shape {
* @return
*/
List<? extends Object> asList();
/**
* Returns the command to be used to create the {@literal $within} criterion.
*

View File

@@ -1,11 +1,11 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2012 the original author or authors.
*
* 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
* 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,
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.index;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -24,14 +24,29 @@ import java.lang.annotation.Target;
/**
* Mark a class to use compound indexes.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Jon Brisbin
* @author Oliver Gierke
*/
@Target({ ElementType.TYPE })
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CompoundIndex {
/**
* The actual index definition in JSON format. The keys of the JSON document are the fields to be indexed, the values
* define the index direction (1 for ascending, -1 for descending).
*
* @return
*/
String def();
/**
* It does not actually make sense to use that attribute as the direction has to be defined in the {@link #def()}
* attribute actually.
*
* @return
*/
@Deprecated
IndexDirection direction() default IndexDirection.ASCENDING;
boolean unique() default false;
@@ -40,8 +55,18 @@ public @interface CompoundIndex {
boolean dropDups() default false;
/**
* The name of the index to be created.
*
* @return
*/
String name() default "";
/**
* The collection the index will be created in. Will default to the collection the annotated domain class will be
* stored in.
*
* @return
*/
String collection() default "";
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core.index;
import org.springframework.data.mongodb.core.query.Order;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Value object for an index field.
*
* @author Oliver Gierke
*/
public final class IndexField {
private final String key;
private final Order order;
private final boolean isGeo;
private IndexField(String key, Order order, boolean isGeo) {
Assert.hasText(key);
Assert.isTrue(order != null ^ isGeo);
this.key = key;
this.order = order;
this.isGeo = isGeo;
}
/**
* Creates a default {@link IndexField} with the given key and {@link Order}.
*
* @param key must not be {@literal null} or emtpy.
* @param order must not be {@literal null}.
* @return
*/
public static IndexField create(String key, Order order) {
Assert.notNull(order);
return new IndexField(key, order, false);
}
/**
* Creates a geo {@link IndexField} for the given key.
*
* @param key must not be {@literal null} or empty.
* @return
*/
public static IndexField geo(String key) {
return new IndexField(key, null, true);
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* Returns the order of the {@link IndexField} or {@literal null} in case we have a geo index field.
*
* @return the order
*/
public Order getOrder() {
return order;
}
/**
* Returns whether the {@link IndexField} is a geo index field.
*
* @return the isGeo
*/
public boolean isGeo() {
return isGeo;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof IndexField)) {
return false;
}
IndexField that = (IndexField) obj;
return this.key.equals(that.key) && ObjectUtils.nullSafeEquals(this.order, that.order) && this.isGeo == that.isGeo;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * ObjectUtils.nullSafeHashCode(key);
result += 31 * ObjectUtils.nullSafeHashCode(order);
result += 31 * ObjectUtils.nullSafeHashCode(isGeo);
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("IndexField [ key: %s, order: %s, isGeo: %s]", key, order, isGeo);
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core.index;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.data.mongodb.core.query.Order;
import org.springframework.util.ObjectUtils;
public class IndexInfo {
private final Map<String, Order> fieldSpec;
private final List<IndexField> indexFields;
private final String name;
private final boolean unique;
private final boolean dropDuplicates;
private final boolean sparse;
public IndexInfo(Map<String, Order> fieldSpec, List<IndexField> indexFields, String name, boolean unique,
boolean dropDuplicates, boolean sparse) {
this.fieldSpec = fieldSpec;
this.indexFields = Collections.unmodifiableList(indexFields);
this.name = name;
this.unique = unique;
this.dropDuplicates = dropDuplicates;
this.sparse = sparse;
}
/**
* @deprecated use {@link #getIndexFields()} instead as this {@link Map} does not contain geo indexes.
* @return
*/
@Deprecated
public Map<String, Order> getFieldSpec() {
return fieldSpec;
}
/**
* Returns the individual index fields of the index.
*
* @return
*/
public List<IndexField> getIndexFields() {
return this.indexFields;
}
public String getName() {
return name;
}
public boolean isUnique() {
return unique;
}
public boolean isDropDuplicates() {
return dropDuplicates;
}
public boolean isSparse() {
return sparse;
}
@Override
public String toString() {
return "IndexInfo [indexFields=" + indexFields + ", name=" + name + ", unique=" + unique + ", dropDuplicates="
+ dropDuplicates + ", sparse=" + sparse + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (dropDuplicates ? 1231 : 1237);
result = prime * result + ObjectUtils.nullSafeHashCode(indexFields);
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + (sparse ? 1231 : 1237);
result = prime * result + (unique ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
IndexInfo other = (IndexInfo) obj;
if (dropDuplicates != other.dropDuplicates) {
return false;
}
if (indexFields == null) {
if (other.indexFields != null) {
return false;
}
} else if (!indexFields.equals(other.indexFields)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (sparse != other.sparse) {
return false;
}
if (unique != other.unique) {
return false;
}
return true;
}
}

View File

@@ -23,12 +23,12 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
/**
* An implementation of ApplicationEventPublisher that will only fire MappingContextEvents for use by the index creator when
* MongoTemplate is used 'stand-alone', that is not declared inside a Spring ApplicationContext.
* An implementation of ApplicationEventPublisher that will only fire MappingContextEvents for use by the index creator
* when MongoTemplate is used 'stand-alone', that is not declared inside a Spring ApplicationContext.
*
* Declare MongoTemplate inside an ApplicationContext to enable the publishing of all persistence events such as
* {@link AfterLoadEvent}, {@link AfterSaveEvent}, etc.
*
* Declare MongoTemplate inside an ApplicationContext to enable the publishing of all persistence events such as
* {@link AfterLoadEvent}, {@link AfterSaveEvent}, etc.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoMappingEventPublisher implements ApplicationEventPublisher {

View File

@@ -1,11 +1,11 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2012 the original author or authors.
*
* 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
* 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,
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.index;
import java.lang.reflect.Field;
@@ -23,10 +22,10 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.event.MappingContextEvent;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
@@ -38,10 +37,10 @@ import com.mongodb.DBObject;
import com.mongodb.util.JSON;
/**
* Component that inspects {@link BasicMongoPersistentEntity} instances contained in the given
* {@link MongoMappingContext} for indexing metadata and ensures the indexes to be available.
* Component that inspects {@link MongoPersistentEntity} instances contained in the given {@link MongoMappingContext}
* for indexing metadata and ensures the indexes to be available.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Jon Brisbin
* @author Oliver Gierke
*/
public class MongoPersistentEntityIndexCreator implements
@@ -76,7 +75,13 @@ public class MongoPersistentEntityIndexCreator implements
*/
public void onApplicationEvent(
MappingContextEvent<MongoPersistentEntity<MongoPersistentProperty>, MongoPersistentProperty> event) {
checkForIndexes(event.getPersistentEntity());
PersistentEntity<?, ?> entity = event.getPersistentEntity();
// Double check type as Spring infrastructure does not consider nested generics
if (entity instanceof MongoPersistentEntity) {
checkForIndexes(event.getPersistentEntity());
}
}
protected void checkForIndexes(final MongoPersistentEntity<?> entity) {
@@ -90,12 +95,12 @@ public class MongoPersistentEntityIndexCreator implements
if (type.isAnnotationPresent(CompoundIndexes.class)) {
CompoundIndexes indexes = type.getAnnotation(CompoundIndexes.class);
for (CompoundIndex index : indexes.value()) {
String indexColl = index.collection();
if ("".equals(indexColl)) {
indexColl = entity.getCollection();
}
ensureIndex(indexColl, index.name(), index.def(), index.direction(), index.unique(), index.dropDups(),
index.sparse());
String indexColl = StringUtils.hasText(index.collection()) ? index.collection() : entity.getCollection();
DBObject definition = (DBObject) JSON.parse(index.def());
ensureIndex(indexColl, index.name(), definition, index.unique(), index.dropDups(), index.sparse());
if (log.isDebugEnabled()) {
log.debug("Created compound index " + index);
}
@@ -104,10 +109,14 @@ public class MongoPersistentEntityIndexCreator implements
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) {
Field field = persistentProperty.getField();
if (field.isAnnotationPresent(Indexed.class)) {
Indexed index = field.getAnnotation(Indexed.class);
String name = index.name();
if (!StringUtils.hasText(name)) {
name = persistentProperty.getFieldName();
} else {
@@ -119,11 +128,17 @@ public class MongoPersistentEntityIndexCreator implements
}
}
}
String collection = StringUtils.hasText(index.collection()) ? index.collection() : entity.getCollection();
ensureIndex(collection, name, null, index.direction(), index.unique(), index.dropDups(), index.sparse());
int direction = index.direction() == IndexDirection.ASCENDING ? 1 : -1;
DBObject definition = new BasicDBObject(persistentProperty.getFieldName(), direction);
ensureIndex(collection, name, definition, index.unique(), index.dropDups(), index.sparse());
if (log.isDebugEnabled()) {
log.debug("Created property index " + index);
}
} else if (field.isAnnotationPresent(GeoSpatialIndexed.class)) {
GeoSpatialIndexed index = field.getAnnotation(GeoSpatialIndexed.class);
@@ -148,21 +163,15 @@ public class MongoPersistentEntityIndexCreator implements
}
}
protected void ensureIndex(String collection, final String name, final String def, final IndexDirection direction,
final boolean unique, final boolean dropDups, final boolean sparse) {
DBObject defObj;
if (null != def) {
defObj = (DBObject) JSON.parse(def);
} else {
defObj = new BasicDBObject();
defObj.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1));
}
protected void ensureIndex(String collection, String name, DBObject indexDefinition, boolean unique,
boolean dropDups, boolean sparse) {
DBObject opts = new BasicDBObject();
opts.put("name", name);
opts.put("dropDups", dropDups);
opts.put("sparse", sparse);
opts.put("unique", unique);
mongoDbFactory.getDb().getCollection(collection).ensureIndex(defObj, opts);
}
mongoDbFactory.getDb().getCollection(collection).ensureIndex(indexDefinition, opts);
}
}

View File

@@ -53,7 +53,7 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
if (this.isIdProperty == null) {
this.isIdProperty = super.isIdProperty();
}
return this.isIdProperty;
}
@@ -67,7 +67,7 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
if (this.fieldName == null) {
this.fieldName = super.getFieldName();
}
return super.getFieldName();
return this.fieldName;
}
}

View File

@@ -30,7 +30,8 @@ import org.springframework.data.util.TypeInformation;
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Oliver Gierke ogierke@vmware.com
*/
public class MongoMappingContext extends AbstractMappingContext<BasicMongoPersistentEntity<?>, MongoPersistentProperty> implements ApplicationContextAware {
public class MongoMappingContext extends AbstractMappingContext<BasicMongoPersistentEntity<?>, MongoPersistentProperty>
implements ApplicationContextAware {
private ApplicationContext context;
@@ -57,16 +58,16 @@ public class MongoMappingContext extends AbstractMappingContext<BasicMongoPersis
*/
@Override
protected <T> BasicMongoPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
BasicMongoPersistentEntity<T> entity = new BasicMongoPersistentEntity<T>(typeInformation);
if (context != null) {
entity.setApplicationContext(context);
}
return entity;
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)

View File

@@ -19,11 +19,15 @@ import java.math.BigInteger;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import org.bson.types.Binary;
import org.bson.types.CodeWScope;
import org.bson.types.ObjectId;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
/**
@@ -33,19 +37,23 @@ import com.mongodb.DBRef;
*/
public abstract class MongoSimpleTypes {
public static final Set<Class<?>> SUPPORTED_ID_CLASSES;
public static final Set<Class<?>> AUTOGENERATED_ID_TYPES;
static {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(ObjectId.class);
classes.add(String.class);
classes.add(BigInteger.class);
SUPPORTED_ID_CLASSES = Collections.unmodifiableSet(classes);
AUTOGENERATED_ID_TYPES = Collections.unmodifiableSet(classes);
Set<Class<?>> simpleTypes = new HashSet<Class<?>>();
simpleTypes.add(DBRef.class);
simpleTypes.add(ObjectId.class);
simpleTypes.add(CodeWScope.class);
simpleTypes.add(DBObject.class);
simpleTypes.add(Pattern.class);
simpleTypes.add(Binary.class);
simpleTypes.add(UUID.class);
MONGO_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
}

View File

@@ -31,12 +31,13 @@ public abstract class AbstractMongoEventListener<E> implements ApplicationListen
protected final Log LOG = LogFactory.getLog(getClass());
private final Class<?> domainClass;
/**
* Creates a new {@link AbstractMongoEventListener}.
*/
public AbstractMongoEventListener() {
this.domainClass = GenericTypeResolver.resolveTypeArgument(this.getClass(), AbstractMongoEventListener.class);
Class<?> typeArgument = GenericTypeResolver.resolveTypeArgument(this.getClass(), AbstractMongoEventListener.class);
this.domainClass = typeArgument == null ? Object.class : typeArgument;
}
/*

View File

@@ -39,9 +39,9 @@ public class AfterLoadEvent<T> extends MongoMappingEvent<DBObject> {
* @param type must not be {@literal null}.
*/
public AfterLoadEvent(DBObject dbo, Class<T> type) {
super(dbo, dbo);
Assert.notNull(type, "Type must not be null!");
this.type = type;
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2010-2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core.mapreduce;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Collects the parameters required to perform a group operation on a collection. The query condition and the input
* collection are specified on the group method as method arguments to be consistent with other operations, e.g.
* map-reduce.
*
* @author Mark Pollack
*/
public class GroupBy {
private DBObject dboKeys;
private String keyFunction;
private String initial;
private DBObject initialDbObject;
private String reduce;
private String finalize;
public GroupBy(String... keys) {
DBObject dbo = new BasicDBObject();
for (String key : keys) {
dbo.put(key, 1);
}
dboKeys = dbo;
}
// NOTE GroupByCommand does not handle keyfunction.
public GroupBy(String key, boolean isKeyFunction) {
DBObject dbo = new BasicDBObject();
if (isKeyFunction) {
keyFunction = key;
} else {
dbo.put(key, 1);
dboKeys = dbo;
}
}
public static GroupBy keyFunction(String key) {
return new GroupBy(key, true);
}
public static GroupBy key(String... keys) {
return new GroupBy(keys);
}
public GroupBy initialDocument(String initialDocument) {
initial = initialDocument;
return this;
}
public GroupBy initialDocument(DBObject initialDocument) {
initialDbObject = initialDocument;
return this;
}
public GroupBy reduceFunction(String reduceFunction) {
reduce = reduceFunction;
return this;
}
public GroupBy finalizeFunction(String finalizeFunction) {
finalize = finalizeFunction;
return this;
}
public DBObject getGroupByObject() {
// return new GroupCommand(dbCollection, dboKeys, condition, initial, reduce, finalize);
BasicDBObject dbo = new BasicDBObject();
if (dboKeys != null) {
dbo.put("key", dboKeys);
}
if (keyFunction != null) {
dbo.put("$keyf", keyFunction);
}
dbo.put("$reduce", reduce);
dbo.put("initial", initialDbObject);
if (initial != null) {
dbo.put("initial", initial);
}
if (finalize != null) {
dbo.put("finalize", finalize);
}
return dbo;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core.mapreduce;
import java.util.Iterator;
import java.util.List;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* Collects the results of executing a group operation.
*
* @author Mark Pollack
* @param <T> The class in which the results are mapped onto, accessible via an interator.
*/
public class GroupByResults<T> implements Iterable<T> {
private final List<T> mappedResults;
private final DBObject rawResults;
private double count;
private int keys;
private String serverUsed;
public GroupByResults(List<T> mappedResults, DBObject rawResults) {
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
this.mappedResults = mappedResults;
this.rawResults = rawResults;
parseKeys();
parseCount();
parseServerUsed();
}
public double getCount() {
return count;
}
public int getKeys() {
return keys;
}
public String getServerUsed() {
return serverUsed;
}
public Iterator<T> iterator() {
return mappedResults.iterator();
}
public DBObject getRawResults() {
return rawResults;
}
private void parseCount() {
Object object = rawResults.get("count");
if (object instanceof Double) {
count = (Double) object;
}
}
private void parseKeys() {
Object object = rawResults.get("keys");
if (object instanceof Integer) {
keys = (Integer) object;
}
}
private void parseServerUsed() {
// "serverUsed" : "127.0.0.1:27017"
Object object = rawResults.get("serverUsed");
if (object instanceof String) {
serverUsed = (String) object;
}
}
}

View File

@@ -15,13 +15,14 @@
*/
package org.springframework.data.mongodb.core.mapreduce;
/**
* @author Mark Pollack
*/
public class MapReduceCounts {
private int inputCount;
private int emitCount;
private int outputCount;
private final int inputCount;
private final int emitCount;
private final int outputCount;
public MapReduceCounts(int inputCount, int emitCount, int outputCount) {
super();
@@ -42,12 +43,20 @@ public class MapReduceCounts {
return outputCount;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "MapReduceCounts [inputCount=" + inputCount + ", emitCount=" + emitCount + ", outputCount=" + outputCount
+ "]";
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
@@ -58,24 +67,31 @@ public class MapReduceCounts {
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
if (this == obj) {
return true;
if (obj == null)
}
if (obj == null) {
return false;
if (getClass() != obj.getClass())
}
if (getClass() != obj.getClass()) {
return false;
}
MapReduceCounts other = (MapReduceCounts) obj;
if (emitCount != other.emitCount)
if (emitCount != other.emitCount) {
return false;
if (inputCount != other.inputCount)
}
if (inputCount != other.inputCount) {
return false;
if (outputCount != other.outputCount)
}
if (outputCount != other.outputCount) {
return false;
}
return true;
}
}

View File

@@ -39,15 +39,13 @@ public class MapReduceOptions {
private Boolean jsMode;
private Boolean verbose = true;
private Map<String, Object> extraOptions = new HashMap<String, Object>();
/**
* Static factory method to create a Criteria using the provided key
* Static factory method to create a MapReduceOptions instance
*
* @param key
* @return
* @return a new instance
*/
public static MapReduceOptions options() {
return new MapReduceOptions();
@@ -190,12 +188,12 @@ public class MapReduceOptions {
this.verbose = verbose;
return this;
}
/**
* Add additional extra options that may not have a method on this class. This method will help if you use a
* version of this client library with a server version that has added additional map-reduce options that do not
* yet have an method for use in setting them.
* options
* Add additional extra options that may not have a method on this class. This method will help if you use a version
* of this client library with a server version that has added additional map-reduce options that do not yet have an
* method for use in setting them. options
*
* @param key The key option
* @param value The value of the option
* @return MapReduceOptions so that methods can be chained in a fluent API style
@@ -204,40 +202,39 @@ public class MapReduceOptions {
extraOptions.put(key, value);
return this;
}
public Map<String, Object> getExtraOptions() {
return extraOptions;
return extraOptions;
}
public String getFinalizeFunction() {
return this.finalizeFunction;
}
public Boolean getJavaScriptMode() {
return this.jsMode;
}
public String getOutputCollection() {
return this.outputCollection;
}
public String getOutputDatabase() {
return this.outputDatabase;
}
public Boolean getOutputSharded() {
return this.outputSharded;
}
public MapReduceCommand.OutputType getOutputType() {
return this.outputType;
}
public Map<String, Object> getScopeVariables() {
return this.scopeVariables;
}
public DBObject getOptionsObject() {
BasicDBObject cmd = new BasicDBObject();
@@ -254,7 +251,7 @@ public class MapReduceOptions {
if (scopeVariables != null) {
cmd.put("scope", scopeVariables);
}
if (!extraOptions.keySet().isEmpty()) {
cmd.putAll(extraOptions);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,73 +22,128 @@ import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* Collects the results of performing a MapReduce operations.
*
* @author Mark Pollack
* @author Oliver Gierke
* @param <T> The class in which the results are mapped onto, accessible via an iterator.
*/
public class MapReduceResults<T> implements Iterable<T> {
private final List<T> mappedResults;
private DBObject rawResults;
private final DBObject rawResults;
private final String outputCollection;
private final MapReduceTiming mapReduceTiming;
private final MapReduceCounts mapReduceCounts;
private MapReduceTiming mapReduceTiming;
private MapReduceCounts mapReduceCounts;
private String outputCollection;
/**
* Creates a new {@link MapReduceResults} from the given mapped results and the raw one.
*
* @param mappedResults must not be {@literal null}.
* @param rawResults must not be {@literal null}.
*/
public MapReduceResults(List<T> mappedResults, DBObject rawResults) {
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
this.mappedResults = mappedResults;
this.rawResults = rawResults;
parseTiming(rawResults);
parseCounts(rawResults);
if (rawResults.get("result") != null) {
this.outputCollection = (String) rawResults.get("result");
}
this.mapReduceTiming = parseTiming(rawResults);
this.mapReduceCounts = parseCounts(rawResults);
this.outputCollection = parseOutputCollection(rawResults);
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<T> iterator() {
return mappedResults.iterator();
return mappedResults.iterator();
}
public MapReduceTiming getTiming() {
return mapReduceTiming;
}
public MapReduceCounts getCounts() {
return mapReduceCounts;
}
public String getOutputCollection() {
return outputCollection;
}
public DBObject getRawResults() {
return rawResults;
}
protected void parseTiming(DBObject rawResults) {
private MapReduceTiming parseTiming(DBObject rawResults) {
DBObject timing = (DBObject) rawResults.get("timing");
if (timing != null) {
if (timing.get("mapTime") != null && timing.get("emitLoop") != null && timing.get("total") != null) {
mapReduceTiming = new MapReduceTiming( (Long)timing.get("mapTime"),
(Integer)timing.get("emitLoop"),
(Integer)timing.get("total"));
}
} else {
mapReduceTiming = new MapReduceTiming(-1,-1,-1);
if (timing == null) {
return new MapReduceTiming(-1, -1, -1);
}
if (timing.get("mapTime") != null && timing.get("emitLoop") != null && timing.get("total") != null) {
return new MapReduceTiming(getAsLong(timing, "mapTime"), getAsLong(timing, "emitLoop"),
getAsLong(timing, "total"));
}
return new MapReduceTiming(-1, -1, -1);
}
protected void parseCounts(DBObject rawResults) {
/**
* Returns the value of the source's field with the given key as {@link Long}.
*
* @param source
* @param key
* @return
*/
private Long getAsLong(DBObject source, String key) {
Object raw = source.get(key);
return raw instanceof Long ? (Long) raw : (Integer) raw;
}
/**
* Parses the raw {@link DBObject} result into a {@link MapReduceCounts} value object.
*
* @param rawResults
* @return
*/
private MapReduceCounts parseCounts(DBObject rawResults) {
DBObject counts = (DBObject) rawResults.get("counts");
if (counts != null) {
if (counts.get("input") != null && counts.get("emit") != null && counts.get("output") != null) {
mapReduceCounts = new MapReduceCounts( (Integer)counts.get("input"), (Integer)counts.get("emit"), (Integer)counts.get("output"));
}
} else {
mapReduceCounts = new MapReduceCounts(-1,-1,-1);
if (counts == null) {
return new MapReduceCounts(-1, -1, -1);
}
if (counts.get("input") != null && counts.get("emit") != null && counts.get("output") != null) {
return new MapReduceCounts((Integer) counts.get("input"), (Integer) counts.get("emit"),
(Integer) counts.get("output"));
}
return new MapReduceCounts(-1, -1, -1);
}
/**
* Parses the output collection from the raw {@link DBObject} result.
*
* @param rawResults
* @return
*/
private String parseOutputCollection(DBObject rawResults) {
Object resultField = rawResults.get("result");
if (resultField == null) {
return null;
}
return resultField instanceof DBObject ? ((DBObject) resultField).get("collection").toString() : resultField
.toString();
}
}

View File

@@ -18,15 +18,15 @@ package org.springframework.data.mongodb.core.mapreduce;
public class MapReduceTiming {
private long mapTime;
private long emitLoopTime;
private long totalTime;
public MapReduceTiming(long mapTime, long emitLoopTime, long totalTime) {
this.mapTime = mapTime;
this.emitLoopTime = emitLoopTime;
this.totalTime = totalTime;
this.totalTime = totalTime;
}
public long getMapTime() {
@@ -73,8 +73,5 @@ public class MapReduceTiming {
return false;
return true;
}
}

View File

@@ -15,18 +15,23 @@
*/
package org.springframework.data.mongodb.core.query;
import static org.springframework.util.ObjectUtils.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.regex.Pattern;
import org.bson.BSON;
import org.bson.types.BasicBSONList;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.core.geo.Circle;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.mongodb.core.geo.Shape;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.ObjectUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -68,7 +73,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Static factory method to create a Criteria using the provided key
*
*
* @param key
* @return
*/
@@ -78,7 +83,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Static factory method to create a Criteria using the provided key
*
*
* @return
*/
public Criteria and(String key) {
@@ -87,7 +92,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using equality
*
*
* @param o
* @return
*/
@@ -96,16 +101,20 @@ public class Criteria implements CriteriaDefinition {
throw new InvalidMongoDbApiUsageException(
"Multiple 'is' values declared. You need to use 'and' with multiple criteria");
}
if (this.criteria.size() > 0 && "$not".equals(this.criteria.keySet().toArray()[this.criteria.size() - 1])) {
if (lastOperatorWasNot()) {
throw new InvalidMongoDbApiUsageException("Invalid query: 'not' can't be used with 'is' - use 'ne' instead.");
}
this.isValue = o;
return this;
}
private boolean lastOperatorWasNot() {
return this.criteria.size() > 0 && "$not".equals(this.criteria.keySet().toArray()[this.criteria.size() - 1]);
}
/**
* Creates a criterion using the $ne operator
*
*
* @param o
* @return
*/
@@ -116,7 +125,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $lt operator
*
*
* @param o
* @return
*/
@@ -127,7 +136,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $lte operator
*
*
* @param o
* @return
*/
@@ -138,7 +147,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $gt operator
*
*
* @param o
* @return
*/
@@ -149,7 +158,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $gte operator
*
*
* @param o
* @return
*/
@@ -160,7 +169,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $in operator
*
*
* @param o the values to match against
* @return
*/
@@ -169,35 +178,39 @@ public class Criteria implements CriteriaDefinition {
throw new InvalidMongoDbApiUsageException("You can only pass in one argument of type "
+ o[1].getClass().getName());
}
criteria.put("$in", o);
criteria.put("$in", Arrays.asList(o));
return this;
}
/**
* Creates a criterion using the $in operator
*
*
* @param c the collection containing the values to match against
* @return
*/
public Criteria in(Collection<?> c) {
criteria.put("$in", c.toArray());
criteria.put("$in", c);
return this;
}
/**
* Creates a criterion using the $nin operator
*
*
* @param o
* @return
*/
public Criteria nin(Object... o) {
return nin(Arrays.asList(o));
}
public Criteria nin(Collection<?> o) {
criteria.put("$nin", o);
return this;
}
/**
* Creates a criterion using the $mod operator
*
*
* @param value
* @param remainder
* @return
@@ -212,18 +225,22 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $all operator
*
*
* @param o
* @return
*/
public Criteria all(Object... o) {
return all(Arrays.asList(o));
}
public Criteria all(Collection<?> o) {
criteria.put("$all", o);
return this;
}
/**
* Creates a criterion using the $size operator
*
*
* @param s
* @return
*/
@@ -234,7 +251,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $exists operator
*
*
* @param b
* @return
*/
@@ -245,7 +262,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $type operator
*
*
* @param t
* @return
*/
@@ -256,43 +273,65 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $not meta operator which affects the clause directly following
*
*
* @return
*/
public Criteria not() {
criteria.put("$not", null);
return not(null);
}
private Criteria not(Object value) {
criteria.put("$not", value);
return this;
}
/**
* Creates a criterion using a $regex
*
*
* @param re
* @return
*/
public Criteria regex(String re) {
criteria.put("$regex", re);
return this;
return regex(re, null);
}
/**
* Creates a criterion using a $regex and $options
*
*
* @param re
* @param options
* @return
*/
public Criteria regex(String re, String options) {
criteria.put("$regex", re);
if (StringUtils.hasText(options)) {
criteria.put("$options", options);
return regex(toPattern(re, options));
}
/**
* Syntactical sugar for {@link #is(Object)} making obvious that we create a regex predicate.
*
* @param pattern
* @return
*/
public Criteria regex(Pattern pattern) {
Assert.notNull(pattern);
if (lastOperatorWasNot()) {
return not(pattern);
}
this.isValue = pattern;
return this;
}
private Pattern toPattern(String regex, String options) {
Assert.notNull(regex);
return Pattern.compile(regex, options == null ? 0 : BSON.regexFlags(options));
}
/**
* Creates a geospatial criterion using a $within $center operation. This is only available for Mongo 1.7 and higher.
*
*
* @param circle must not be {@literal null}
* @return
*/
@@ -311,7 +350,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a geospatial criterion using a $near operation
*
*
* @param point must not be {@literal null}
* @return
*/
@@ -323,7 +362,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a geospatial criterion using a $nearSphere operation. This is only available for Mongo 1.7 and higher.
*
*
* @param point must not be {@literal null}
* @return
*/
@@ -335,7 +374,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a geospatical criterion using a $maxDistance operation, for use with $near
*
*
* @param maxDistance
* @return
*/
@@ -346,7 +385,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a criterion using the $elemMatch operator
*
*
* @param c
* @return
*/
@@ -357,7 +396,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates an 'or' criteria using the $or operator for all of the provided criteria
*
*
* @param criteria
*/
public Criteria orOperator(Criteria... criteria) {
@@ -368,7 +407,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates a 'nor' criteria using the $nor operator for all of the provided criteria
*
*
* @param criteria
*/
public Criteria norOperator(Criteria... criteria) {
@@ -379,7 +418,7 @@ public class Criteria implements CriteriaDefinition {
/**
* Creates an 'and' criteria using the $and operator for all of the provided criteria
*
*
* @param criteria
*/
public Criteria andOperator(Criteria... criteria) {
@@ -388,17 +427,14 @@ public class Criteria implements CriteriaDefinition {
return this;
}
public String getKey() {
return this.key;
}
/*
* (non-Javadoc)
*
* @see org.springframework.datastore.document.mongodb.query.Criteria#
* getCriteriaObject(java.lang.String)
*/
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.CriteriaDefinition#getCriteriaObject()
*/
public DBObject getCriteriaObject() {
if (this.criteriaChain.size() == 1) {
return criteriaChain.get(0).getSingleCriteriaObject();
@@ -418,16 +454,17 @@ public class Criteria implements CriteriaDefinition {
DBObject dbo = new BasicDBObject();
boolean not = false;
for (String k : this.criteria.keySet()) {
Object value = this.criteria.get(k);
if (not) {
DBObject notDbo = new BasicDBObject();
notDbo.put(k, this.criteria.get(k));
notDbo.put(k, value);
dbo.put("$not", notDbo);
not = false;
} else {
if ("$not".equals(k)) {
if ("$not".equals(k) && value == null) {
not = true;
} else {
dbo.put(k, this.criteria.get(k));
dbo.put(k, value);
}
}
}
@@ -453,12 +490,89 @@ public class Criteria implements CriteriaDefinition {
Object existing = dbo.get(key);
if (existing == null) {
dbo.put(key, value);
}
else {
throw new InvalidMongoDbApiUsageException("Due to limitations of the com.mongodb.BasicDBObject, " +
"you can't add a second '" + key + "' expression specified as '" + key + " : " + value + "'. " +
"Criteria already contains '" + key + " : " + existing + "'.");
} else {
throw new InvalidMongoDbApiUsageException("Due to limitations of the com.mongodb.BasicDBObject, "
+ "you can't add a second '" + key + "' expression specified as '" + key + " : " + value + "'. "
+ "Criteria already contains '" + key + " : " + existing + "'.");
}
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
Criteria that = (Criteria) obj;
if (this.criteriaChain.size() != that.criteriaChain.size()) {
return false;
}
for (int i = 0; i < this.criteriaChain.size(); i++) {
Criteria left = this.criteriaChain.get(i);
Criteria right = that.criteriaChain.get(i);
if (!simpleCriteriaEquals(left, right)) {
return false;
}
}
return true;
}
private boolean simpleCriteriaEquals(Criteria left, Criteria right) {
boolean keyEqual = left.key == null ? right.key == null : left.key.equals(right.key);
boolean criteriaEqual = left.criteria.equals(right.criteria);
boolean valueEqual = isEqual(left.isValue, right.isValue);
return keyEqual && criteriaEqual && valueEqual;
}
/**
* Checks the given objects for equality. Handles {@link Pattern} and arrays correctly.
*
* @param left
* @param right
* @return
*/
private boolean isEqual(Object left, Object right) {
if (left == null) {
return right == null;
}
if (left instanceof Pattern) {
return right instanceof Pattern ? ((Pattern) left).pattern().equals(((Pattern) right).pattern()) : false;
}
return ObjectUtils.nullSafeEquals(left, right);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += nullSafeHashCode(key);
result += criteria.hashCode();
result += nullSafeHashCode(isValue);
return result;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2011 the original author or authors.
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,12 @@ package org.springframework.data.mongodb.core.query;
import java.util.ArrayList;
import java.util.List;
/**
* @deprecated use {@link Criteria#orOperator(Criteria...)} instead.
* @author Thomas Risberg
* @author Oliver Gierke
*/
@Deprecated
public class OrQuery extends Query {
public OrQuery(Query... q) {
@@ -31,5 +37,4 @@ public class OrQuery extends Query {
}
return new Criteria(criteriaList, "$or");
}
}

View File

@@ -15,13 +15,18 @@
*/
package org.springframework.data.mongodb.core.query;
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import static org.springframework.util.ObjectUtils.*;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
public class Query {
@@ -30,10 +35,11 @@ public class Query {
private Sort sort;
private int skip;
private int limit;
private String hint;
/**
* Static factory method to create a Query using the provided criteria
*
*
* @param critera
* @return
*/
@@ -53,20 +59,17 @@ public class Query {
String key = criteria.getKey();
if (existing == null) {
this.criteria.put(key, criteria);
}
else {
throw new InvalidMongoDbApiUsageException("Due to limitations of the com.mongodb.BasicDBObject, " +
"you can't add a second '" + key + "' criteria. " +
"Query already contains '" + existing.getCriteriaObject() + "'.");
} else {
throw new InvalidMongoDbApiUsageException("Due to limitations of the com.mongodb.BasicDBObject, "
+ "you can't add a second '" + key + "' criteria. " + "Query already contains '"
+ existing.getCriteriaObject() + "'.");
}
return this;
}
public Field fields() {
synchronized (this) {
if (fieldSpec == null) {
this.fieldSpec = new Field();
}
if (fieldSpec == null) {
this.fieldSpec = new Field();
}
return this.fieldSpec;
}
@@ -81,12 +84,23 @@ public class Query {
return this;
}
/**
* Configures the query to use the given hint when being executed.
*
* @param name must not be {@literal null} or empty.
* @return
*/
public Query withHint(String name) {
Assert.hasText(name, "Hint must not be empty or null!");
this.hint = name;
return this;
}
public Sort sort() {
synchronized (this) {
if (this.sort == null) {
this.sort = new Sort();
}
if (this.sort == null) {
this.sort = new Sort();
}
return this.sort;
}
@@ -122,7 +136,67 @@ public class Query {
return this.limit;
}
public String getHint() {
return hint;
}
protected List<Criteria> getCriteria() {
return new ArrayList<Criteria>(this.criteria.values());
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Query: %s, Fields: %s, Sort: %s", serializeToJsonSafely(getQueryObject()),
serializeToJsonSafely(getFieldsObject()), serializeToJsonSafely(getSortObject()));
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
Query that = (Query) obj;
boolean criteriaEqual = this.criteria.equals(that.criteria);
boolean fieldsEqual = this.fieldSpec == null ? that.fieldSpec == null : this.fieldSpec.equals(that.fieldSpec);
boolean sortEqual = this.sort == null ? that.sort == null : this.sort.equals(that.sort);
boolean hintEqual = this.hint == null ? that.hint == null : this.hint.equals(that.hint);
boolean skipEqual = this.skip == that.skip;
boolean limitEqual = this.limit == that.limit;
return criteriaEqual && fieldsEqual && sortEqual && hintEqual && skipEqual && limitEqual;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * criteria.hashCode();
result += 31 * nullSafeHashCode(fieldSpec);
result += 31 * nullSafeHashCode(sort);
result += 31 * nullSafeHashCode(hint);
result += 31 * skip;
result += 31 * limit;
return result;
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core.query;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.core.convert.converter.Converter;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
/**
* Utility methods for JSON serialization.
*
* @author Oliver Gierke
*/
public abstract class SerializationUtils {
private SerializationUtils() {
}
/**
* Serializes the given object into pseudo-JSON meaning it's trying to create a JSON representation as far as possible
* but falling back to the given object's {@link Object#toString()} method if it's not serializable. Useful for
* printing raw {@link DBObject}s containing complex values before actually converting them into Mongo native types.
*
* @param value
* @return
*/
public static String serializeToJsonSafely(Object value) {
if (value == null) {
return null;
}
try {
return JSON.serialize(value);
} catch (Exception e) {
if (value instanceof Collection) {
return toString((Collection<?>) value);
} else if (value instanceof Map) {
return toString((Map<?, ?>) value);
} else if (value instanceof DBObject) {
return toString(((DBObject) value).toMap());
} else {
return String.format("{ $java : %s }", value.toString());
}
}
}
private static String toString(Map<?, ?> source) {
return iterableToDelimitedString(source.entrySet(), "{ ", " }", new Converter<Entry<?, ?>, Object>() {
public Object convert(Entry<?, ?> source) {
return String.format("\"%s\" : %s", source.getKey(), serializeToJsonSafely(source.getValue()));
}
});
}
private static String toString(Collection<?> source) {
return iterableToDelimitedString(source, "[ ", " ]", new Converter<Object, Object>() {
public Object convert(Object source) {
return serializeToJsonSafely(source);
}
});
}
/**
* Creates a string representation from the given {@link Iterable} prepending the postfix, applying the given
* {@link Converter} to each element before adding it to the result {@link String}, concatenating each element with
* {@literal ,} and applying the postfix.
*
* @param source
* @param prefix
* @param postfix
* @param transformer
* @return
*/
private static <T> String iterableToDelimitedString(Iterable<T> source, String prefix, String postfix,
Converter<? super T, Object> transformer) {
StringBuilder builder = new StringBuilder(prefix);
Iterator<T> iterator = source.iterator();
while (iterator.hasNext()) {
builder.append(transformer.convert(iterator.next()));
if (iterator.hasNext()) {
builder.append(", ");
}
}
return builder.append(postfix).toString();
}
}

View File

@@ -48,7 +48,7 @@ public class MongoRepositoryConfigParser extends
protected void postProcessBeanDefinition(MongoRepositoryConfiguration context, BeanDefinitionBuilder builder,
BeanDefinitionRegistry registry, Object beanSource) {
builder.addPropertyReference("template", context.getMongoTemplateRef());
builder.addPropertyReference("mongoOperations", context.getMongoTemplateRef());
builder.addPropertyValue("createIndexesForQueryMethods", context.getCreateQueryIndexes());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2010-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,9 +21,9 @@ import java.util.List;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.CollectionCallback;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.GeoPage;
import org.springframework.data.mongodb.core.geo.GeoResult;
import org.springframework.data.mongodb.core.geo.GeoResults;
import org.springframework.data.mongodb.core.geo.Point;
@@ -34,10 +34,6 @@ import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
/**
* Base class for {@link RepositoryQuery} implementations for Mongo.
*
@@ -46,45 +42,50 @@ import com.mongodb.DBObject;
public abstract class AbstractMongoQuery implements RepositoryQuery {
private final MongoQueryMethod method;
private final MongoTemplate template;
private final MongoOperations operations;
/**
* Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoTemplate}.
* Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
*
* @param method
* @param template
* @param method must not be {@literal null}.
* @param operations must not be {@literal null}.
*/
public AbstractMongoQuery(MongoQueryMethod method, MongoTemplate template) {
public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations) {
Assert.notNull(template);
Assert.notNull(operations);
Assert.notNull(method);
this.method = method;
this.template = template;
this.operations = operations;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod()
*/
public MongoQueryMethod getQueryMethod() {
return method;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.RepositoryQuery#execute(java .lang.Object[])
*/
* (non-Javadoc)
* @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[])
*/
public Object execute(Object[] parameters) {
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(method, parameters);
Query query = createQuery(new ConvertingParameterAccessor(template.getConverter(), accessor));
Query query = createQuery(new ConvertingParameterAccessor(operations.getConverter(), accessor));
if (method.isGeoNearQuery()) {
if (method.isGeoNearQuery() && method.isPageQuery()) {
MongoParameterAccessor countAccessor = new MongoParametersParameterAccessor(method, parameters);
Query countQuery = createCountQuery(new ConvertingParameterAccessor(operations.getConverter(), countAccessor));
return new GeoNearExecution(accessor).execute(query, countQuery);
} else if (method.isGeoNearQuery()) {
return new GeoNearExecution(accessor).execute(query);
} else if (method.isCollectionQuery()) {
return new CollectionExecution().execute(query);
return new CollectionExecution(accessor.getPageable()).execute(query);
} else if (method.isPageQuery()) {
return new PagedExecution(accessor.getPageable()).execute(query);
} else {
@@ -93,14 +94,25 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
}
/**
* Create a {@link Query} instance using the given {@link ParameterAccessor}
* Creates a {@link Query} instance using the given {@link ParameterAccessor}
*
* @param accessor
* @param converter
* @param accessor must not be {@literal null}.
* @return
*/
protected abstract Query createQuery(ConvertingParameterAccessor accessor);
/**
* Creates a {@link Query} instance using the given {@link ConvertingParameterAccessor}. Will delegate to
* {@link #createQuery(ConvertingParameterAccessor)} by default but allows customization of the count query to be
* triggered.
*
* @param accessor must not be {@literal null}.
* @return
*/
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
return createQuery(accessor);
}
private abstract class Execution {
abstract Object execute(Query query);
@@ -110,7 +122,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
MongoEntityInformation<?, ?> metadata = method.getEntityInformation();
String collectionName = metadata.getCollectionName();
return template.find(query, metadata.getJavaType(), collectionName);
return operations.find(query, metadata.getJavaType(), collectionName);
}
}
@@ -121,14 +133,23 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
*/
class CollectionExecution extends Execution {
private final Pageable pageable;
CollectionExecution(Pageable pageable) {
this.pageable = pageable;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.mongodb.repository.MongoQuery.Execution #execute(com.mongodb.DBObject)
*/
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query)
*/
@Override
public Object execute(Query query) {
if (pageable != null) {
query = applyPagination(query, pageable);
}
return readCollection(query);
}
}
@@ -162,24 +183,13 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
Object execute(Query query) {
MongoEntityInformation<?, ?> metadata = method.getEntityInformation();
int count = getCollectionCursor(metadata.getCollectionName(), query.getQueryObject()).count();
long count = operations.count(query, metadata.getCollectionName());
List<?> result = template.find(applyPagination(query, pageable), metadata.getJavaType(),
List<?> result = operations.find(applyPagination(query, pageable), metadata.getJavaType(),
metadata.getCollectionName());
return new PageImpl(result, pageable, count);
}
private DBCursor getCollectionCursor(String collectionName, final DBObject query) {
return template.execute(collectionName, new CollectionCallback<DBCursor>() {
public DBCursor doInCollection(DBCollection collection) {
return collection.find(query);
}
});
}
}
/**
@@ -197,19 +207,19 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
Object execute(Query query) {
MongoEntityInformation<?, ?> entityInformation = method.getEntityInformation();
return template.findOne(query, entityInformation.getJavaType());
return operations.findOne(query, entityInformation.getJavaType());
}
}
/**
* {@link Execution} to execute geo-near queries.
*
*
* @author Oliver Gierke
*/
class GeoNearExecution extends Execution {
private final MongoParameterAccessor accessor;
public GeoNearExecution(MongoParameterAccessor accessor) {
this.accessor = accessor;
}
@@ -220,27 +230,48 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
*/
@Override
Object execute(Query query) {
GeoResults<?> results = doExecuteQuery(query);
return isListOfGeoResult() ? results.getContent() : results;
}
/**
* Executes the given {@link Query} to return a page.
*
* @param query must not be {@literal null}.
* @param countQuery must not be {@literal null}.
* @return
*/
Object execute(Query query, Query countQuery) {
MongoEntityInformation<?, ?> information = method.getEntityInformation();
long count = operations.count(countQuery, information.getCollectionName());
return new GeoPage<Object>(doExecuteQuery(query), accessor.getPageable(), count);
}
@SuppressWarnings("unchecked")
private GeoResults<Object> doExecuteQuery(Query query) {
Point nearLocation = accessor.getGeoNearLocation();
NearQuery nearQuery = NearQuery.near(nearLocation);
if (query != null) {
nearQuery.query(query);
}
Distance maxDistance = accessor.getMaxDistance();
if (maxDistance != null) {
nearQuery.maxDistance(maxDistance);
}
MongoEntityInformation<?,?> entityInformation = method.getEntityInformation();
GeoResults<?> results = template.geoNear(nearQuery, entityInformation.getJavaType(), entityInformation.getCollectionName());
return isListOfGeoResult() ? results.getContent() : results;
MongoEntityInformation<?, ?> entityInformation = method.getEntityInformation();
return (GeoResults<Object>) operations.geoNear(nearQuery, entityInformation.getJavaType(),
entityInformation.getCollectionName());
}
private boolean isListOfGeoResult() {
TypeInformation<?> returnType = method.getReturnType();
return returnType.getType().equals(List.class) && GeoResult.class.equals(returnType.getComponentType());
}

View File

@@ -20,13 +20,10 @@ import java.util.Iterator;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.convert.TypeKeyAware;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.repository.query.ParameterAccessor;
import com.mongodb.BasicDBList;
import com.mongodb.DBObject;
import org.springframework.util.Assert;
/**
* Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format.
@@ -41,9 +38,14 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
/**
* Creates a new {@link ConvertingParameterAccessor} with the given {@link MongoWriter} and delegate.
*
* @param writer
* @param writer must not be {@literal null}.
* @param delegate must not be {@literal null}.
*/
public ConvertingParameterAccessor(MongoWriter<?> writer, MongoParameterAccessor delegate) {
Assert.notNull(writer);
Assert.notNull(delegate);
this.writer = writer;
this.delegate = delegate;
}
@@ -90,7 +92,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
public Distance getMaxDistance() {
return delegate.getMaxDistance();
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
*/
@@ -105,49 +107,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
* @return
*/
private Object getConvertedValue(Object value) {
if (!(writer instanceof TypeKeyAware)) {
return value;
}
return removeTypeInfoRecursively(writer.convertToMongoType(value), ((TypeKeyAware) writer));
}
/**
* Removes the type information from the conversion result.
*
* @param object
* @return
*/
private Object removeTypeInfoRecursively(Object object, TypeKeyAware typeKeyAware) {
if (!(object instanceof DBObject) || typeKeyAware == null) {
return object;
}
DBObject dbObject = (DBObject) object;
String keyToRemove = null;
for (String key : dbObject.keySet()) {
if (typeKeyAware.isTypeKey(key)) {
keyToRemove = key;
}
Object value = dbObject.get(key);
if (value instanceof BasicDBList) {
for (Object element : (BasicDBList) value) {
removeTypeInfoRecursively(element, typeKeyAware);
}
} else {
removeTypeInfoRecursively(value, typeKeyAware);
}
}
if (keyToRemove != null) {
dbObject.removeField(keyToRemove);
}
return dbObject;
return writer.convertToMongoType(value);
}
/**

View File

@@ -17,10 +17,9 @@ package org.springframework.data.mongodb.repository.query;
import java.io.Serializable;
/**
* Interface for components being able to provide {@link EntityInformationCreator} for a given {@link Class}.
*
*
* @author Oliver Gierke
*/
public interface EntityInformationCreator {

View File

@@ -33,7 +33,7 @@ public interface MongoParameterAccessor extends ParameterAccessor {
* at all or the given value for it was {@literal null}.
*/
Distance getMaxDistance();
/**
* Returns the {@link Point} to use for a geo-near query.
*

View File

@@ -35,7 +35,7 @@ public class MongoParameters extends Parameters {
private final Integer distanceIndex;
private Integer nearIndex;
/**
* Creates a new {@link MongoParameters} instance from the given {@link Method} and {@link MongoQueryMethod}.
*
@@ -43,55 +43,56 @@ public class MongoParameters extends Parameters {
* @param queryMethod must not be {@literal null}.
*/
public MongoParameters(Method method, boolean isGeoNearMethod) {
super(method);
List<Class<?>> parameterTypes = Arrays.asList(method.getParameterTypes());
this.distanceIndex = parameterTypes.indexOf(Distance.class);
if (this.nearIndex == null && isGeoNearMethod) {
this.nearIndex = getNearIndex(parameterTypes);
} else if (this.nearIndex == null) {
this.nearIndex = -1;
}
}
@SuppressWarnings("unchecked")
private final int getNearIndex(List<Class<?>> parameterTypes) {
for (Class<?> reference : Arrays.asList(Point.class, double[].class)) {
int nearIndex = parameterTypes.indexOf(reference);
if (nearIndex == -1) {
continue;
}
if (nearIndex == parameterTypes.lastIndexOf(reference)) {
return nearIndex;
} else {
throw new IllegalStateException("Multiple Point parameters found but none annotated with @Near!");
}
}
return -1;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.Parameters#createParameter(org.springframework.core.MethodParameter)
*/
@Override
protected Parameter createParameter(MethodParameter parameter) {
MongoParameter mongoParameter = new MongoParameter(parameter);
// Detect manually annotated @Near Point and reject multiple annotated ones
if (this.nearIndex == null && mongoParameter.isManuallyAnnotatedNearParameter()) {
this.nearIndex = mongoParameter.getIndex();
} else if (mongoParameter.isManuallyAnnotatedNearParameter()) {
throw new IllegalStateException(String.format("Found multiple @Near annotations ond method %s! Only one allowed!", parameter.getMethod().toString()));
throw new IllegalStateException(String.format(
"Found multiple @Near annotations ond method %s! Only one allowed!", parameter.getMethod().toString()));
}
return mongoParameter;
}
@@ -103,7 +104,7 @@ public class MongoParameters extends Parameters {
public int getDistanceIndex() {
return distanceIndex;
}
/**
* Returns the index of the parameter to be used to start a geo-near query from.
*
@@ -112,16 +113,16 @@ public class MongoParameters extends Parameters {
public int getNearIndex() {
return nearIndex;
}
/**
* Custom {@link Parameter} implementation adding parameters of type {@link Distance} to the special ones.
*
*
* @author Oliver Gierke
*/
class MongoParameter extends Parameter {
private final MethodParameter parameter;
/**
* Creates a new {@link MongoParameter}.
*
@@ -130,7 +131,7 @@ public class MongoParameters extends Parameters {
MongoParameter(MethodParameter parameter) {
super(parameter);
this.parameter = parameter;
if (!isPoint() && hasNearAnnotation()) {
throw new IllegalArgumentException("Near annotation is only allowed at Point parameter!");
}
@@ -142,23 +143,22 @@ public class MongoParameters extends Parameters {
*/
@Override
public boolean isSpecialParameter() {
return super.isSpecialParameter() || getType().equals(Distance.class)
|| isNearParameter();
return super.isSpecialParameter() || getType().equals(Distance.class) || isNearParameter();
}
private boolean isNearParameter() {
Integer nearIndex = MongoParameters.this.nearIndex;
return nearIndex != null && nearIndex.equals(getIndex());
}
private boolean isManuallyAnnotatedNearParameter() {
return isPoint() && hasNearAnnotation();
}
private boolean isPoint() {
return getType().equals(Point.class) || getType().equals(double[].class);
}
private boolean hasNearAnnotation() {
return parameter.getParameterAnnotation(Near.class) != null;
}

View File

@@ -21,13 +21,13 @@ import org.springframework.data.repository.query.ParametersParameterAccessor;
/**
* Mongo-specific {@link ParametersParameterAccessor} to allow access to the {@link Distance} parameter.
*
*
* @author Oliver Gierke
*/
public class MongoParametersParameterAccessor extends ParametersParameterAccessor implements MongoParameterAccessor {
private final MongoQueryMethod method;
/**
* Creates a new {@link MongoParametersParameterAccessor}.
*
@@ -47,25 +47,25 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso
int index = method.getParameters().getDistanceIndex();
return index == -1 ? null : (Distance) getValue(index);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
*/
public Point getGeoNearLocation() {
int nearIndex = method.getParameters().getNearIndex();
if (nearIndex == -1) {
return null;
}
Object value = getValue(nearIndex);
if (value == null) {
return null;
}
if (value instanceof double[]) {
double[] typedValue = (double[]) value;
if (typedValue.length != 2) {
@@ -74,7 +74,7 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso
return new Point(typedValue[0], typedValue[1]);
}
}
return (Point) value;
}
}

View File

@@ -31,7 +31,6 @@ import org.springframework.data.mongodb.core.geo.Shape;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.OrQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator;
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
@@ -45,7 +44,7 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
*/
class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class);
private final MongoParameterAccessor accessor;
@@ -92,7 +91,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#create(org.springframework.data.repository.query.parser.Part, java.util.Iterator)
*/
@Override
protected Query create(Part part, Iterator<Object> iterator) {
protected Criteria create(Part part, Iterator<Object> iterator) {
if (isGeoNearQuery && part.getType().equals(Type.NEAR)) {
return null;
@@ -103,7 +102,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
(PotentiallyConvertingIterator) iterator);
return new Query(criteria);
return criteria;
}
/*
@@ -111,18 +110,18 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator)
*/
@Override
protected Query and(Part part, Query base, Iterator<Object> iterator) {
protected Criteria and(Part part, Criteria base, Iterator<Object> iterator) {
if (base == null) {
return create(part, iterator);
}
PersistentPropertyPath<MongoPersistentProperty> path2 = context.getPersistentPropertyPath(part.getProperty());
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
Criteria criteria = from(part.getType(),
where(path2.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
(PotentiallyConvertingIterator) iterator);
return base.addCriteria(criteria);
return new Criteria().andOperator(
base,
from(part.getType(), where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
(PotentiallyConvertingIterator) iterator));
}
/*
@@ -133,8 +132,10 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
* #or(java.lang.Object, java.lang.Object)
*/
@Override
protected Query or(Query base, Query query) {
return new OrQuery(new Query[] {base, query});
protected Criteria or(Criteria base, Criteria criteria) {
Criteria result = new Criteria();
return result.orOperator(base, criteria);
}
/*
@@ -145,16 +146,17 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
* #complete(java.lang.Object, org.springframework.data.domain.Sort)
*/
@Override
protected Query complete(Query query, Sort sort) {
protected Query complete(Criteria criteria, Sort sort) {
if (query == null) {
if (criteria == null) {
return null;
}
Query query = new Query(criteria);
QueryUtils.applySorting(query, sort);
if (LOG.isDebugEnabled()) {
LOG.debug("Created query " + query.getQueryObject());
LOG.debug("Created query " + query);
}
return query;
@@ -192,10 +194,19 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
case LIKE:
String value = parameters.next().toString();
return criteria.regex(toLikeRegex(value));
case REGEX:
return criteria.regex(parameters.next().toString());
case EXISTS:
return criteria.exists((Boolean) parameters.next());
case TRUE:
return criteria.is(true);
case FALSE:
return criteria.is(false);
case NEAR:
Distance distance = accessor.getMaxDistance();
Point point = nextAs(parameters, Point.class);
Point point = accessor.getGeoNearLocation();
point = point == null ? nextAs(parameters, Point.class) : point;
if (distance == null) {
return criteria.near(point);
@@ -257,4 +268,4 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Query> {
return source.replaceAll("\\*", ".*");
}
}
}

View File

@@ -27,7 +27,6 @@ import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.util.ClassUtils;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
@@ -57,7 +56,7 @@ public class MongoQueryMethod extends QueryMethod {
super(method, metadata);
Assert.notNull(entityInformationCreator, "DefaultEntityInformationCreator must not be null!");
this.method = method;
this.entityInformation = entityInformationCreator.getEntityInformation(ClassUtils.getReturnedDomainClass(method),
this.entityInformation = entityInformationCreator.getEntityInformation(metadata.getReturnedDomainClass(method),
getDomainClass());
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.repository.query;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.Query;
@@ -37,18 +38,20 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
/**
* Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}.
*
* @param method
* @param template
* @param method must not be {@literal null}.
* @param template must not be {@literal null}.
*/
public PartTreeMongoQuery(MongoQueryMethod method, MongoTemplate template) {
public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations) {
super(method, template);
super(method, mongoOperations);
this.tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType());
this.isGeoNearQuery = method.isGeoNearQuery();
this.context = template.getConverter().getMappingContext();
this.context = mongoOperations.getConverter().getMappingContext();
}
/**
* Return the {@link PartTree} backing the query.
*
* @return the tree
*/
public PartTree getTree() {
@@ -56,16 +59,22 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.mongodb.repository.AbstractMongoQuery#createQuery(org.springframework.data.
* document.mongodb.repository.ConvertingParameterAccessor)
*/
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#createQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor, boolean)
*/
@Override
protected Query createQuery(ConvertingParameterAccessor accessor) {
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery);
return creator.createQuery();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#createCountQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
*/
@Override
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
return new MongoQueryCreator(tree, accessor, context, false).createQuery();
}
}

View File

@@ -20,11 +20,12 @@ import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.util.JSON;
/**
* Query to use a plain JSON String to create the {@link Query} to actually execute.
*
@@ -44,23 +45,20 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
* @param method
* @param template
*/
public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoTemplate template) {
super(method, template);
public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations) {
super(method, mongoOperations);
this.query = query;
this.fieldSpec = method.getFieldSpecification();
}
public StringBasedMongoQuery(MongoQueryMethod method, MongoTemplate template) {
this(method.getAnnotatedQuery(), method, template);
public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations) {
this(method.getAnnotatedQuery(), method, mongoOperations);
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.mongodb.repository.AbstractMongoQuery#createQuery(org.springframework.data.
* repository.query.SimpleParameterAccessor, org.springframework.data.mongodb.core.core.support.convert.MongoConverter)
*/
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#createQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
*/
@Override
protected Query createQuery(ConvertingParameterAccessor accessor) {
@@ -75,6 +73,8 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
query = new BasicQuery(queryString);
}
QueryUtils.applySorting(query, accessor.getSort());
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Created query %s", query.getQueryObject()));
}
@@ -97,13 +97,6 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
}
private String getParameterWithIndex(ConvertingParameterAccessor accessor, int index) {
Object parameter = accessor.getBindableValue(index);
if (parameter instanceof String || parameter.getClass().isEnum()) {
return String.format("\"%s\"", parameter);
} else if (parameter instanceof ObjectId) {
return String.format("{ '$oid' : '%s' }", parameter);
}
return parameter.toString();
return JSON.serialize(accessor.getBindableValue(index));
}
}

View File

@@ -25,7 +25,8 @@ import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.util.Assert;
/**
* Simple {@link EntityInformationCreator} to to create {@link MongoEntityInformation} instances based on a {@link MappingContext}.
* Simple {@link EntityInformationCreator} to to create {@link MongoEntityInformation} instances based on a
* {@link MappingContext}.
*
* @author Oliver Gierke
*/

View File

@@ -44,7 +44,7 @@ class IndexEnsuringQueryCreationListener implements QueryCreationListener<PartTr
private static final Set<Type> GEOSPATIAL_TYPES = new HashSet<Type>(Arrays.asList(Type.NEAR, Type.WITHIN));
private static final Log LOG = LogFactory.getLog(IndexEnsuringQueryCreationListener.class);
private final MongoOperations operations;
/**
@@ -86,7 +86,7 @@ class IndexEnsuringQueryCreationListener implements QueryCreationListener<PartTr
}
MongoEntityInformation<?, ?> metadata = query.getQueryMethod().getEntityInformation();
operations.ensureIndex(index, metadata.getCollectionName());
operations.indexOps(metadata.getCollectionName()).ensureIndex(index);
LOG.debug(String.format("Created %s!", index));
}

View File

@@ -16,14 +16,11 @@
package org.springframework.data.mongodb.repository.support;
import java.util.Collections;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import org.springframework.data.mongodb.core.mapping.Document;
@@ -33,8 +30,9 @@ import com.mysema.query.annotations.QueryEmbedded;
import com.mysema.query.annotations.QueryEntities;
import com.mysema.query.annotations.QuerySupertype;
import com.mysema.query.annotations.QueryTransient;
import com.mysema.query.apt.AbstractQuerydslProcessor;
import com.mysema.query.apt.Configuration;
import com.mysema.query.apt.DefaultConfiguration;
import com.mysema.query.apt.Processor;
/**
* Annotation processor to create Querydsl query types for QueryDsl annoated classes.
@@ -44,10 +42,14 @@ import com.mysema.query.apt.Processor;
@SuppressWarnings("restriction")
@SupportedAnnotationTypes({ "com.mysema.query.annotations.*", "org.springframework.data.mongodb.core.mapping.*" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MongoAnnotationProcessor extends AbstractProcessor {
public class MongoAnnotationProcessor extends AbstractQuerydslProcessor {
/*
* (non-Javadoc)
* @see com.mysema.query.apt.AbstractQuerydslProcessor#createConfiguration(javax.annotation.processing.RoundEnvironment)
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
protected Configuration createConfiguration(RoundEnvironment roundEnv) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Running " + getClass().getSimpleName());
@@ -56,8 +58,6 @@ public class MongoAnnotationProcessor extends AbstractProcessor {
QueryEmbeddable.class, QueryEmbedded.class, QueryTransient.class);
configuration.setUnknownAsEmbedded(true);
Processor processor = new Processor(processingEnv, roundEnv, configuration);
processor.process();
return true;
return configuration;
}
}

View File

@@ -21,8 +21,8 @@ import java.io.Serializable;
import java.lang.reflect.Method;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.query.EntityInformationCreator;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
@@ -37,7 +37,6 @@ import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Factory to create {@link MongoRepository} instances.
@@ -46,7 +45,7 @@ import org.springframework.util.StringUtils;
*/
public class MongoRepositoryFactory extends RepositoryFactorySupport {
private final MongoTemplate template;
private final MongoOperations mongoOperations;
private final EntityInformationCreator entityInformationCreator;
/**
@@ -55,11 +54,12 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
* @param template must not be {@literal null}
* @param mappingContext
*/
public MongoRepositoryFactory(MongoTemplate template) {
public MongoRepositoryFactory(MongoOperations mongoOperations) {
Assert.notNull(template);
this.template = template;
this.entityInformationCreator = new DefaultEntityInformationCreator(template.getConverter().getMappingContext());
Assert.notNull(mongoOperations);
this.mongoOperations = mongoOperations;
this.entityInformationCreator = new DefaultEntityInformationCreator(mongoOperations.getConverter()
.getMappingContext());
}
/*
@@ -84,9 +84,9 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
MongoEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainClass());
if (isQueryDslRepository(repositoryInterface)) {
return new QueryDslMongoRepository(entityInformation, template);
return new QueryDslMongoRepository(entityInformation, mongoOperations);
} else {
return new SimpleMongoRepository(entityInformation, template);
return new SimpleMongoRepository(entityInformation, mongoOperations);
}
}
@@ -110,41 +110,27 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
* @author Oliver Gierke
*/
private class MongoQueryLookupStrategy implements QueryLookupStrategy {
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.repository.core.NamedQueries)
*/
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, entityInformationCreator);
String namedQueryName = queryMethod.getNamedQueryName();
if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName);
return new StringBasedMongoQuery(namedQuery, queryMethod, template);
return new StringBasedMongoQuery(namedQuery, queryMethod, mongoOperations);
} else if (queryMethod.hasAnnotatedQuery()) {
return new StringBasedMongoQuery(queryMethod, template);
return new StringBasedMongoQuery(queryMethod, mongoOperations);
} else {
return new PartTreeMongoQuery(queryMethod, template);
return new PartTreeMongoQuery(queryMethod, mongoOperations);
}
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.support.RepositoryFactorySupport#validate(org.springframework.data.repository.support.RepositoryMetadata)
*/
@Override
protected void validate(RepositoryMetadata metadata) {
Class<?> idClass = metadata.getIdClass();
if (!MongoSimpleTypes.SUPPORTED_ID_CLASSES.contains(idClass)) {
throw new IllegalArgumentException(String.format("Unsupported id class! Only %s are supported!",
StringUtils.collectionToCommaDelimitedString(MongoSimpleTypes.SUPPORTED_ID_CLASSES)));
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)

View File

@@ -17,7 +17,7 @@ package org.springframework.data.mongodb.repository.support;
import java.io.Serializable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
@@ -32,17 +32,17 @@ import org.springframework.util.Assert;
public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends
RepositoryFactoryBeanSupport<T, S, ID> {
private MongoTemplate template;
private MongoOperations operations;
private boolean createIndexesForQueryMethods = false;
/**
* Configures the {@link MongoTemplate} to be used.
* Configures the {@link MongoOperations} to be used.
*
* @param template the template to set
* @param operations the operations to set
*/
public void setTemplate(MongoTemplate template) {
public void setMongoOperations(MongoOperations operations) {
this.template = template;
this.operations = operations;
}
/**
@@ -64,10 +64,10 @@ public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID exten
@Override
protected final RepositoryFactorySupport createRepositoryFactory() {
RepositoryFactorySupport factory = getFactoryInstance(template);
RepositoryFactorySupport factory = getFactoryInstance(operations);
if (createIndexesForQueryMethods) {
factory.addQueryCreationListener(new IndexEnsuringQueryCreationListener(template));
factory.addQueryCreationListener(new IndexEnsuringQueryCreationListener(operations));
}
return factory;
@@ -76,11 +76,11 @@ public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID exten
/**
* Creates and initializes a {@link RepositoryFactorySupport} instance.
*
* @param template
* @param operations
* @return
*/
protected RepositoryFactorySupport getFactoryInstance(MongoTemplate template) {
return new MongoRepositoryFactory(template);
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new MongoRepositoryFactory(operations);
}
/*
@@ -94,6 +94,6 @@ public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID exten
public void afterPropertiesSet() {
super.afterPropertiesSet();
Assert.notNull(template, "MongoTemplate must not be null!");
Assert.notNull(operations, "MongoTemplate must not be null!");
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.support;
import java.io.Serializable;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.collections15.Transformer;
import org.springframework.data.domain.Page;
@@ -25,7 +26,10 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.QueryMapper;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
@@ -34,6 +38,7 @@ import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.repository.core.EntityMetadata;
import org.springframework.util.Assert;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mysema.query.mongodb.MongodbQuery;
@@ -64,9 +69,9 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
* @param entityInformation
* @param template
*/
public QueryDslMongoRepository(MongoEntityInformation<T, ID> entityInformation, MongoTemplate template) {
public QueryDslMongoRepository(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations) {
this(entityInformation, template, SimpleEntityPathResolver.INSTANCE);
this(entityInformation, mongoOperations, SimpleEntityPathResolver.INSTANCE);
}
/**
@@ -74,17 +79,17 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
* and {@link EntityPathResolver}.
*
* @param entityInformation
* @param template
* @param mongoOperations
* @param resolver
*/
public QueryDslMongoRepository(MongoEntityInformation<T, ID> entityInformation, MongoTemplate template,
public QueryDslMongoRepository(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations,
EntityPathResolver resolver) {
super(entityInformation, template);
super(entityInformation, mongoOperations);
Assert.notNull(resolver);
EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.serializer = new SpringDataMongodbSerializer(template.getConverter().getMappingContext());
this.serializer = new SpringDataMongodbSerializer(mongoOperations.getConverter());
}
/*
@@ -229,14 +234,19 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
*/
static class SpringDataMongodbSerializer extends MongodbSerializer {
private final MongoConverter converter;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final QueryMapper mapper;
/**
* Creates a new {@link SpringDataMongodbSerializer} for the given {@link MappingContext}.
*
* @param mappingContext
*/
public SpringDataMongodbSerializer(MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
this.mappingContext = mappingContext;
public SpringDataMongodbSerializer(MongoConverter converter) {
this.mappingContext = converter.getMappingContext();
this.converter = converter;
this.mapper = new QueryMapper(converter);
}
@Override
@@ -245,7 +255,17 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
Path<?> parent = metadata.getParent();
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(parent.getType());
MongoPersistentProperty property = entity.getPersistentProperty(metadata.getExpression().toString());
return property.getFieldName();
return property == null ? super.getKeyForPath(expr, metadata) : property.getFieldName();
}
@Override
protected DBObject asDBObject(String key, Object value) {
if ("_id".equals(key)) {
return super.asDBObject(key, mapper.convertId(value));
}
return super.asDBObject(key, value instanceof Pattern ? value : converter.convertToMongoType(value));
}
}
}

View File

@@ -42,7 +42,7 @@ import org.springframework.util.Assert;
*/
public class SimpleMongoRepository<T, ID extends Serializable> implements PagingAndSortingRepository<T, ID> {
private final MongoTemplate template;
private final MongoOperations mongoOperations;
private final MongoEntityInformation<T, ID> entityInformation;
/**
@@ -51,34 +51,34 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
* @param metadata
* @param template
*/
public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoTemplate template) {
public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
Assert.notNull(template);
Assert.notNull(mongoOperations);
Assert.notNull(metadata);
this.entityInformation = metadata;
this.template = template;
this.mongoOperations = mongoOperations;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#save(java.lang.Object)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
*/
public T save(T entity) {
template.save(entity, entityInformation.getCollectionName());
Assert.notNull(entity, "Entity must not be null!");
mongoOperations.save(entity, entityInformation.getCollectionName());
return entity;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#save(java.lang.Iterable)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
*/
public List<T> save(Iterable<? extends T> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
List<T> result = new ArrayList<T>();
for (T entity : entities) {
@@ -91,14 +91,11 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#findById(java.io.Serializable
* )
* @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable)
*/
public T findOne(ID id) {
Assert.notNull(id, "The given id must not be null!");
return template.findById(id, entityInformation.getJavaType());
return mongoOperations.findById(id, entityInformation.getJavaType());
}
private Query getIdQuery(Object id) {
@@ -111,42 +108,36 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#exists(java.io.Serializable
* )
* @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable)
*/
public boolean exists(ID id) {
Assert.notNull(id, "The given id must not be null!");
return template.findOne(new Query(Criteria.where("_id").is(id)), Object.class,
return mongoOperations.findOne(new Query(Criteria.where("_id").is(id)), Object.class,
entityInformation.getCollectionName()) != null;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.Repository#count()
* @see org.springframework.data.repository.CrudRepository#count()
*/
public long count() {
return template.getCollection(entityInformation.getCollectionName()).count();
return mongoOperations.getCollection(entityInformation.getCollectionName()).count();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.Repository#delete(java.io.Serializable)
* @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
*/
public void delete(ID id) {
Assert.notNull(id, "The given id must not be null!");
template.remove(getIdQuery(id), entityInformation.getJavaType());
mongoOperations.remove(getIdQuery(id), entityInformation.getJavaType());
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#delete(java.lang.Object)
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
*/
public void delete(T entity) {
Assert.notNull(entity, "The given entity must not be null!");
@@ -155,12 +146,12 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#delete(java.lang.Iterable)
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable)
*/
public void delete(Iterable<? extends T> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
for (T entity : entities) {
delete(entity);
}
@@ -168,18 +159,16 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.Repository#deleteAll()
* @see org.springframework.data.repository.CrudRepository#deleteAll()
*/
public void deleteAll() {
template.remove(new Query(), entityInformation.getCollectionName());
mongoOperations.remove(new Query(), entityInformation.getCollectionName());
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.Repository#findAll()
* @see org.springframework.data.repository.CrudRepository#findAll()
*/
public List<T> findAll() {
@@ -188,10 +177,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.PagingAndSortingRepository#findAll
* (org.springframework.data.domain.Pageable)
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable)
*/
public Page<T> findAll(final Pageable pageable) {
@@ -203,50 +189,20 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.PagingAndSortingRepository#findAll
* (org.springframework.data.domain.Sort)
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
*/
public List<T> findAll(final Sort sort) {
return findAll(QueryUtils.applySorting(new Query(), sort));
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.Repository#findAll(java.lang.Iterable
* )
*/
public List<T> findAll(Iterable<ID> ids) {
Query query = null;
//TODO: verify intent
// for (ID id : ids) {
// if (query == null) {
// query = getIdQuery(id);
// } else {
// query = new Query().or(getIdQuery(id));
// }
// }
List<ID> idList = new ArrayList<ID>();
for (ID id : ids) {
idList.add(id);
}
query = new Query(Criteria.where(entityInformation.getIdAttribute()).in(idList));
return findAll(query);
}
private List<T> findAll(Query query) {
if (query == null) {
return Collections.emptyList();
}
return template.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
return mongoOperations.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
}
/**
@@ -256,7 +212,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
*/
protected MongoOperations getMongoOperations() {
return this.template;
return this.mongoOperations;
}
/**

View File

@@ -7,17 +7,14 @@
xmlns:context="http://www.springframework.org/schema/context"
xmlns:repository="http://www.springframework.org/schema/data/repository"
targetNamespace="http://www.springframework.org/schema/data/mongo"
elementFormDefault="qualified" attributeFormDefault="unqualified"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:import namespace="http://www.springframework.org/schema/context"
schemaLocation="http://www.springframework.org/schema/context/spring-context.xsd"/>
schemaLocation="http://www.springframework.org/schema/context/spring-context.xsd" />
<xsd:import namespace="http://www.springframework.org/schema/data/repository"
schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository.xsd"/>
schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository-1.0.xsd" />
<xsd:element name="mongo" type="mongoType">
<xsd:annotation>
@@ -47,8 +44,9 @@ The name of the mongo definition (by default "mongoDbFactory").]]></xsd:document
</xsd:attribute>
<xsd:attribute name="mongo-ref" type="mongoRef" use="optional">
<xsd:annotation>
<xsd:documentation>
The reference to a Mongo. Will default to 'mongo'.
<xsd:documentation><![CDATA[
The reference to a Mongo instance. If not configured a default com.mongodb.Mongo instance will be created.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -93,7 +91,16 @@ The password to use when connecting to a MongoDB server.
The Mongo URI string.]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="writeConcern" />
<xsd:attribute name="write-concern">
<xsd:annotation>
<xsd:documentation>
The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="writeConcernEnumeration xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
@@ -140,9 +147,10 @@ The Mongo URI string.]]></xsd:documentation>
<xsd:element name="mapping-converter">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a MongoConverter for getting rich mapping functionality.
]]></xsd:documentation>
<xsd:documentation><![CDATA[Defines a MongoConverter for getting rich mapping functionality.]]></xsd:documentation>
<xsd:appinfo>
<tool:exports type="org.springframework.data.mongodb.core.convert.MappingMongoConverter" />
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
@@ -150,14 +158,14 @@ Defines a MongoConverter for getting rich mapping functionality.
<xsd:annotation>
<xsd:documentation><![CDATA[
Top-level element that contains one or more custom converters to be used for mapping
domain objects to and from Mongo's DBObject
]]>
domain objects to and from Mongo's DBObject]]>
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="converter" type="customConverterType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="base-package" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
@@ -259,6 +267,18 @@ The name of the Mongo object that determines what server to monitor. (by default
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:simpleType name="writeConcernEnumeration">
<xsd:restriction base="xsd:token">
<xsd:enumeration value="NONE" />
<xsd:enumeration value="NORMAL" />
<xsd:enumeration value="SAFE" />
<xsd:enumeration value="FSYNC_SAFE" />
<xsd:enumeration value="REPLICAS_SAFE" />
<xsd:enumeration value="JOURNAL_SAFE" />
<xsd:enumeration value="MAJORITY" />
</xsd:restriction>
</xsd:simpleType>
<!-- MLP
<xsd:attributeGroup name="writeConcern">
<xsd:attribute name="write-concern">
<xsd:simpleType>
@@ -268,11 +288,13 @@ The name of the Mongo object that determines what server to monitor. (by default
<xsd:enumeration value="SAFE" />
<xsd:enumeration value="FSYNC_SAFE" />
<xsd:enumeration value="REPLICA_SAFE" />
<xsd:enumeration value="JOURNAL_SAFE" />
<xsd:enumeration value="MAJORITY" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:attributeGroup>
-->
<xsd:complexType name="mongoType">
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element name="options" type="optionsType">
@@ -288,7 +310,19 @@ The Mongo driver options
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="write-concern">
<xsd:annotation>
<xsd:documentation>
The WriteConcern that will be the default value used when asking the MongoDbFactory for a DB object
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="writeConcernEnumeration xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
<!-- MLP
<xsd:attributeGroup ref="writeConcern" />
-->
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -15,10 +15,26 @@
*/
package org.springframework.data.mongodb.config;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Collections;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.mapping.Account;
import org.springframework.data.mongodb.repository.Person;
import org.springframework.stereotype.Component;
import com.mongodb.DBObject;
/**
* Integration tests for {@link MongoParser}.
@@ -27,12 +43,46 @@ import org.springframework.core.io.ClassPathResource;
*/
public class MappingMongoConverterParserIntegrationTests {
DefaultListableBeanFactory factory;
@Before
public void setUp() {
factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new ClassPathResource("namespace/converter.xml"));
}
@Test
public void allowsDbFactoryRefAttribute() {
ConfigurableListableBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/converter.xml"));
factory.getBeanDefinition("converter");
factory.getBean("converter");
}
@Test
public void scansForConverterAndSetsUpCustomConversionsAccordingly() {
CustomConversions conversions = factory.getBean(CustomConversions.class);
assertThat(conversions.hasCustomWriteTarget(Person.class), is(true));
assertThat(conversions.hasCustomWriteTarget(Account.class), is(true));
}
@Component
public static class SampleConverter implements Converter<Person, DBObject> {
public DBObject convert(Person source) {
return null;
}
}
@Component
public static class SampleConverterFactory implements GenericConverter {
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Account.class, DBObject.class));
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return null;
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* 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.
*/
package org.springframework.data.mongodb.config;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Integration tests for {@link MongoDbFactory}.
*
* @author Thomas Risbergf
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MongoDbFactoryNoDatabaseRunningTests {
@Autowired
MongoTemplate mongoTemplate;
/**
* @see DATADOC-139
*/
@Test
public void startsUpWithoutADatabaseRunning() {
assertThat(mongoTemplate.getClass().getName(), is("org.springframework.data.mongodb.core.MongoTemplate"));
}
@Test(expected = DataAccessResourceFailureException.class)
public void failsDataAccessWithoutADatabaseRunning() {
mongoTemplate.getCollectionNames();
}
}

View File

@@ -18,73 +18,159 @@ package org.springframework.data.mongodb.config;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoURI;
import com.mongodb.WriteConcern;
/**
* Integration tests for {@link MongoDbFactoryParser}.
*
*
* @author Oliver Gierke
*/
public class MongoDbFactoryParserIntegrationTests {
DefaultListableBeanFactory factory;
BeanDefinitionReader reader;
@Before
public void setUp() {
factory = new DefaultListableBeanFactory();
reader = new XmlBeanDefinitionReader(factory);
}
@Test
public void testWriteConcern() throws Exception {
SimpleMongoDbFactory dbFactory = new SimpleMongoDbFactory(new Mongo("localhost"), "database");
dbFactory.setWriteConcern(WriteConcern.SAFE);
dbFactory.getDb();
assertThat(ReflectionTestUtils.getField(dbFactory, "writeConcern"), is((Object) WriteConcern.SAFE));
}
@Test
public void parsesWriteConcern() {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/db-factory-bean.xml"));
BeanDefinition definition = factory.getBeanDefinition("first");
List<PropertyValue> values = definition.getPropertyValues().getPropertyValueList();
assertThat(values, hasItem(new PropertyValue("writeConcern", "SAFE")));
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("namespace/db-factory-bean.xml");
assertWriteConcern(ctx, WriteConcern.SAFE);
}
@Test
public void parsesCustomWriteConcern() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
"namespace/db-factory-bean-custom-write-concern.xml");
assertWriteConcern(ctx, new WriteConcern("rack1"));
}
/**
* @see DATAMONGO-331
*/
@Test
public void readsReplicasWriteConcernCorrectly() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("namespace/db-factory-bean-custom-write-concern.xml");
MongoDbFactory factory = ctx.getBean("second", MongoDbFactory.class);
DB db = factory.getDb();
assertThat(db.getWriteConcern(), is(WriteConcern.REPLICAS_SAFE));
}
private void assertWriteConcern(ClassPathXmlApplicationContext ctx, WriteConcern expectedWriteConcern) {
SimpleMongoDbFactory dbFactory = ctx.getBean("first", SimpleMongoDbFactory.class);
DB db = dbFactory.getDb();
assertThat(db.getName(), is("db"));
WriteConcern configuredConcern = (WriteConcern) ReflectionTestUtils.getField(dbFactory, "writeConcern");
MyWriteConcern myDbFactoryWriteConcern = new MyWriteConcern(configuredConcern);
MyWriteConcern myDbWriteConcern = new MyWriteConcern(db.getWriteConcern());
MyWriteConcern myExpectedWriteConcern = new MyWriteConcern(expectedWriteConcern);
assertThat(myDbFactoryWriteConcern, is(myExpectedWriteConcern));
assertThat(myDbWriteConcern, is(myExpectedWriteConcern));
assertThat(myDbWriteConcern, is(myDbFactoryWriteConcern));
}
// This test will fail since equals in WriteConcern uses == for _w and not .equals
public void testWriteConcernEquality() {
String s1 = new String("rack1");
String s2 = new String("rack1");
WriteConcern wc1 = new WriteConcern(s1);
WriteConcern wc2 = new WriteConcern(s2);
assertThat(wc1, is(wc2));
}
@Test
public void createsDbFactoryBean() {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/db-factory-bean.xml"));
reader.loadBeanDefinitions(new ClassPathResource("namespace/db-factory-bean.xml"));
factory.getBean("first");
}
/**
* @see DATADOC-280
*/
@Test
public void parsesMaxAutoConnectRetryTimeCorrectly() {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/db-factory-bean.xml"));
reader.loadBeanDefinitions(new ClassPathResource("namespace/db-factory-bean.xml"));
Mongo mongo = factory.getBean(Mongo.class);
assertThat(mongo.getMongoOptions().maxAutoConnectRetryTime, is(27L));
}
/**
* @see DATADOC-295
*/
@Test
public void setsUpMongoDbFactoryUsingAMongoUri() {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/mongo-uri.xml"));
reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-uri.xml"));
BeanDefinition definition = factory.getBeanDefinition("mongoDbFactory");
ConstructorArgumentValues constructorArguments = definition.getConstructorArgumentValues();
assertThat(constructorArguments.getArgumentCount(), is(1));
ValueHolder argument = constructorArguments.getArgumentValue(0, MongoURI.class);
assertThat(argument, is(notNullValue()));
}
/**
* @see DATADOC-306
*/
@Test
public void setsUpMongoDbFactoryUsingAMongoUriWithoutCredentials() {
reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-uri-no-credentials.xml"));
BeanDefinition definition = factory.getBeanDefinition("mongoDbFactory");
ConstructorArgumentValues constructorArguments = definition.getConstructorArgumentValues();
assertThat(constructorArguments.getArgumentCount(), is(1));
ValueHolder argument = constructorArguments.getArgumentValue(0, MongoURI.class);
assertThat(argument, is(notNullValue()));
MongoDbFactory dbFactory = factory.getBean("mongoDbFactory", MongoDbFactory.class);
DB db = dbFactory.getDb();
assertThat(db.getName(), is("database"));
}
/**
* @see DATADOC-295
*/
@Test(expected = BeanDefinitionParsingException.class)
public void rejectsUriPlusDetailedConfiguration() {
new XmlBeanFactory(new ClassPathResource("namespace/mongo-uri-and-details.xml"));
reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-uri-and-details.xml"));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 the original author or authors.
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.config;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
@@ -29,6 +30,7 @@ import org.springframework.data.mongodb.core.MongoFactoryBean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.CommandResult;
import com.mongodb.Mongo;
@@ -36,30 +38,45 @@ import com.mongodb.ServerAddress;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MongoNamespaceReplicaSetTests extends NamespaceTestSupport {
public class MongoNamespaceReplicaSetTests {
@Autowired
private ApplicationContext ctx;
@Test
@SuppressWarnings("unchecked")
public void testParsingMongoWithReplicaSets() throws Exception {
assertTrue(ctx.containsBean("replicaSetMongo"));
MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&replicaSetMongo");
List<ServerAddress> replicaSetSeeds = readField("replicaSetSeeds", mfb);
assertNotNull(replicaSetSeeds);
assertEquals("127.0.0.1", replicaSetSeeds.get(0).getHost());
assertEquals(10001, replicaSetSeeds.get(0).getPort());
assertEquals("localhost", replicaSetSeeds.get(1).getHost());
assertEquals(10002, replicaSetSeeds.get(1).getPort());
List<ServerAddress> replicaSetSeeds = (List<ServerAddress>) ReflectionTestUtils.getField(mfb, "replicaSetSeeds");
assertThat(replicaSetSeeds, is(notNullValue()));
assertThat(replicaSetSeeds, hasItems(new ServerAddress("127.0.0.1", 10001), new ServerAddress("localhost", 10002)));
}
@Test
@SuppressWarnings("unchecked")
public void testParsingWithPropertyPlaceHolder() throws Exception {
assertTrue(ctx.containsBean("manyReplicaSetMongo"));
MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&manyReplicaSetMongo");
List<ServerAddress> replicaSetSeeds = (List<ServerAddress>) ReflectionTestUtils.getField(mfb, "replicaSetSeeds");
assertThat(replicaSetSeeds, is(notNullValue()));
assertThat(replicaSetSeeds, hasSize(3));
assertThat(
replicaSetSeeds,
hasItems(new ServerAddress("192.168.174.130", 27017), new ServerAddress("192.168.174.130", 27018),
new ServerAddress("192.168.174.130", 27019)));
}
@Test
@Ignore("CI infrastructure does not yet support replica sets")
public void testMongoWithReplicaSets() {
Mongo mongo = ctx.getBean(Mongo.class);
assertEquals(2, mongo.getAllAddress().size());
List<ServerAddress> servers = mongo.getAllAddress();
@@ -67,10 +84,9 @@ public class MongoNamespaceReplicaSetTests extends NamespaceTestSupport {
assertEquals("localhost", servers.get(1).getHost());
assertEquals(10001, servers.get(0).getPort());
assertEquals(10002, servers.get(1).getPort());
MongoTemplate template = new MongoTemplate(mongo, "admin");
CommandResult result = template.executeCommand("{replSetGetStatus : 1}");
assertEquals("blort", result.getString("set"));
}
}

View File

@@ -16,17 +16,19 @@
package org.springframework.data.mongodb.config;
import static org.springframework.test.util.ReflectionTestUtils.*;
import static org.junit.Assert.*;
import static org.springframework.test.util.ReflectionTestUtils.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoFactoryBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.Mongo;
import com.mongodb.MongoOptions;
@@ -62,12 +64,12 @@ public class MongoNamespaceTests {
Mongo mongo = (Mongo) getField(dbf, "mongo");
assertEquals("localhost", mongo.getAddress().getHost());
assertEquals(27017, mongo.getAddress().getPort());
assertEquals("joe", getField(dbf, "username"));
assertEquals("secret", getField(dbf, "password"));
assertEquals(new UserCredentials("joe", "secret"), getField(dbf, "credentials"));
assertEquals("database", getField(dbf, "databaseName"));
}
@Test
@SuppressWarnings("deprecation")
public void testMongoSingletonWithPropertyPlaceHolders() throws Exception {
assertTrue(ctx.containsBean("mongo"));
MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&mongo");

View File

@@ -15,18 +15,23 @@
*/
package org.springframework.data.mongodb.config;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
import com.mongodb.Mongo;
/**
* Integration tests for {@link MongoParser}.
*
@@ -34,10 +39,19 @@ import org.springframework.core.io.ClassPathResource;
*/
public class MongoParserIntegrationTests {
DefaultListableBeanFactory factory;
BeanDefinitionReader reader;
@Before
public void setUp() {
factory = new DefaultListableBeanFactory();
reader = new XmlBeanDefinitionReader(factory);
}
@Test
public void readsMongoAttributesCorrectly() {
ConfigurableListableBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/mongo-bean.xml"));
reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-bean.xml"));
BeanDefinition definition = factory.getBeanDefinition("mongo");
List<PropertyValue> values = definition.getPropertyValues().getPropertyValueList();
@@ -45,4 +59,18 @@ public class MongoParserIntegrationTests {
factory.getBean("mongo");
}
/**
* @see DATAMONGO-343
*/
@Test
public void readsServerAddressesCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-bean.xml"));
GenericApplicationContext context = new GenericApplicationContext(factory);
context.refresh();
assertThat(context.getBean("mongo2", Mongo.class), is(notNullValue()));
}
}

View File

@@ -0,0 +1,58 @@
package org.springframework.data.mongodb.config;
import com.mongodb.WriteConcern;
public class MyWriteConcern {
public MyWriteConcern(WriteConcern wc) {
this._w = wc.getWObject();
this._continueOnErrorForInsert = wc.getContinueOnErrorForInsert();
this._fsync = wc.getFsync();
this._j = wc.getJ();
this._wtimeout = wc.getWtimeout();
}
Object _w = 0;
int _wtimeout = 0;
boolean _fsync = false;
boolean _j = false;
boolean _continueOnErrorForInsert = false;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (_continueOnErrorForInsert ? 1231 : 1237);
result = prime * result + (_fsync ? 1231 : 1237);
result = prime * result + (_j ? 1231 : 1237);
result = prime * result + ((_w == null) ? 0 : _w.hashCode());
result = prime * result + _wtimeout;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyWriteConcern other = (MyWriteConcern) obj;
if (_continueOnErrorForInsert != other._continueOnErrorForInsert)
return false;
if (_fsync != other._fsync)
return false;
if (_j != other._j)
return false;
if (_w == null) {
if (other._w != null)
return false;
} else if (!_w.equals(other._w))
return false;
if (_wtimeout != other._wtimeout)
return false;
return true;
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2011 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.config;
import java.lang.reflect.Field;
public class NamespaceTestSupport {
@SuppressWarnings({ "unchecked" })
public static <T> T readField(String name, Object target) throws Exception {
Field field = null;
Class<?> clazz = target.getClass();
do {
try {
field = clazz.getDeclaredField(name);
} catch (Exception ex) {
}
clazz = clazz.getSuperclass();
} while (field == null && !clazz.equals(Object.class));
if (field == null)
throw new IllegalArgumentException("Cannot find field '" + name + "' in the class hierarchy of "
+ target.getClass());
field.setAccessible(true);
return (T) field.get(target);
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.config;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import com.mongodb.ServerAddress;
/**
* Unit tests for {@link ServerAddressPropertyEditor}.
*
* @author Oliver Gierke
*/
public class ServerAddressPropertyEditorUnitTests {
ServerAddressPropertyEditor editor;
@Before
public void setUp() {
editor = new ServerAddressPropertyEditor();
}
/**
* @see DATAMONGO-454
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsAddressConfigWithoutASingleParsableServerAddress() {
editor.setAsText("foo, bar");
}
/**
* @see DATAMONGO-454
*/
@Test
public void skipsUnparsableAddressIfAtLeastOneIsParsable() throws UnknownHostException {
editor.setAsText("foo, localhost");
assertSingleAddressOfLocalhost(editor.getValue());
}
/**
* @see DATAMONGO-454
*/
@Test
public void handlesEmptyAddressAsParseError() throws UnknownHostException {
editor.setAsText(", localhost");
assertSingleAddressOfLocalhost(editor.getValue());
}
private static void assertSingleAddressOfLocalhost(Object result) throws UnknownHostException {
assertThat(result, is(instanceOf(ServerAddress[].class)));
Collection<ServerAddress> addresses = Arrays.asList((ServerAddress[]) result);
assertThat(addresses, hasSize(1));
assertThat(addresses, hasItem(new ServerAddress("localhost")));
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.config;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import com.mongodb.WriteConcern;
/**
* Unit tests for {@link StringToWriteConcernConverter}.
*
* @author Oliver Gierke
*/
public class StringToWriteConcernConverterUnitTest {
StringToWriteConcernConverter converter = new StringToWriteConcernConverter();
@Test
public void createsWellKnownConstantsCorrectly() {
assertThat(converter.convert("SAFE"), is(WriteConcern.SAFE));
}
@Test
public void createsWriteConcernForUnknownValue() {
assertThat(converter.convert("-1"), is(new WriteConcern("-1")));
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.config;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import com.mongodb.WriteConcern;
/**
* Unit tests for {@link WriteConcernPropertyEditor}.
*
* @author Oliver Gierke
*/
public class WriteConcernPropertyEditorUnitTests {
WriteConcernPropertyEditor editor;
@Before
public void setUp() {
editor = new WriteConcernPropertyEditor();
}
@Test
public void createsWriteConcernForWellKnownConstants() {
editor.setAsText("SAFE");
assertThat(editor.getValue(), is((Object) WriteConcern.SAFE));
}
@Test
public void createsWriteConcernForUnknownConstants() {
editor.setAsText("-1");
assertThat(editor.getValue(), is((Object) new WriteConcern("-1")));
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import com.mongodb.BasicDBList;
import com.mongodb.DBObject;
/**
* Helper classes to ease assertions on {@link DBObject}s.
*
* @author Oliver Gierke
*/
public abstract class DBObjectUtils {
private DBObjectUtils() {
}
/**
* Expects the field with the given key to be not {@literal null} and a {@link DBObject} in turn and returns it.
*
* @param source the {@link DBObject} to lookup the nested one
* @param key the key of the field to lokup the nested {@link DBObject}
* @return
*/
public static DBObject getAsDBObject(DBObject source, String key) {
return getTypedValue(source, key, DBObject.class);
}
/**
* Expects the field with the given key to be not {@literal null} and a {@link BasicDBList}.
*
* @param source the {@link DBObject} to lookup the {@link BasicDBList} in
* @param key the key of the field to find the {@link BasicDBList} in
* @return
*/
public static BasicDBList getAsDBList(DBObject source, String key) {
return getTypedValue(source, key, BasicDBList.class);
}
/**
* Expects the list element with the given index to be a non-{@literal null} {@link DBObject} and returns it.
*
* @param source the {@link BasicDBList} to look up the {@link DBObject} element in
* @param index the index of the element expected to contain a {@link DBObject}
* @return
*/
public static DBObject getAsDBObject(BasicDBList source, int index) {
assertThat(source.size(), greaterThanOrEqualTo(index + 1));
Object value = source.get(index);
assertThat(value, is(instanceOf(DBObject.class)));
return (DBObject) value;
}
@SuppressWarnings("unchecked")
private static <T> T getTypedValue(DBObject source, String key, Class<T> type) {
Object value = source.get(key);
assertThat(value, is(notNullValue()));
assertThat(value, is(instanceOf(type)));
return (T) value;
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.core;
public class Friend {
private String id;

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import com.mongodb.DB;
import com.mongodb.Mongo;
/**
* Unit tests for {@link MongoDbUtils}.
*
* @author Oliver Gierke
*/
public class MongoDbUtilsUnitTests {
Mongo mongo;
@Before
public void setUp() throws Exception {
this.mongo = new Mongo();
TransactionSynchronizationManager.initSynchronization();
}
@After
public void tearDown() {
for (Object key : TransactionSynchronizationManager.getResourceMap().keySet()) {
TransactionSynchronizationManager.unbindResource(key);
}
TransactionSynchronizationManager.clearSynchronization();
}
@Test
public void returnsNewInstanceForDifferentDatabaseName() {
DB first = MongoDbUtils.getDB(mongo, "first");
assertThat(first, is(notNullValue()));
assertThat(MongoDbUtils.getDB(mongo, "first"), is(first));
DB second = MongoDbUtils.getDB(mongo, "second");
assertThat(second, is(not(first)));
assertThat(MongoDbUtils.getDB(mongo, "second"), is(second));
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2012 the original author or authors.
*
* 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.
*/
package org.springframework.data.mongodb.core;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.data.mongodb.config.WriteConcernPropertyEditor;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.WriteConcern;
/**
* Integration tests for {@link MongoFactoryBean}.
*
* @author Oliver Gierke
*/
public class MongoFactoryBeanIntegrationTest {
/**
* @see DATAMONGO-408
*/
@Test
public void convertsWriteConcernCorrectly() {
RootBeanDefinition definition = new RootBeanDefinition(MongoFactoryBean.class);
definition.getPropertyValues().addPropertyValue("writeConcern", "SAFE");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerCustomEditor(WriteConcern.class, WriteConcernPropertyEditor.class);
factory.registerBeanDefinition("factory", definition);
MongoFactoryBean bean = factory.getBean("&factory", MongoFactoryBean.class);
assertThat(ReflectionTestUtils.getField(bean, "writeConcern"), is((Object) WriteConcern.SAFE));
}
}

View File

@@ -30,8 +30,10 @@ import org.springframework.dao.DataAccessException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.AbstractMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.NearQuery;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -290,6 +292,66 @@ public abstract class MongoOperationsUnitTests {
}.assertDataAccessException();
}
/**
* @see DATAMONGO-341
*/
@Test
public void geoNearRejectsNullNearQuery() {
new Execution() {
@Override
public void doWith(MongoOperations operations) {
operations.geoNear(null, Person.class);
}
}.assertDataAccessException();
}
/**
* @see DATAMONGO-341
*/
@Test
public void geoNearRejectsNullNearQueryifCollectionGiven() {
new Execution() {
@Override
public void doWith(MongoOperations operations) {
operations.geoNear(null, Person.class, "collection");
}
}.assertDataAccessException();
}
/**
* @see DATAMONGO-341
*/
@Test
public void geoNearRejectsNullEntityClass() {
final NearQuery query = NearQuery.near(new Point(10, 20));
new Execution() {
@Override
public void doWith(MongoOperations operations) {
operations.geoNear(query, null);
}
}.assertDataAccessException();
}
/**
* @see DATAMONGO-341
*/
@Test
public void geoNearRejectsNullEntityClassIfCollectionGiven() {
final NearQuery query = NearQuery.near(new Point(10, 20));
new Execution() {
@Override
public void doWith(MongoOperations operations) {
operations.geoNear(query, null, "collection");
}
}.assertDataAccessException();
}
private abstract class Execution {
public void assertDataAccessException() {

Some files were not shown because too many files have changed in this diff Show More