Compare commits

..

59 Commits

Author SHA1 Message Date
Spring Buildmaster
4be229312c DATAMONGO-396 - Release 1.1.0.M1. 2012-05-07 06:11:25 -07:00
Oliver Gierke
54ec7af462 DATAMONGO-396 - Updated changelog before 1.1.0.M1 release.
Fixed dependency to Spring Data Commons to 1.3.0.RC2.
2012-05-07 14:54:20 +02:00
Oliver Gierke
7162677992 DATAMONGO-443 - Upgraded to Querydsl 2.5.0.
Updated repository implementation and support classes to use Google Guava API instead of Commons Collections introduced in Querydsl 2.5.0.
2012-05-07 14:28:46 +02:00
Oliver Gierke
cf131aaa7e DATAMONGO-441 - Improved MongoDBUtils API.
Use UserCredentials value object instead of low level String and char[] for username and password. Adapted clients accordingly.
2012-05-07 14:28:45 +02:00
Thomas Risberg
6cd3b7a3aa DATAMONGO-299 - Adding tests ensuring exception alert for multiple conditions for same key. 2012-05-07 14:28:22 +02:00
Oliver Gierke
71e639402c DATAMONGO-439 - Created performance tests.
Added a performance tests that tests two things. Write performance in a variety of WriteConcern configurations as well as write-read performance against WriteConcern.SAFE. The tests can be executed by running the Maven build with the performance-test profile. Fixed some performance hotspots discovered in the course of profiling the test case.
2012-05-03 17:57:26 +02:00
Oliver Gierke
fed31c74e1 DATACMNS-166 - Adapted changes of Spring Data Commons. 2012-05-02 19:54:31 +02:00
Maciej Walkowiak
5b13307e39 DATAMONGO-36 - Added support for JSR-303 validation
Added event listener that uses a java.validation.Validator to trigger validation on an entity before persisting it. This validation is enabled by default if the javx.validation API is present on the classpath. Added namespace attribute 'disable-validation' to allow disabling that auto enabling behavior.
2012-04-27 15:36:29 +02:00
Oliver Gierke
d317bcfa71 DATAMONGO-396 - Upgrade to current Spring Data Commons snapshot version. 2012-04-26 19:23:34 +02:00
Oliver Gierke
883bfbe79e Added Eclipse formatter settings.
Formatted cross-store module with the Spring Data formatter.
2012-04-16 15:29:48 +02:00
Oliver Gierke
7502ac137c DATAMONGO-432 - Upgraded to Spring Data Commons 1.3.0.RC1. 2012-04-16 15:19:59 +02:00
Oliver Gierke
3dfc59bfb8 DATAMONGO-429 - Fixed handling of nested arrays in QueryMapper.
QueryMapper now correctly transforms arrays not concreting them into BasicDBObjects anymore.
2012-04-16 15:19:59 +02:00
Oliver Gierke
9e50621c1b DATAMONGO-431 - Adapted changes in CrudRepository.save(…). 2012-04-16 15:19:59 +02:00
Oliver Gierke
472beaf6d2 DATAMONGO-427 - Added support for Before and After keywords for query creation.
The query creator now supports methods as follows: List<User> findByCreatedAtBefore(Date date);
2012-04-16 15:19:58 +02:00
Oliver Gierke
d6d8a13b0c 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:19:58 +02:00
Oliver Gierke
3dcf93744b 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:19:58 +02:00
Oliver Gierke
6373a70ca4 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. Uses improved UserCredentials API introduced in DATACMNS-142.
2012-04-16 15:19:44 +02:00
Oliver Gierke
858afa80f1 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-02 13:32:54 +02:00
Oliver Gierke
97f57e3541 DATAMONGO-418 - Added support for StartingWith, EndingWith and Containing keywords. 2012-03-21 20:44:48 +01:00
Oliver Gierke
05c2045e45 DATAMONGO-347 - Derived repository queries now correctly resolve referenced DBRef properties.
Added toDBRef(…) method to MongoWriter to be able to create a DBRef representation of an object. Polished DBRef creation implementation inside MappingMongoConverter. Removed collection and id attributes from @DBRef annotation as they have neither been documented nor used so far. Refactored MongoQueryCreator to be aware of the property and convert the given value into a DBRef if necessary.
2012-03-12 16:21:56 +01:00
Oliver Gierke
d3598198a6 DATAMONGO-360 - Beautify IndexInfo API.
Added a method to IndexInfo to allow asking the object whether it is an index with a given collection of fields. Refactored RepositoryIndexCreationIntegrationTest to use that API to be more safe against potential duplicate index definitions.
2012-03-12 16:20:15 +01:00
Oliver Gierke
2a8fe5bac7 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. Removed OrQuery class as it is deprecated in the 1.0.x branch.
2012-03-12 11:53:50 +01:00
Oliver Gierke
cdc8f52bad DATAMONGO-356 - CDI integration for Mongo repositories. 2012-03-09 17:52:33 +01:00
Oliver Gierke
39492daa50 DATAMONGO-366 - Polished reference documentation.
Fixed link to bug tracker. Polished docbook files a bit.
2012-03-09 16:03:28 +01:00
Oliver Gierke
16904ba1a7 DATAMONGO-402 - Adapt new entity instantiation API.
Adapt to the entity instantiation API refactored to be able to cope with non-static member classes.
2012-03-09 15:22:24 +01:00
Oliver Gierke
51a469b46f DATAMONGO-412 - General overhaul of the JavaConfig base class.
Refactored JavaConfig base class to reflect the XML namespace a bit more closely (esp. regarding configuration of custom converters and thus registering "simple" types). Prevent duplicate invocation of getUserCredentials(). Added JavaDoc to explain which configuration methods use which other ones to ease detailed configuration.
2012-03-08 13:10:50 +01:00
Oliver Gierke
9f2b1f4ed0 DATAMONGO-273, DATAMONGO-294 - Re-enabled accidentally disabled test case. 2012-03-08 12:16:59 +01:00
Oliver Gierke
e0ea8a6088 Fixed some architecture inconsistencies.
Updated Sonargraph architecture description file to include GridFS subsystem. Moved QueryMapper into convert subsystem. Polished MongoRepository interface. Let SimpleMongoRepository implement MongoRepository.
2012-03-08 12:16:42 +01:00
Oliver Gierke
8861934336 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-03-08 12:11:25 +01:00
Oliver Gierke
93c9713adf DATAMONGO-382 - Fixed potential ClassCastException in MappingMongoConverter.
MappingMongoConverter's convertToMongoType(…) now deals with Sets (and more generally all Collections) correctly.
2012-03-06 21:26:10 +01:00
Oliver Gierke
68a0c1ee2c DATAMONGO-394 - Upgrade to Querydsl 2.3.2. 2012-03-06 19:24:45 +01:00
Oliver Gierke
c3d533d59d 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-03-02 09:08:24 +01:00
Oliver Gierke
e4b9be6b38 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-03-02 09:07:59 +01:00
Oliver Gierke
09a8b9c9c4 DATAMONGO-401 - Fixed NullPointerException in StringBasedMongoQuery. 2012-02-11 14:52:21 +01:00
Oliver Gierke
e531508bdc 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:19:23 +01:00
Oliver Gierke
289c4b95ae 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:16:15 +01:00
Oliver Gierke
25e6d6e025 DATAMONGO-396 - Update Spring Data Commons dependency to 1.3.0.M1. 2012-02-08 21:15:03 +01:00
Oliver Gierke
83d18b5ce6 Readded Spring Test dependency to cross-store module. 2012-02-08 20:39:08 +01:00
Oliver Gierke
8f09748d78 DATAMONGO-394 - Upgrade to Querydsl 2.3.1. 2012-02-08 10:57:29 +01:00
Oliver Gierke
4429971548 DATACMNS-390 - Added UUIDToBinaryConverter to be able to handle UUIDs by default. 2012-02-06 15:08:21 +01:00
Oliver Gierke
065a50bc8c DATAMONGO-391 - Move to SLF4J for logging.
Polished pom.xml files. Upgraded to Log4J 1.2.16 as it excludes unnecessary dependencies. Consolidated test dependencies into parent pom. Remove dependency-management sections. Exclude commons-logging in favor of SLF4J.
2012-02-06 15:00:34 +01:00
Oliver Gierke
f8daa69e53 DATACMNS-129 - Implement CrudRepository.findAll(Iterable<ID> ids). 2012-02-02 16:55:09 +01:00
Oliver Gierke
8a18cd4aef DATACMNS-128 - Adapted to API changes of ParametersParameterAccessor. 2012-02-02 16:54:28 +01:00
Oliver Gierke
3320208a1d 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:29:57 +01:00
Oliver Gierke
4ebc6e96d3 DATAMONGO-385 - Added test case to show repositories working with Long id. 2012-02-01 15:30:20 +01:00
Oliver Gierke
91fe5480a8 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:23:29 +01:00
Oliver Gierke
b6322d7bbc DATAMONGO-374 - Added test case showing custom converters being used for updates. 2012-02-01 11:47:01 +01:00
Oliver Gierke
572c4bb2cd DATAMONGO-6 - Initial support for GridFS.
We now provide a GridFsTemplate for storing, retrieving and deleting documents from GridFS. Introduced GridFsResource implementation to integrate with Spring's Resource abstraction. Beyond that GridFsOperations extends ResourcePatternResolver and thus allows to retrieve GridFsResources via Ant-style filename patterns.
2012-02-01 10:28:28 +01:00
Oliver Gierke
9caa8b15cd DATAMONGO-375 - Polished XSD by removing unnecessary version numbers. 2012-02-01 09:59:43 +01:00
Oliver Gierke
275f4fe6b8 Consolidate Maven repository usage to use repo.springsource.org/libs-snapshot. 2012-01-31 18:46:36 +01:00
Oliver Gierke
4c40ef7e3d DATAMONGO-383 - Adapt new EntityInstantiator API of Spring Data Commons. 2012-01-24 10:49:53 +01:00
Oliver Gierke
609097c0dc 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:19:15 +00:00
Oliver Gierke
307d86498e DATAMONGO-368 - MappingMongoConverter does not remove null values from collections anymore. 2012-01-12 15:51:38 +01:00
Oliver Gierke
19879b8dab DATAMONGO-376 - Fixed potential NPE in SpringDataMongodbSerializer. 2012-01-12 12:23:51 +01:00
Oliver Gierke
b190f22551 Polished formatting and Javadoc. 2012-01-12 12:23:18 +01:00
Oliver Gierke
0e9c6a5de8 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 10:52:27 +01:00
Oliver Gierke
5ec0cc7327 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-11 20:25:10 +01:00
Oliver Gierke
817e167056 Fixed failing test case.
Include dependency to Person repository to make sure instance creation is triggered.
2012-01-11 20:03:34 +01:00
Oliver Gierke
4a6fa5cade DATAMONGO-357 - Prepare 1.1.0 development branch. 2011-12-22 21:16:53 +01:00
116 changed files with 4606 additions and 2895 deletions

291
etc/formatting.xml Normal file
View File

@@ -0,0 +1,291 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="12">
<profile kind="CodeFormatterProfile" name="Spring Data" version="12">
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.compiler.source" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
</profile>
</profiles>

50
pom.xml
View File

@@ -4,9 +4,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-dist</artifactId>
<name>Spring Data MongoDB Distribution</name>
<description>Spring Data project for MongoDB</description>
<url>http://www.springsource.org/spring-data/mongodb</url>
<version>1.0.5.BUILD-SNAPSHOT</version>
<version>1.1.0.M1</version>
<packaging>pom</packaging>
<modules>
<module>spring-data-mongodb</module>
@@ -15,18 +13,7 @@
<module>spring-data-mongodb-parent</module>
</modules>
<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>
<developers>
<developer>
<id>trisberg</id>
<name>Thomas Risberg</name>
@@ -51,6 +38,17 @@
</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>
@@ -262,16 +260,10 @@
</build>
<pluginRepositories>
<!-- Necessary for the build extension -->
<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>
</pluginRepository>
<pluginRepository>
<id>spring-libs-release</id>
<name>Spring Framework Maven Release Repository</name>
<url>http://repo.springsource.org/libs-release</url>
<id>spring-plugins-release</id>
<url>http://repo.springsource.org/plugins-release</url>
</pluginRepository>
</pluginRepositories>
@@ -285,9 +277,9 @@
</url>
</site>
<repository>
<id>spring-release</id>
<name>Spring Release Repository</name>
<url>s3://maven.springframework.org/release</url>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>s3://maven.springframework.org/milestone</url>
</repository>
<snapshotRepository>
<id>spring-snapshot</id>
@@ -295,7 +287,5 @@
<url>s3://maven.springframework.org/snapshot</url>
</snapshotRepository>
</distributionManagement>
<scm>
<url>https://github.com/SpringSource/spring-data-mongodb</url>
</scm>
</project>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.5.BUILD-SNAPSHOT</version>
<version>1.1.0.M1</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-cross-store</artifactId>
@@ -15,10 +15,12 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@@ -28,53 +30,19 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<version>${org.springframework.version.range}</version>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-core</artifactId>
<version>${data.commons.version}</version>
</dependency>
<!-- <dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-aspects</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<optional>true</optional>
<version>1.1.0.M1</version>
</dependency>
<dependency>

View File

@@ -58,10 +58,8 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
this.entityManagerFactory = entityManagerFactory;
}
public void getPersistentState(Class<? extends ChangeSetBacked> entityClass,
Object id, final ChangeSet changeSet)
throws DataAccessException, NotFoundException {
public void getPersistentState(Class<? extends ChangeSetBacked> entityClass, Object id, final ChangeSet changeSet)
throws DataAccessException, NotFoundException {
if (id == null) {
log.debug("Unable to load MongoDB data for null id");
@@ -77,8 +75,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
log.debug("Loading MongoDB data for " + dbk);
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
for (DBObject dbo : collection.find(dbk)) {
String key = (String) dbo.get(ENTITY_FIELD_NAME);
if (log.isDebugEnabled()) {
@@ -87,9 +84,8 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
if (!changeSet.getValues().containsKey(key)) {
String className = (String) dbo.get(ENTITY_FIELD_CLASS);
if (className == null) {
throw new DataIntegrityViolationException(
"Unble to convert property " + key
+ ": Invalid metadata, " + ENTITY_FIELD_CLASS + " not available");
throw new DataIntegrityViolationException("Unble to convert property " + key + ": Invalid metadata, "
+ ENTITY_FIELD_CLASS + " not available");
}
Class<?> clazz = ClassUtils.resolveClassName(className, ClassUtils.getDefaultClassLoader());
Object value = mongoTemplate.getConverter().read(clazz, dbo);
@@ -135,10 +131,8 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
dbQuery.put(ENTITY_ID, getPersistentId(entity, cs));
dbQuery.put(ENTITY_CLASS, entity.getClass().getName());
dbQuery.put(ENTITY_FIELD_NAME, key);
DBObject dbId = mongoTemplate.execute(collName,
new CollectionCallback<DBObject>() {
public DBObject doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
DBObject dbId = mongoTemplate.execute(collName, new CollectionCallback<DBObject>() {
public DBObject doInCollection(DBCollection collection) throws MongoException, DataAccessException {
return collection.findOne(dbQuery);
}
});
@@ -147,14 +141,12 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
log.debug("Flush: removing: " + dbQuery);
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
collection.remove(dbQuery);
return null;
}
});
}
else {
} else {
final DBObject dbDoc = new BasicDBObject();
dbDoc.putAll(dbQuery);
if (log.isDebugEnabled()) {
@@ -166,8 +158,7 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
dbDoc.put("_id", dbId.get("_id"));
}
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection)
throws MongoException, DataAccessException {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
collection.save(dbDoc);
return null;
}

View File

@@ -38,235 +38,234 @@ import org.springframework.data.crossstore.HashMapChangeSet;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Aspect to turn an object annotated with @Document into a persistent document
* using Mongo.
* Aspect to turn an object annotated with @Document into a persistent document using Mongo.
*
* @author Thomas Risberg
*/
public aspect MongoDocumentBacking {
private static final Log LOGGER = LogFactory
.getLog(MongoDocumentBacking.class);
private static final Log LOGGER = LogFactory.getLog(MongoDocumentBacking.class);
// Aspect shared config
private ChangeSetPersister<Object> changeSetPersister;
// Aspect shared config
private ChangeSetPersister<Object> changeSetPersister;
public void setChangeSetPersister(
ChangeSetPersister<Object> changeSetPersister) {
this.changeSetPersister = changeSetPersister;
}
public void setChangeSetPersister(ChangeSetPersister<Object> changeSetPersister) {
this.changeSetPersister = changeSetPersister;
}
// ITD to introduce N state to Annotated objects
declare parents : (@Entity *) implements DocumentBacked;
// ITD to introduce N state to Annotated objects
declare parents : (@Entity *) implements DocumentBacked;
// The annotated fields that will be persisted in MongoDB rather than with JPA
declare @field: @RelatedDocument * (@Entity+ *).*:@Transient;
// The annotated fields that will be persisted in MongoDB rather than with JPA
declare @field: @RelatedDocument * (@Entity+ *).*:@Transient;
// -------------------------------------------------------------------------
// Advise user-defined constructors of ChangeSetBacked objects to create a new
// backing ChangeSet
// -------------------------------------------------------------------------
pointcut arbitraryUserConstructorOfChangeSetBackedObject(DocumentBacked entity) :
// -------------------------------------------------------------------------
// Advise user-defined constructors of ChangeSetBacked objects to create a new
// backing ChangeSet
// -------------------------------------------------------------------------
pointcut arbitraryUserConstructorOfChangeSetBackedObject(DocumentBacked entity) :
execution((DocumentBacked+).new(..)) &&
!execution((DocumentBacked+).new(ChangeSet)) &&
this(entity);
pointcut finderConstructorOfChangeSetBackedObject(DocumentBacked entity,
ChangeSet cs) :
pointcut finderConstructorOfChangeSetBackedObject(DocumentBacked entity, ChangeSet cs) :
execution((DocumentBacked+).new(ChangeSet)) &&
this(entity) &&
args(cs);
protected pointcut entityFieldGet(DocumentBacked entity) :
protected pointcut entityFieldGet(DocumentBacked entity) :
get(@RelatedDocument * DocumentBacked+.*) &&
this(entity) &&
!get(* DocumentBacked.*);
protected pointcut entityFieldSet(DocumentBacked entity, Object newVal) :
protected pointcut entityFieldSet(DocumentBacked entity, Object newVal) :
set(@RelatedDocument * DocumentBacked+.*) &&
this(entity) &&
args(newVal) &&
!set(* DocumentBacked.*);
// intercept EntityManager.merge calls
public pointcut entityManagerMerge(EntityManager em, Object entity) :
// intercept EntityManager.merge calls
public pointcut entityManagerMerge(EntityManager em, Object entity) :
call(* EntityManager.merge(Object)) &&
target(em) &&
args(entity);
// intercept EntityManager.remove calls
// public pointcut entityManagerRemove(EntityManager em, Object entity) :
// call(* EntityManager.remove(Object)) &&
// target(em) &&
// args(entity);
// move changeSet from detached entity to the newly merged persistent object
Object around(EntityManager em, Object entity) : entityManagerMerge(em, entity) {
Object mergedEntity = proceed(em, entity);
if (entity instanceof DocumentBacked && mergedEntity instanceof DocumentBacked) {
((DocumentBacked)mergedEntity).changeSet = ((DocumentBacked)entity).getChangeSet();
}
return mergedEntity;
}
// intercept EntityManager.remove calls
// public pointcut entityManagerRemove(EntityManager em, Object entity) :
// call(* EntityManager.remove(Object)) &&
// target(em) &&
// args(entity);
// clear changeSet from removed entity
// Object around(EntityManager em, Object entity) : entityManagerRemove(em, entity) {
// if (entity instanceof DocumentBacked) {
// removeChangeSetValues((DocumentBacked)entity);
// }
// return proceed(em, entity);
// }
private static void removeChangeSetValues(DocumentBacked entity) {
LOGGER.debug("Removing all change-set values for " + entity);
ChangeSet nulledCs = new HashMapChangeSet();
DocumentBacked documentEntity = (DocumentBacked) entity;
@SuppressWarnings("unchecked")
ChangeSetPersister<Object> changeSetPersister = (ChangeSetPersister<Object>)documentEntity.itdChangeSetPersister;
try {
changeSetPersister.getPersistentState(
documentEntity.getClass(),
documentEntity.get_persistent_id(),
documentEntity.getChangeSet());
}
catch (DataAccessException e) {}
catch (NotFoundException e) {}
for (String key :entity.getChangeSet().getValues().keySet()) {
nulledCs.set(key, null);
// move changeSet from detached entity to the newly merged persistent object
Object around(EntityManager em, Object entity) : entityManagerMerge(em, entity) {
Object mergedEntity = proceed(em, entity);
if (entity instanceof DocumentBacked && mergedEntity instanceof DocumentBacked) {
((DocumentBacked) mergedEntity).changeSet = ((DocumentBacked) entity).getChangeSet();
}
return mergedEntity;
}
entity.setChangeSet(nulledCs);
}
before(DocumentBacked entity) : arbitraryUserConstructorOfChangeSetBackedObject(entity) {
LOGGER
.debug("User-defined constructor called on DocumentBacked object of class "
+ entity.getClass());
// Populate all ITD fields
entity.setChangeSet(new HashMapChangeSet());
entity.itdChangeSetPersister = changeSetPersister;
entity.itdTransactionSynchronization =
new ChangeSetBackedTransactionSynchronization(changeSetPersister, entity);
//registerTransactionSynchronization(entity);
}
// clear changeSet from removed entity
// Object around(EntityManager em, Object entity) : entityManagerRemove(em, entity) {
// if (entity instanceof DocumentBacked) {
// removeChangeSetValues((DocumentBacked)entity);
// }
// return proceed(em, entity);
// }
private static void registerTransactionSynchronization(DocumentBacked entity) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
if (!TransactionSynchronizationManager.getSynchronizations().contains(entity.itdTransactionSynchronization)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding transaction synchronization for " + entity);
}
TransactionSynchronizationManager.registerSynchronization(entity.itdTransactionSynchronization);
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization already active for " + entity);
}
}
}
else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization is not active for " + entity);
}
}
}
private static void removeChangeSetValues(DocumentBacked entity) {
LOGGER.debug("Removing all change-set values for " + entity);
ChangeSet nulledCs = new HashMapChangeSet();
DocumentBacked documentEntity = (DocumentBacked) entity;
@SuppressWarnings("unchecked")
ChangeSetPersister<Object> changeSetPersister = (ChangeSetPersister<Object>) documentEntity.itdChangeSetPersister;
try {
changeSetPersister.getPersistentState(documentEntity.getClass(), documentEntity.get_persistent_id(),
documentEntity.getChangeSet());
} catch (DataAccessException e) {
} catch (NotFoundException e) {
}
for (String key : entity.getChangeSet().getValues().keySet()) {
nulledCs.set(key, null);
}
entity.setChangeSet(nulledCs);
}
// -------------------------------------------------------------------------
// ChangeSet-related mixins
// -------------------------------------------------------------------------
// Introduced field
@Transient private ChangeSet DocumentBacked.changeSet;
before(DocumentBacked entity) : arbitraryUserConstructorOfChangeSetBackedObject(entity) {
LOGGER.debug("User-defined constructor called on DocumentBacked object of class " + entity.getClass());
// Populate all ITD fields
entity.setChangeSet(new HashMapChangeSet());
entity.itdChangeSetPersister = changeSetPersister;
entity.itdTransactionSynchronization = new ChangeSetBackedTransactionSynchronization(changeSetPersister, entity);
// registerTransactionSynchronization(entity);
}
@Transient private ChangeSetPersister<?> DocumentBacked.itdChangeSetPersister;
private static void registerTransactionSynchronization(DocumentBacked entity) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
if (!TransactionSynchronizationManager.getSynchronizations().contains(entity.itdTransactionSynchronization)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding transaction synchronization for " + entity);
}
TransactionSynchronizationManager.registerSynchronization(entity.itdTransactionSynchronization);
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization already active for " + entity);
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Transaction synchronization is not active for " + entity);
}
}
}
@Transient private ChangeSetBackedTransactionSynchronization DocumentBacked.itdTransactionSynchronization;
// -------------------------------------------------------------------------
// ChangeSet-related mixins
// -------------------------------------------------------------------------
// Introduced field
@Transient
private ChangeSet DocumentBacked.changeSet;
public void DocumentBacked.setChangeSet(ChangeSet cs) {
this.changeSet = cs;
}
@Transient
private ChangeSetPersister<?> DocumentBacked.itdChangeSetPersister;
public ChangeSet DocumentBacked.getChangeSet() {
return changeSet;
}
@Transient
private ChangeSetBackedTransactionSynchronization DocumentBacked.itdTransactionSynchronization;
// Flush the entity state to the persistent store
public void DocumentBacked.flush() {
Object id = itdChangeSetPersister.getPersistentId(this, this.changeSet);
itdChangeSetPersister.persistState(this, this.changeSet);
}
public void DocumentBacked.setChangeSet(ChangeSet cs) {
this.changeSet = cs;
}
public Object DocumentBacked.get_persistent_id() {
return itdChangeSetPersister.getPersistentId(this, this.changeSet);
}
public ChangeSet DocumentBacked.getChangeSet() {
return changeSet;
}
// lifecycle methods
@javax.persistence.PostPersist public void DocumentBacked.itdPostPersist() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PrePersist: " + this.getClass().getName());
}
registerTransactionSynchronization(this);
}
@javax.persistence.PreUpdate public void DocumentBacked.itdPreUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PreUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostUpdate public void DocumentBacked.itdPostUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostRemove public void DocumentBacked.itdPostRemove() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostRemove: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
removeChangeSetValues(this);
}
@javax.persistence.PostLoad public void DocumentBacked.itdPostLoad() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostLoad: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
/**
* delegates field reads to the state accessors instance
*/
Object around(DocumentBacked entity): entityFieldGet(entity) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("GET " + f + " -> ChangeSet value property [" + propName
+ "] using: " + entity.getChangeSet());
if (entity.getChangeSet().getValues().get(propName) == null) {
try {
this.changeSetPersister.getPersistentState(entity.getClass(),
entity.get_persistent_id(), entity.getChangeSet());
} catch (NotFoundException e) {
}
}
Object fValue = entity.getChangeSet().getValues().get(propName);
if (fValue != null) {
return fValue;
}
return proceed(entity);
}
// Flush the entity state to the persistent store
public void DocumentBacked.flush() {
Object id = itdChangeSetPersister.getPersistentId(this, this.changeSet);
itdChangeSetPersister.persistState(this, this.changeSet);
}
/**
* delegates field writes to the state accessors instance
*/
Object around(DocumentBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("SET " + f + " -> ChangeSet number value property [" + propName
+ "] with value=[" + newVal + "]");
entity.getChangeSet().set(propName, newVal);
return proceed(entity, newVal);
}
public Object DocumentBacked.get_persistent_id() {
return itdChangeSetPersister.getPersistentId(this, this.changeSet);
}
Field field(JoinPoint joinPoint) {
FieldSignature fieldSignature = (FieldSignature) joinPoint.getSignature();
return fieldSignature.getField();
}
// lifecycle methods
@javax.persistence.PostPersist
public void DocumentBacked.itdPostPersist() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PrePersist: " + this.getClass().getName());
}
registerTransactionSynchronization(this);
}
@javax.persistence.PreUpdate
public void DocumentBacked.itdPreUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PreUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostUpdate
public void DocumentBacked.itdPostUpdate() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostUpdate: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
@javax.persistence.PostRemove
public void DocumentBacked.itdPostRemove() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostRemove: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
removeChangeSetValues(this);
}
@javax.persistence.PostLoad
public void DocumentBacked.itdPostLoad() {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("JPA lifecycle event PostLoad: " + this.getClass().getName() + " :: " + this);
}
registerTransactionSynchronization(this);
}
/**
* delegates field reads to the state accessors instance
*/
Object around(DocumentBacked entity): entityFieldGet(entity) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("GET " + f + " -> ChangeSet value property [" + propName + "] using: " + entity.getChangeSet());
if (entity.getChangeSet().getValues().get(propName) == null) {
try {
this.changeSetPersister
.getPersistentState(entity.getClass(), entity.get_persistent_id(), entity.getChangeSet());
} catch (NotFoundException e) {
}
}
Object fValue = entity.getChangeSet().getValues().get(propName);
if (fValue != null) {
return fValue;
}
return proceed(entity);
}
/**
* delegates field writes to the state accessors instance
*/
Object around(DocumentBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
Field f = field(thisJoinPoint);
String propName = f.getName();
LOGGER.trace("SET " + f + " -> ChangeSet number value property [" + propName + "] with value=[" + newVal + "]");
entity.getChangeSet().set(propName, newVal);
return proceed(entity, newVal);
}
Field field(JoinPoint joinPoint) {
FieldSignature fieldSignature = (FieldSignature) joinPoint.getSignature();
return fieldSignature.getField();
}
}

View File

@@ -24,8 +24,6 @@ import java.lang.annotation.Target;
* @author Thomas Risberg
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.FIELD
})
@Target({ ElementType.FIELD })
public @interface RelatedDocument {
}

View File

@@ -90,8 +90,7 @@ public class CrossStoreMongoTests {
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found.getResume());
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; "
+ "VMware, Developer, 2007-", found.getResume().getJobs());
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; " + "VMware, Developer, 2007-", found.getResume().getJobs());
found.getResume().addJob("SpringDeveloper.com, Consultant, 2005-2006");
found.setAge(44);
}
@@ -106,8 +105,7 @@ public class CrossStoreMongoTests {
Assert.assertNotNull(found);
Assert.assertEquals(Long.valueOf(1), found.getId());
Assert.assertNotNull(found.getResume());
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; "
+ "VMware, Developer, 2007-" + "; "
Assert.assertEquals("DiMark, DBA, 1990-2000" + "; " + "VMware, Developer, 2007-" + "; "
+ "SpringDeveloper.com, Consultant, 2005-2006", found.getResume().getJobs());
}

View File

@@ -23,8 +23,7 @@ public class Address {
private String state;
private String zip;
public Address(Integer streetNumber, String streetName, String city,
String state, String zip) {
public Address(Integer streetNumber, String streetName, String city, String state, String zip) {
super();
this.streetNumber = streetNumber;
this.streetName = streetName;
@@ -36,34 +35,41 @@ public class Address {
public Integer getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(Integer streetNumber) {
this.streetNumber = streetNumber;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.5.BUILD-SNAPSHOT</version>
<version>1.1.0.M1</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb-log4j</artifactId>
@@ -27,7 +27,7 @@
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>compile</scope>
<version>${log4j.version}</version>
</dependency>
</dependencies>

View File

@@ -4,98 +4,24 @@
<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.5.BUILD-SNAPSHOT</version>
<version>1.1.0.M1</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- versions for commonly-used dependencies -->
<junit.version>4.10</junit.version>
<junit.version>4.8.1</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.slf4j.version>1.6.1</org.slf4j.version>
<org.codehaus.jackson.version>1.6.1</org.codehaus.jackson.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>
<data.commons.version>1.3.0.RC2</data.commons.version>
<aspectj.version>1.6.11.RELEASE</aspectj.version>
</properties>
<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
@@ -107,9 +33,9 @@
</url>
</site>
<repository>
<id>spring-release</id>
<name>Spring Release Repository</name>
<url>s3://maven.springframework.org/release</url>
<id>spring-milestone</id>
<name>Spring Milestone Repository</name>
<url>s3://maven.springframework.org/milestone</url>
</repository>
<snapshotRepository>
<id>spring-snapshot</id>
@@ -117,168 +43,68 @@
<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
must explicitly declare the groupId/artifactId of these dependencies
in order for them to show up on the classpath, but metadata like
<version> and <scope> are inherited, which cuts down on verbosity.
see
http://www.sonatype.com/books/mvnref-book/reference/pom-relationships-sect-dep-manage.html
-->
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<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.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version.range}</version>
<scope>test</scope>
</dependency>
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-core</artifactId>
<version>${data.commons.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-aspects</artifactId>
<version>${data.commons.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchdb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.0.5.BUILD-SNAPSHOT</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${org.mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 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>
<!-- Test dependencies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>${org.mockito.version}</version>
<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>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version.range}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@@ -293,11 +119,48 @@
<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>
<version>2.3.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
@@ -317,13 +180,16 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<version>2.8</version>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
</includes>
<junitArtifactName>junit:junit-dep</junitArtifactName>
<excludes>
<exclude>**/PerformanceTests.java</exclude>
</excludes>
<junitArtifactName>junit:junit</junitArtifactName>
</configuration>
</plugin>
<plugin>
@@ -378,15 +244,11 @@
<id>spring-plugins-release</id>
<url>http://repo.springsource.org/plugins-release</url>
</pluginRepository>
<pluginRepository>
<id>querydsl</id>
<url>http://source.mysema.com/maven2/releases</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>spring-libs-release</id>
<url>http://repo.springsource.org/libs-release</url>
<id>spring-libs-snapshot</id>
<url>http://repo.springsource.org/libs-snapshot</url>
</repository>
</repositories>
<reporting>

View File

@@ -49,6 +49,12 @@
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core"/>
</element>
<element name="GridFS" type="Layer">
<element name="Assignment" type="TypeFilter">
<element name="**.gridfs.**" type="IncludeTypePattern"/>
</element>
<dependency type="AllowedDependency" toName="Project|spring-data-mongodb::Layer|Core"/>
</element>
<element name="Core" type="Layer">
<element name="Assignment" type="TypeFilter">
<element name="**.core.**" type="IncludeTypePattern"/>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.0.5.BUILD-SNAPSHOT</version>
<version>1.1.0.M1</version>
<relativePath>../spring-data-mongodb-parent/pom.xml</relativePath>
</parent>
<artifactId>spring-data-mongodb</artifactId>
@@ -12,7 +12,10 @@
<properties>
<mongo.version>2.7.1</mongo.version>
<querydsl.version>2.3.0</querydsl.version>
<querydsl.version>2.5.0</querydsl.version>
<cdi.version>1.0</cdi.version>
<validation.version>1.0.0.GA</validation.version>
<webbeans.version>1.1.3</webbeans.version>
</properties>
<dependencies>
@@ -21,27 +24,33 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<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-beans</artifactId>
<version>${org.springframework.version.range}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version.range}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<version>${org.springframework.version.range}</version>
</dependency>
<!-- Spring Data -->
@@ -69,45 +78,98 @@
<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>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<!-- Test dependencies -->
<!-- CDI -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>${cdi.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>${cdi.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.apache.openwebbeans.test</groupId>
<artifactId>cditest-owb</artifactId>
<version>${webbeans.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>test</scope>
</dependency>
<!-- JSR 303 Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>performance-tests</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.8</version>
<configuration>
<includes>
<include>**/PerformanceTests.java</include>
</includes>
<excludes>
<exclude>none</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<plugins>
<plugin>
@@ -118,14 +180,7 @@
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>maven-apt-plugin</artifactId>
<version>1.0.4</version>
<dependencies>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<version>1.0.2</version>
<executions>
<execution>
<phase>generate-test-sources</phase>

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.
@@ -16,42 +16,51 @@
package org.springframework.data.mongodb;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.data.authentication.UserCredentials;
/**
* Exception being thrown in case we cannot connect to a MongoDB instance.
*
* @author Oliver Gierke
*/
public class CannotGetMongoDbConnectionException extends DataAccessResourceFailureException {
private String username;
private char[] password;
private String database;
private final UserCredentials credentials;
private final String database;
private static final long serialVersionUID = 1172099106475265589L;
public CannotGetMongoDbConnectionException(String msg, Throwable cause) {
super(msg, cause);
this.database = null;
this.credentials = UserCredentials.NO_CREDENTIALS;
}
public CannotGetMongoDbConnectionException(String msg) {
super(msg);
this(msg, null, UserCredentials.NO_CREDENTIALS);
}
public CannotGetMongoDbConnectionException(String msg, String database, String username, char[] password) {
public CannotGetMongoDbConnectionException(String msg, String database, UserCredentials credentials) {
super(msg);
this.username = username;
this.password = password == null ? null : password.clone();
this.database = database;
this.credentials = credentials;
}
public String getUsername() {
return username;
}
public char[] getPassword() {
return password;
/**
* Returns the {@link UserCredentials} that were used when trying to connect to the MongoDB instance.
*
* @return
*/
public UserCredentials getCredentials() {
return this.credentials;
}
/**
* Returns the name of the database trying to be accessed.
*
* @return
*/
public String getDatabase() {
return database;
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.mongodb.config;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -22,12 +23,13 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
@@ -37,7 +39,7 @@ import org.springframework.util.StringUtils;
import com.mongodb.Mongo;
/**
* Abstract base class to ease JavaConfig setup for Spring Data MongoDB.
* Base class for Spring Data Mongo configuration using JavaConfig.
*
* @author Mark Pollack
* @author Oliver Gierke
@@ -45,66 +47,144 @@ import com.mongodb.Mongo;
@Configuration
public abstract class AbstractMongoConfiguration {
public abstract String getDatabaseName();
/**
* Return the name of the database to connect to.
*
* @return must not be {@literal null}.
*/
protected abstract String getDatabaseName();
/**
* Return the {@link Mongo} instance to connect to.
*
* @return
* @throws Exception
*/
@Bean
public abstract Mongo mongo() throws Exception;
/**
* Creates a {@link MongoTemplate}.
*
* @return
* @throws Exception
*/
@Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
}
/**
* Creates a {@link SimpleMongoDbFactory} to be used by the {@link MongoTemplate}. Will use the {@link Mongo} instance
* configured in {@link #mongo()}.
*
* @see #mongo()
* @see #mongoTemplate()
* @return
* @throws Exception
*/
@Bean
public MongoDbFactory mongoDbFactory() throws Exception {
UserCredentials credentials = getUserCredentials();
if (credentials == null) {
public SimpleMongoDbFactory mongoDbFactory() throws Exception {
UserCredentials creadentials = getUserCredentials();
if (creadentials == null) {
return new SimpleMongoDbFactory(mongo(), getDatabaseName());
} else {
return new SimpleMongoDbFactory(mongo(), getDatabaseName(), credentials);
return new SimpleMongoDbFactory(mongo(), getDatabaseName(), creadentials);
}
}
public String getMappingBasePackage() {
return "";
}
public UserCredentials getUserCredentials() {
/**
* Return the base package to scan for mapped {@link Document}s.
*
* @return
*/
protected String getMappingBasePackage() {
return null;
}
/**
* Return {@link UserCredentials} to be used when connecting to the MongoDB instance or {@literal null} if none shall
* be used.
*
* @return
*/
protected UserCredentials getUserCredentials() {
return null;
}
/**
* Creates a {@link MongoMappingContext} equipped with entity classes scanned from the mapping base package.
*
* @see #getMappingBasePackage()
* @return
* @throws ClassNotFoundException
*/
@Bean
public MongoMappingContext mongoMappingContext() throws ClassNotFoundException, LinkageError {
public MongoMappingContext mongoMappingContext() throws ClassNotFoundException {
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
mappingContext.afterPropertiesSet();
return mappingContext;
}
/**
* Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
* {@link CustomConversions} will be registered with the {@link #mappingMongoConverter()} and
* {@link #mongoMappingContext()}. Returns an empty {@link CustomConversions} instance by default.
*
* @return must not be {@literal null}.
*/
@Bean
public CustomConversions customConversions() {
return new CustomConversions(Collections.emptyList());
}
/**
* Creates a {@link MappingMongoConverter} using the configured {@link #mongoDbFactory()} and
* {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #mongoMappingContext()
* @see #mongoDbFactory()
* @return
* @throws Exception
*/
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), mongoMappingContext());
converter.setCustomConversions(customConversions());
return converter;
}
/**
* Scans the mapping base package for classes annotated with {@link Document}.
*
* @see #getMappingBasePackage()
* @return
* @throws ClassNotFoundException
*/
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getMappingBasePackage();
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), mappingContext.getClass()
.getClassLoader()));
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
AbstractMongoConfiguration.class.getClassLoader()));
}
mappingContext.setInitialEntitySet(initialEntitySet);
}
return mappingContext;
}
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), mongoMappingContext());
afterMappingMongoConverterCreation(converter);
return converter;
}
/**
* Hook that allows post-processing after the MappingMongoConverter has been successfully created.
*
* @param converter
*/
protected void afterMappingMongoConverterCreation(MappingMongoConverter converter) {
return initialEntitySet;
}
}

View File

@@ -25,4 +25,5 @@ public abstract class BeanNames {
static final String INDEX_HELPER = "indexCreationHelper";
static final String MONGO = "mongo";
static final String DB_FACTORY = "mongoDbFactory";
static final String VALIDATING_EVENT_LISTENER = "validatingMongoEventListener";
}

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.config;
import static org.springframework.data.mongodb.config.BeanNames.*;
@@ -30,12 +29,14 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -52,18 +53,25 @@ 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.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
* Bean definition parser for the {@code mapping-converter} element.
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Maciej Walkowiak
*/
public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
private static final String BASE_PACKAGE = "base-package";
private static final boolean jsr303Present = ClassUtils.isPresent("javax.validation.Validator",
MappingMongoConverterParser.class.getClassLoader());
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
@@ -107,9 +115,53 @@ public class MappingMongoConverterParser extends AbstractBeanDefinitionParser {
registry.registerBeanDefinition(INDEX_HELPER, indexHelperBuilder.getBeanDefinition());
}
BeanDefinition validatingMongoEventListener = potentiallyCreateValidatingMongoEventListener(element, parserContext);
if (validatingMongoEventListener != null) {
registry.registerBeanDefinition(VALIDATING_EVENT_LISTENER, validatingMongoEventListener);
}
return converterBuilder.getBeanDefinition();
}
private BeanDefinition potentiallyCreateValidatingMongoEventListener(Element element, ParserContext parserContext) {
String disableValidation = element.getAttribute("disable-validation");
boolean validationDisabled = StringUtils.hasText(disableValidation) && Boolean.valueOf(disableValidation);
if (!validationDisabled) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
RuntimeBeanReference validator = getValidator(builder, parserContext);
if (validator != null) {
builder.getRawBeanDefinition().setBeanClass(ValidatingMongoEventListener.class);
builder.addConstructorArgValue(validator);
return builder.getBeanDefinition();
}
}
return null;
}
private RuntimeBeanReference getValidator(Object source, ParserContext parserContext) {
if (!jsr303Present) {
return null;
}
RootBeanDefinition validatorDef = new RootBeanDefinition(
"org.springframework.validation.beanvalidation.LocalValidatorFactoryBean");
validatorDef.setSource(source);
validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
parserContext.registerComponent(new BeanComponentDefinition(validatorDef, validatorName));
return new RuntimeBeanReference(validatorName);
}
private String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
BeanDefinition conversionsDefinition) {

View File

@@ -17,11 +17,7 @@ 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;
@@ -34,8 +30,6 @@ import com.mongodb.ServerAddress;
*/
public class ServerAddressPropertyEditor extends PropertyEditorSupport {
private static final Log LOG = LogFactory.getLog(ServerAddressPropertyEditor.class);
/*
* (non-Javadoc)
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
@@ -44,49 +38,21 @@ public class ServerAddressPropertyEditor extends PropertyEditorSupport {
public void setAsText(String replicaSetString) {
String[] replicaSetStringArray = StringUtils.commaDelimitedListToStringArray(replicaSetString);
Set<ServerAddress> serverAddresses = new HashSet<ServerAddress>(replicaSetStringArray.length);
ServerAddress[] serverAddresses = new ServerAddress[replicaSetStringArray.length];
for (String element : replicaSetStringArray) {
for (int i = 0; i < replicaSetStringArray.length; i++) {
ServerAddress address = parseServerAddress(element);
String[] hostAndPort = StringUtils.delimitedListToStringArray(replicaSetStringArray[i], ":");
if (address != null) {
serverAddresses.add(address);
try {
serverAddresses[i] = new ServerAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1]));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Could not parse port " + hostAndPort[1], e);
} catch (UnknownHostException e) {
throw new IllegalArgumentException("Could not parse host " + hostAndPort[0], e);
}
}
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;
setValue(serverAddresses);
}
}

View File

@@ -16,9 +16,7 @@
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;
@@ -131,7 +129,6 @@ public class DefaultIndexOperations implements IndexOperations {
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()) {
@@ -139,10 +136,8 @@ public class DefaultIndexOperations implements IndexOperations {
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));
@@ -155,7 +150,7 @@ public class DefaultIndexOperations implements IndexOperations {
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));
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse));
}
return indexInfoList;

View File

@@ -20,6 +20,7 @@ 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.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-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.
@@ -15,32 +15,28 @@
*/
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.authentication.UserCredentials;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.Assert;
import com.mongodb.DB;
import com.mongodb.Mongo;
/**
* Mongo server administration exposed via JMX annotations
*
* @author Mark Pollack
*/
@ManagedResource(description = "Mongo Admin Operations")
public class MongoAdmin implements MongoAdminOperations {
/**
* Logger available to subclasses
*/
protected final Log logger = LogFactory.getLog(getClass());
private Mongo mongo;
private final Mongo mongo;
private String username;
private String password;
public MongoAdmin(Mongo mongo) {
Assert.notNull(mongo);
this.mongo = mongo;
}
@@ -83,11 +79,10 @@ public class MongoAdmin implements MongoAdminOperations {
* @param password The password to use
*/
public void setPassword(String password) {
this.password = password;
}
DB getDB(String databaseName) {
return MongoDbUtils.getDB(mongo, databaseName, username, password == null ? null : password.toCharArray());
return MongoDbUtils.getDB(mongo, databaseName, new UserCredentials(username, password));
}
}

View File

@@ -15,8 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
@@ -37,7 +38,7 @@ import com.mongodb.Mongo;
*/
public abstract class MongoDbUtils {
private static final Log LOGGER = LogFactory.getLog(MongoDbUtils.class);
private static final Logger LOGGER = LoggerFactory.getLogger(MongoDbUtils.class);
/**
* Private constructor to prevent instantiation.
@@ -49,29 +50,32 @@ public abstract class MongoDbUtils {
/**
* Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
*
* @param mongo The {@link Mongo} instance
* @param databaseName The database name
* @return The {@link DB} connection
* @param mongo the {@link Mongo} instance, must not be {@literal null}.
* @param databaseName the database name, must not be {@literal null} or empty.
* @return the {@link DB} connection
*/
public static DB getDB(Mongo mongo, String databaseName) {
return doGetDB(mongo, databaseName, null, null, true);
return doGetDB(mongo, databaseName, UserCredentials.NO_CREDENTIALS, true);
}
/**
* Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
*
* @param mongo The {@link Mongo} instance
* @param databaseName The database name
* @param username The username to authenticate with
* @param password The password to authenticate with
* @return The {@link DB} connection
* @param mongo the {@link Mongo} instance, must not be {@literal null}.
* @param databaseName the database name, must not be {@literal null} or empty.
* @param credentials the credentials to use, must not be {@literal null}.
* @return the {@link DB} connection
*/
public static DB getDB(Mongo mongo, String databaseName, String username, char[] password) {
return doGetDB(mongo, databaseName, username, password, true);
public static DB getDB(Mongo mongo, String databaseName, UserCredentials credentials) {
Assert.notNull(mongo, "No Mongo instance specified!");
Assert.hasText(databaseName, "Database name must be given!");
Assert.notNull(credentials, "Credentials must not be null, use UserCredentials.NO_CREDENTIALS!");
return doGetDB(mongo, databaseName, credentials, true);
}
public static DB doGetDB(Mongo mongo, String databaseName, String username, char[] password, boolean allowCreate) {
Assert.notNull(mongo, "No Mongo instance specified");
private static DB doGetDB(Mongo mongo, String databaseName, UserCredentials credentials, boolean allowCreate) {
DbHolder dbHolder = (DbHolder) TransactionSynchronizationManager.getResource(mongo);
if (dbHolder != null && !dbHolder.isEmpty()) {
@@ -79,7 +83,7 @@ public abstract class MongoDbUtils {
DB db = null;
if (TransactionSynchronizationManager.isSynchronizationActive() && dbHolder.doesNotHoldNonDefaultDB()) {
// Spring transaction management is active ->
db = dbHolder.getDB(databaseName);
db = dbHolder.getDB();
if (db != null && !dbHolder.isSynchronizedWithTransaction()) {
LOGGER.debug("Registering Spring transaction synchronization for existing Mongo DB");
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(dbHolder, mongo));
@@ -94,15 +98,15 @@ public abstract class MongoDbUtils {
LOGGER.trace("Getting Mongo Database name=[" + databaseName + "]");
DB db = mongo.getDB(databaseName);
boolean credentialsGiven = username != null && password != null;
boolean credentialsGiven = credentials.hasUsername() && credentials.hasPassword();
if (credentialsGiven && !db.isAuthenticated()) {
// Note, can only authenticate once against the same com.mongodb.DB object.
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);
}
String username = credentials.getUsername();
String password = credentials.hasPassword() ? credentials.getPassword() : null;
if (!db.authenticate(username, password == null ? null : password.toCharArray())) {
throw new CannotGetMongoDbConnectionException("Failed to authenticate to database [" + databaseName
+ "], username = [" + username + "], password = [" + password + "]", databaseName, credentials);
}
}
@@ -113,9 +117,9 @@ public abstract class MongoDbUtils {
LOGGER.debug("Registering Spring transaction synchronization for new Hibernate Session");
DbHolder holderToUse = dbHolder;
if (holderToUse == null) {
holderToUse = new DbHolder(databaseName, db);
holderToUse = new DbHolder(db);
} else {
holderToUse.addDB(databaseName, db);
holderToUse.addDB(db);
}
TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
holderToUse.setSynchronizedWithTransaction(true);
@@ -146,7 +150,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

@@ -16,7 +16,6 @@
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;
@@ -30,9 +29,9 @@ import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -47,7 +46,6 @@ 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;
@@ -55,6 +53,7 @@ import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.GeoResult;
import org.springframework.data.mongodb.core.geo.GeoResults;
@@ -106,11 +105,10 @@ import com.mongodb.util.JSON;
* @author Graeme Rocher
* @author Mark Pollack
* @author Oliver Gierke
* @author Amol Nayak
*/
public class MongoTemplate implements MongoOperations, ApplicationContextAware {
private static final Log LOGGER = LogFactory.getLog(MongoTemplate.class);
private static final Logger LOGGER = LoggerFactory.getLogger(MongoTemplate.class);
private static final String ID = "_id";
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
@SuppressWarnings("serial")
@@ -332,12 +330,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
Assert.notNull(query);
DBObject queryObject = query.getQueryObject();
DBObject sortObject = query.getSortObject();
DBObject fieldsObject = query.getFieldsObject();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Executing query: %s sort: %s fields: %s in collection: $s",
serializeToJsonSafely(queryObject), sortObject, fieldsObject, collectionName));
LOGGER.debug("find using query: " + queryObject + " fields: " + fieldsObject + " in collection: "
+ collectionName);
}
this.executeQueryInternal(new FindCallback(queryObject, fieldsObject), preparer, dch, collectionName);
@@ -456,7 +453,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
} else {
query.limit(1);
List<T> results = find(query, entityClass, collectionName);
return results.isEmpty() ? null : results.get(0);
return (results.isEmpty() ? null : results.get(0));
}
}
@@ -732,13 +729,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT, collectionName,
entityClass, dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) {
wr = collection.insert(dbDoc);
collection.insert(dbDoc);
} else {
wr = collection.insert(dbDoc, writeConcernToUse);
collection.insert(dbDoc, writeConcernToUse);
}
handleAnyWriteResultErrors(wr, dbDoc, "insert");
return dbDoc.get(ID);
}
});
@@ -757,13 +752,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.INSERT_LIST, collectionName, null,
null, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) {
wr = collection.insert(dbDocList);
collection.insert(dbDocList);
} else {
wr = collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse);
collection.insert(dbDocList.toArray((DBObject[]) new BasicDBObject[dbDocList.size()]), writeConcernToUse);
}
handleAnyWriteResultErrors(wr, null, "insert_list");
return null;
}
});
@@ -790,13 +783,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.SAVE, collectionName, entityClass,
dbDoc, null);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
WriteResult wr;
if (writeConcernToUse == null) {
wr = collection.save(dbDoc);
collection.save(dbDoc);
} else {
wr = collection.save(dbDoc, writeConcernToUse);
collection.save(dbDoc, writeConcernToUse);
}
handleAnyWriteResultErrors(wr, dbDoc, "save");
return dbDoc.get(ID);
}
});
@@ -836,11 +827,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
DBObject queryObj = query == null ? new BasicDBObject()
: mapper.getMappedObject(query.getQueryObject(), entity);
DBObject updateObj = update.getUpdateObject();
for (String key : updateObj.keySet()) {
updateObj.put(key, mongoConverter.convertToMongoType(updateObj.get(key)));
}
DBObject updateObj = update == null ? new BasicDBObject() : mapper.getMappedObject(update.getUpdateObject(),
entity);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("calling update using query: " + queryObj + " and update: " + updateObj + " in collection: "
@@ -879,7 +867,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return;
}
doRemove(collection, getIdQueryFor(object), object.getClass());
remove(getIdQueryFor(object), collection);
}
/**
@@ -944,7 +932,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
entityClass, null, queryObject);
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("remove using query: " + dboq + " in collection: " + collection.getName());
LOGGER.debug("remove using query: " + queryObject + " in collection: " + collection.getName());
}
if (writeConcernToUse == null) {
wr = collection.remove(dboq);
@@ -1002,15 +990,26 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
LOGGER.debug("Executing MapReduce on collection [" + command.getInput() + "], mapFunction [" + mapFunc
+ "], reduceFunction [" + reduceFunc + "]");
}
CommandResult commandResult = command.getOutputType() == MapReduceCommand.OutputType.INLINE ? executeCommand(
commandObject, getDb().getOptions()) : executeCommand(commandObject);
handleCommandError(commandResult, commandObject);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("MapReduce command result = [%s]", serializeToJsonSafely(commandObject)));
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);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MapReduce command result = [" + commandResult + "]");
}
MapReduceOutput mapReduceOutput = new MapReduceOutput(inputCollection, commandObject, commandResult);
List<T> mappedResults = new ArrayList<T>();
DbObjectCallback<T> callback = new ReadDbObjectCallback<T>(mongoConverter, entityClass);
@@ -1035,7 +1034,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
if (criteria == null) {
dbo.put("cond", null);
} else {
dbo.put("cond", mapper.getMappedObject(criteria.getCriteriaObject(), null));
dbo.put("cond", criteria.getCriteriaObject());
}
// If initial document was a JavaScript string, potentially loaded by Spring's Resource abstraction, load it and
// convert to DBObject
@@ -1061,11 +1060,20 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
DBObject commandObject = new BasicDBObject("group", dbo);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Executing Group with DBObject [%s]", serializeToJsonSafely(commandObject)));
LOGGER.debug("Executing Group with DBObject [" + commandObject.toString() + "]");
}
CommandResult commandResult = null;
try {
commandResult = executeCommand(commandObject, getDb().getOptions());
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 = executeCommand(commandObject, getDb().getOptions());
handleCommandError(commandResult, commandObject);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Group command result = [" + commandResult + "]");
@@ -1237,14 +1245,11 @@ 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(String.format("find using query: %s fields: %s for class: %s in collection: %s",
serializeToJsonSafely(query), fields, entityClass, collectionName));
LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + entityClass
+ " in collection: " + collectionName);
}
return executeFindMultiInternal(new FindCallback(mapper.getMappedObject(query, entity), fields), preparer,
objectCallback, collectionName);
}
@@ -1326,15 +1331,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
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);
LOGGER.debug("findAndModify using query: " + query + " 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);
return executeFindOneInternal(new FindAndModifyCallback(mapper.getMappedObject(query, entity), fields, sort,
updateObj, options), new ReadDbObjectCallback<T>(readerToUse, entityClass), collectionName);
}
/**
@@ -1355,19 +1358,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return;
}
ConversionService conversionService = mongoConverter.getConversionService();
BeanWrapper<PersistentEntity<Object, ?>, Object> wrapper = BeanWrapper.create(savedObject, conversionService);
try {
Object idValue = wrapper.getProperty(idProp, idProp.getType(), true);
if (idValue != null) {
return;
}
wrapper.setProperty(idProp, id);
BeanWrapper.create(savedObject, mongoConverter.getConversionService()).setProperty(idProp, id);
return;
} catch (IllegalAccessException e) {
throw new MappingException(e.getMessage(), e);
} catch (InvocationTargetException e) {
@@ -1515,16 +1508,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
String error = wr.getError();
if (error != null) {
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);
}
String 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);
@@ -1547,27 +1533,6 @@ 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();

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.
@@ -50,7 +50,7 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
* @param databaseName database name, not be {@literal null}.
*/
public SimpleMongoDbFactory(Mongo mongo, String databaseName) {
this(mongo, databaseName, new UserCredentials(), false);
this(mongo, databaseName, UserCredentials.NO_CREDENTIALS, false);
}
/**
@@ -87,7 +87,7 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
this.mongo = mongo;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.credentials = credentials == null ? new UserCredentials() : credentials;
this.credentials = credentials == null ? UserCredentials.NO_CREDENTIALS : credentials;
}
/**
@@ -115,10 +115,7 @@ 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());
DB db = MongoDbUtils.getDB(mongo, dbName, credentials);
if (writeConcern != null) {
db.setWriteConcern(writeConcern);

View File

@@ -23,6 +23,7 @@ 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.convert.EntityInstantiators;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerToObjectIdConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToBigIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToStringConverter;
@@ -39,6 +40,7 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
protected final GenericConversionService conversionService;
protected CustomConversions conversions = new CustomConversions();
protected EntityInstantiators instantiators = new EntityInstantiators();
/**
* Creates a new {@link AbstractMongoConverter} using the given {@link GenericConversionService}.
@@ -60,6 +62,15 @@ public abstract class AbstractMongoConverter implements MongoConverter, Initiali
this.conversions = conversions;
}
/**
* Registers {@link EntityInstantiators} to customize entity instantiation.
*
* @param instantiators
*/
public void setInstantiators(EntityInstantiators instantiators) {
this.instantiators = instantiators == null ? new EntityInstantiators() : instantiators;
}
/**
* Registers additional converters that will be available when using the {@link ConversionService} directly (e.g. for
* id conversion). These converters are not custom conversions as they'd introduce unwanted conversions (e.g.

View File

@@ -22,8 +22,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
@@ -38,8 +38,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverters.BigDecimalT
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;
@@ -54,7 +52,7 @@ import org.springframework.util.Assert;
*/
public class CustomConversions {
private static final Log LOG = LogFactory.getLog(CustomConversions.class);
private static final Logger LOG = LoggerFactory.getLogger(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.";
@@ -91,8 +89,6 @@ public class CustomConversions {
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) {

View File

@@ -1,180 +0,0 @@
/*
* 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

@@ -1,5 +1,5 @@
/*
* Copyright 2011 by the original author(s).
* Copyright 2011-2012 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.
@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.core.convert;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -25,34 +24,34 @@ 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.convert.TypeMapper;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
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;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.SpELAwareParameterValueProvider;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -72,7 +71,7 @@ import com.mongodb.DBRef;
*/
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
protected static final Log log = LogFactory.getLog(MappingMongoConverter.class);
protected static final Logger log = LoggerFactory.getLogger(MappingMongoConverter.class);
protected final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
protected final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
@@ -83,6 +82,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
protected MongoTypeMapper typeMapper;
protected String mapKeyDotReplacement = null;
private SpELContext spELContext;
/**
* Creates a new {@link MappingMongoConverter} given the new {@link MongoDbFactory} and {@link MappingContext}.
*
@@ -102,6 +103,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
this.mappingContext = mappingContext;
this.typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext);
this.idMapper = new QueryMapper(this);
this.spELContext = new SpELContext(DBObjectPropertyAccessor.INSTANCE);
}
/**
@@ -153,7 +156,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.spELContext = new SpELContext(this.spELContext, applicationContext);
}
/*
@@ -164,8 +169,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return read(ClassTypeInformation.from(clazz), dbo);
}
@SuppressWarnings("unchecked")
protected <S extends Object> S read(TypeInformation<S> type, DBObject dbo) {
return read(type, dbo, null);
}
@SuppressWarnings("unchecked")
protected <S extends Object> S read(TypeInformation<S> type, DBObject dbo, Object parent) {
if (null == dbo) {
return null;
@@ -183,11 +192,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
if (typeToUse.isCollectionLike() && dbo instanceof BasicDBList) {
return (S) readCollectionOrArray(typeToUse, (BasicDBList) dbo);
return (S) readCollectionOrArray(typeToUse, (BasicDBList) dbo, parent);
}
if (typeToUse.isMap()) {
return (S) readMap(typeToUse, dbo);
return (S) readMap(typeToUse, dbo, parent);
}
// Retrieve persistent entity info
@@ -197,37 +206,43 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
throw new MappingException("No mapping metadata found for " + rawType.getName());
}
return read(persistentEntity, dbo);
return read(persistentEntity, dbo, parent);
}
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo) {
private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(MongoPersistentEntity<?> entity,
DBObject source, DefaultSpELExpressionEvaluator evaluator, Object parent) {
final StandardEvaluationContext spelCtx = new StandardEvaluationContext(dbo);
spelCtx.addPropertyAccessor(DBObjectPropertyAccessor.INSTANCE);
MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(source, evaluator, parent);
PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<MongoPersistentProperty>(
entity, provider, parent);
parameterProvider.setSpELEvaluator(evaluator);
if (applicationContext != null) {
spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext));
}
return parameterProvider;
}
final MappedConstructor constructor = new MappedConstructor(entity, mappingContext);
private <S extends Object> S read(final MongoPersistentEntity<S> entity, final DBObject dbo, Object parent) {
SpELAwareParameterValueProvider delegate = new SpELAwareParameterValueProvider(spelExpressionParser, spelCtx);
ParameterValueProvider provider = new DelegatingParameterValueProvider(constructor, dbo, delegate);
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(dbo, spELContext);
final BeanWrapper<MongoPersistentEntity<S>, S> wrapper = BeanWrapper.create(entity, provider, conversionService);
ParameterValueProvider<MongoPersistentProperty> provider = getParameterProvider(entity, dbo, evaluator, parent);
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
S instance = instantiator.createInstance(entity, provider);
final BeanWrapper<MongoPersistentEntity<S>, S> wrapper = BeanWrapper.create(instance, conversionService);
final S result = wrapper.getBean();
// Set properties not already set in the constructor
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
public void doWithPersistentProperty(MongoPersistentProperty prop) {
boolean isConstructorProperty = constructor.isConstructorParameter(prop);
boolean isConstructorProperty = entity.isConstructorArgument(prop);
boolean hasValueForProperty = dbo.containsField(prop.getFieldName());
if (!hasValueForProperty || isConstructorProperty) {
return;
}
Object obj = getValueInternal(prop, dbo, spelCtx, prop.getSpelExpression());
Object obj = getValueInternal(prop, dbo, evaluator, result);
wrapper.setProperty(prop, obj, useFieldAccessOnly);
}
});
@@ -236,7 +251,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
entity.doWithAssociations(new AssociationHandler<MongoPersistentProperty>() {
public void doWithAssociation(Association<MongoPersistentProperty> association) {
MongoPersistentProperty inverseProp = association.getInverse();
Object obj = getValueInternal(inverseProp, dbo, spelCtx, inverseProp.getSpelExpression());
Object obj = getValueInternal(inverseProp, dbo, evaluator, result);
try {
wrapper.setProperty(inverseProp, obj);
} catch (IllegalAccessException e) {
@@ -247,7 +262,23 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
});
return wrapper.getBean();
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.MongoWriter#toDBRef(java.lang.Object, org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
*/
public DBRef toDBRef(Object object, MongoPersistentProperty referingProperty) {
org.springframework.data.mongodb.core.mapping.DBRef annotation = null;
if (referingProperty != null) {
annotation = referingProperty.getDBRef();
Assert.isTrue(annotation != null, "The referenced property has to be mapped with @DBRef!");
}
return createDBRef(object, annotation);
}
/**
@@ -439,6 +470,7 @@ 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) {
@@ -642,12 +674,20 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
protected DBRef createDBRef(Object target, org.springframework.data.mongodb.core.mapping.DBRef dbref) {
Assert.notNull(target);
MongoPersistentEntity<?> targetEntity = mappingContext.getPersistentEntity(target.getClass());
if (null == targetEntity || null == targetEntity.getIdProperty()) {
return null;
if (null == targetEntity) {
throw new MappingException("No mapping metadata found for " + target.getClass());
}
MongoPersistentProperty idProperty = targetEntity.getIdProperty();
if (idProperty == null) {
throw new MappingException("No id property found on class " + targetEntity.getType());
}
BeanWrapper<MongoPersistentEntity<Object>, Object> wrapper = BeanWrapper.create(target, conversionService);
Object id = wrapper.getProperty(idProperty, Object.class, useFieldAccessOnly);
@@ -655,69 +695,17 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
throw new MappingException("Cannot create a reference to an object with a NULL id.");
}
String collection = dbref.collection();
if ("".equals(collection)) {
collection = targetEntity.getCollection();
}
DB db = mongoDbFactory.getDb();
db = dbref != null && StringUtils.hasText(dbref.db()) ? mongoDbFactory.getDb(dbref.db()) : db;
String dbname = dbref.db();
DB db = StringUtils.hasText(dbname) ? mongoDbFactory.getDb(dbname) : mongoDbFactory.getDb();
return new DBRef(db, collection, idMapper.convertId(id));
return new DBRef(db, targetEntity.getCollection(), idMapper.convertId(id));
}
@SuppressWarnings("unchecked")
protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, StandardEvaluationContext ctx,
String spelExpr) {
protected Object getValueInternal(MongoPersistentProperty prop, DBObject dbo, SpELExpressionEvaluator eval,
Object parent) {
Object o;
if (null != spelExpr) {
Expression x = spelExpressionParser.parseExpression(spelExpr);
o = x.getValue(ctx);
} else {
Object sourceValue = dbo.get(prop.getFieldName());
if (sourceValue == null) {
return null;
}
Class<?> propertyType = prop.getType();
if (conversions.hasCustomReadTarget(sourceValue.getClass(), propertyType)) {
return conversionService.convert(sourceValue, propertyType);
}
if (sourceValue instanceof DBRef) {
sourceValue = ((DBRef) sourceValue).fetch();
}
if (sourceValue instanceof DBObject) {
if (prop.isMap()) {
return readMap(prop.getTypeInformation(), (DBObject) sourceValue);
} else if (prop.isArray() && sourceValue instanceof BasicDBObject
&& ((DBObject) sourceValue).keySet().size() == 0) {
// It's empty
return Array.newInstance(prop.getComponentType(), 0);
} else if (prop.isCollectionLike() && sourceValue instanceof BasicDBList) {
return readCollectionOrArray((TypeInformation<? extends Collection<?>>) prop.getTypeInformation(),
(BasicDBList) sourceValue);
}
TypeInformation<?> toType = typeMapper.readType((DBObject) sourceValue, prop.getTypeInformation());
// It's a complex object, have to read it in
if (toType != null) {
// TODO: why do we remove the type?
// dbo.removeField(CUSTOM_TYPE_KEY);
o = read(toType, (DBObject) sourceValue);
} else {
o = read(mappingContext.getPersistentEntity(prop.getTypeInformation()), (DBObject) sourceValue);
}
} else {
o = sourceValue;
}
}
return o;
MongoDbPropertyValueProvider provider = new MongoDbPropertyValueProvider(dbo, spELContext, parent);
return provider.getPropertyValue(prop);
}
/**
@@ -728,29 +716,33 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @return the converted {@link Collections}, will never be {@literal null}.
*/
@SuppressWarnings("unchecked")
private Object readCollectionOrArray(TypeInformation<?> targetType, BasicDBList sourceValue) {
private Collection<?> readCollectionOrArray(TypeInformation<?> targetType, BasicDBList sourceValue, Object parent) {
Assert.notNull(targetType);
if (sourceValue.isEmpty()) {
return Collections.emptySet();
}
Class<?> collectionType = targetType.getType();
collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>() : CollectionFactory
.createCollection(collectionType, sourceValue.size());
TypeInformation<?> componentType = targetType.getComponentType();
for (int i = 0; i < sourceValue.size(); i++) {
Object dbObjItem = sourceValue.get(i);
if (dbObjItem instanceof DBRef) {
items.add(read(targetType.getComponentType(), ((DBRef) dbObjItem).fetch()));
items.add(read(componentType, ((DBRef) dbObjItem).fetch(), parent));
} else if (dbObjItem instanceof DBObject) {
items.add(read(targetType.getComponentType(), (DBObject) dbObjItem));
items.add(read(componentType, (DBObject) dbObjItem, parent));
} else {
TypeInformation<?> componentType = targetType.getComponentType();
items.add(getPotentiallyConvertedSimpleRead(dbObjItem, componentType == null ? null : componentType.getType()));
}
}
return getPotentiallyConvertedSimpleRead(items, targetType.getType());
return items;
}
/**
@@ -761,7 +753,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* @return
*/
@SuppressWarnings("unchecked")
protected Map<Object, Object> readMap(TypeInformation<?> type, DBObject dbObject) {
protected Map<Object, Object> readMap(TypeInformation<?> type, DBObject dbObject, Object parent) {
Assert.notNull(dbObject);
@@ -786,7 +778,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
TypeInformation<?> valueType = type.getMapValueType();
if (value instanceof DBObject) {
map.put(key, read(valueType, (DBObject) value));
map.put(key, read(valueType, (DBObject) value, parent));
} else {
Class<?> valueClass = valueType == null ? null : valueType.getType();
map.put(key, getPotentiallyConvertedSimpleRead(value, valueClass));
@@ -818,7 +810,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return null;
}
Class<?> target = conversions.getCustomWriteTarget(obj.getClass());
Class<?> target = conversions.getCustomWriteTarget(getClass());
if (target != null) {
return conversionService.convert(obj, target);
}
@@ -907,53 +899,53 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return dbObject;
}
private class DelegatingParameterValueProvider implements ParameterValueProvider {
private class MongoDbPropertyValueProvider implements PropertyValueProvider<MongoPersistentProperty> {
private final DBObject source;
private final ParameterValueProvider delegate;
private final MappedConstructor constructor;
private final SpELExpressionEvaluator evaluator;
private final Object parent;
/**
* {@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) {
public MongoDbPropertyValueProvider(DBObject source, SpELContext factory, Object parent) {
this(source, new DefaultSpELExpressionEvaluator(source, factory), parent);
}
public MongoDbPropertyValueProvider(DBObject source, DefaultSpELExpressionEvaluator evaluator, Object parent) {
Assert.notNull(constructor);
Assert.notNull(source);
Assert.notNull(delegate);
Assert.notNull(evaluator);
this.constructor = constructor;
this.source = source;
this.delegate = delegate;
this.evaluator = evaluator;
this.parent = parent;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
* @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@SuppressWarnings("unchecked")
public <T> T getParameterValue(Parameter<T> parameter) {
public <T> T getPropertyValue(MongoPersistentProperty property) {
MappedConstructor.MappedParameter mappedParameter = constructor.getFor(parameter);
Object value = mappedParameter.hasSpELExpression() ? delegate.getParameterValue(parameter) : source
.get(mappedParameter.getFieldName());
String expression = property.getSpelExpression();
Object value = expression != null ? evaluator.evaluate(expression) : source.get(property.getFieldName());
TypeInformation<?> type = mappedParameter.getPropertyTypeInformation();
if (value == null) {
return null;
}
if (value instanceof DBRef) {
return (T) read(type, ((DBRef) value).fetch());
TypeInformation<?> type = property.getTypeInformation();
Class<?> rawType = type.getType();
if (conversions.hasCustomReadTarget(value.getClass(), rawType)) {
return (T) conversionService.convert(value, rawType);
} else if (value instanceof DBRef) {
return (T) read(type, ((DBRef) value).fetch(), parent);
} else if (value instanceof BasicDBList) {
return (T) getPotentiallyConvertedSimpleRead(readCollectionOrArray(type, (BasicDBList) value), type.getType());
return (T) getPotentiallyConvertedSimpleRead(readCollectionOrArray(type, (BasicDBList) value, parent), rawType);
} else if (value instanceof DBObject) {
return (T) read(type, (DBObject) value);
return (T) read(type, (DBObject) value, parent);
} else {
return (T) getPotentiallyConvertedSimpleRead(value, type.getType());
return (T) getPotentiallyConvertedSimpleRead(value, rawType);
}
}
}

View File

@@ -17,12 +17,8 @@ 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;
@@ -123,28 +119,4 @@ 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

@@ -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.
@@ -16,8 +16,10 @@
package org.springframework.data.mongodb.core.convert;
import org.springframework.data.convert.EntityWriter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
/**
* A MongoWriter is responsible for converting an object of type T to the native MongoDB representation DBObject.
@@ -37,4 +39,14 @@ public interface MongoWriter<T> extends EntityWriter<T, DBObject> {
* @return
*/
Object convertToMongoType(Object obj);
/**
* Creates a {@link DBRef} to refer to the given object.
*
* @param object the object to create a {@link DBRef} to link to. The object's type has to carry an id attribute.
* @param referingProperty the client-side property referring to the object which might carry additional metadata for
* the {@link DBRef} object to create. Can be {@literal null}.
* @return will never be {@literal null}.
*/
DBRef toDBRef(Object object, MongoPersistentProperty referingProperty);
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList;
import java.util.Arrays;
@@ -25,7 +25,7 @@ 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.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.util.Assert;
@@ -73,6 +73,7 @@ public class QueryMapper {
for (String key : query.keySet()) {
MongoPersistentEntity<?> nestedEntity = getNestedEntity(entity, key);
String newKey = key;
Object value = query.get(key);
@@ -86,10 +87,8 @@ public class QueryMapper {
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, null);
value = getMappedObject((DBObject) value, nestedEntity);
}
} else {
value = convertId(value);
@@ -101,12 +100,14 @@ public class QueryMapper {
BasicBSONList newConditions = new BasicBSONList();
Iterator<?> iter = conditions.iterator();
while (iter.hasNext()) {
newConditions.add(getMappedObject((DBObject) iter.next(), entity));
newConditions.add(getMappedObject((DBObject) iter.next(), nestedEntity));
}
value = newConditions;
} else if (key.equals("$ne")) {
value = convertId(value);
}
newDbo.put(newKey, convertSimpleOrDBObject(value, null));
newDbo.put(newKey, convertSimpleOrDBObject(value, nestedEntity));
}
return newDbo;
@@ -141,11 +142,7 @@ public class QueryMapper {
*/
private boolean isIdKey(String key, MongoPersistentEntity<?> entity) {
if (entity == null) {
return false;
}
if (entity.getIdProperty() != null) {
if (null != entity && entity.getIdProperty() != null) {
MongoPersistentProperty idProperty = entity.getIdProperty();
return idProperty.getName().equals(key) || idProperty.getFieldName().equals(key);
}
@@ -153,6 +150,18 @@ public class QueryMapper {
return DEFAULT_ID_NAMES.contains(key);
}
private MongoPersistentEntity<?> getNestedEntity(MongoPersistentEntity<?> entity, String key) {
MongoPersistentProperty property = entity == null ? null : entity.getPersistentProperty(key);
if (property == null || !property.isEntity()) {
return null;
}
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context = converter.getMappingContext();
return context.getPersistentEntity(property);
}
/**
* Converts the given raw id value into either {@link ObjectId} or {@link String}.
*

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2011-2012 the original author or authors.
* 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
* 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,29 +24,14 @@ import java.lang.annotation.Target;
/**
* Mark a class to use compound indexes.
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@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;
@@ -55,18 +40,8 @@ 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

@@ -15,16 +15,16 @@
*/
package org.springframework.data.mongodb.core.index;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.data.mongodb.core.query.Order;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
public class IndexInfo {
private final Map<String, Order> fieldSpec;
private final List<IndexField> indexFields;
private final String name;
@@ -32,10 +32,8 @@ public class IndexInfo {
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) {
public IndexInfo(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;
@@ -43,15 +41,6 @@ public class IndexInfo {
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.
*
@@ -61,6 +50,24 @@ public class IndexInfo {
return this.indexFields;
}
/**
* Returns whether the index is covering exactly the fields given independently of the order.
*
* @param keys must not be {@literal null}.
* @return
*/
public boolean isIndexForFields(Collection<String> keys) {
Assert.notNull(keys);
List<String> indexKeys = new ArrayList<String>(indexFields.size());
for (IndexField field : indexFields) {
indexKeys.add(field.getKey());
}
return indexKeys.containsAll(keys);
}
public String getName() {
return name;
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2011-2012 the original author or authors.
* 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
* 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,19 +13,21 @@
* 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;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
@@ -37,16 +39,16 @@ import com.mongodb.DBObject;
import com.mongodb.util.JSON;
/**
* Component that inspects {@link MongoPersistentEntity} instances contained in the given {@link MongoMappingContext}
* for indexing metadata and ensures the indexes to be available.
* Component that inspects {@link BasicMongoPersistentEntity} instances contained in the given
* {@link MongoMappingContext} for indexing metadata and ensures the indexes to be available.
*
* @author Jon Brisbin
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Oliver Gierke
*/
public class MongoPersistentEntityIndexCreator implements
ApplicationListener<MappingContextEvent<MongoPersistentEntity<MongoPersistentProperty>, MongoPersistentProperty>> {
private static final Log log = LogFactory.getLog(MongoPersistentEntityIndexCreator.class);
private static final Logger log = LoggerFactory.getLogger(MongoPersistentEntityIndexCreator.class);
private final Map<Class<?>, Boolean> classesSeen = new ConcurrentHashMap<Class<?>, Boolean>();
private final MongoDbFactory mongoDbFactory;
@@ -95,12 +97,12 @@ public class MongoPersistentEntityIndexCreator implements
if (type.isAnnotationPresent(CompoundIndexes.class)) {
CompoundIndexes indexes = type.getAnnotation(CompoundIndexes.class);
for (CompoundIndex index : indexes.value()) {
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());
String indexColl = index.collection();
if ("".equals(indexColl)) {
indexColl = entity.getCollection();
}
ensureIndex(indexColl, index.name(), index.def(), index.direction(), index.unique(), index.dropDups(),
index.sparse());
if (log.isDebugEnabled()) {
log.debug("Created compound index " + index);
}
@@ -109,14 +111,10 @@ 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 {
@@ -128,17 +126,11 @@ public class MongoPersistentEntityIndexCreator implements
}
}
}
String collection = StringUtils.hasText(index.collection()) ? index.collection() : entity.getCollection();
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());
ensureIndex(collection, name, null, index.direction(), 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);
@@ -163,15 +155,21 @@ public class MongoPersistentEntityIndexCreator implements
}
}
protected void ensureIndex(String collection, String name, DBObject indexDefinition, boolean unique,
boolean dropDups, boolean sparse) {
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));
}
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(indexDefinition, opts);
mongoDbFactory.getDb().getCollection(collection).ensureIndex(defObj, opts);
}
}

View File

@@ -21,9 +21,9 @@ import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -39,7 +39,7 @@ import com.mongodb.DBObject;
public class BasicMongoPersistentProperty extends AnnotationBasedPersistentProperty<MongoPersistentProperty> implements
MongoPersistentProperty {
private static final Log LOG = LogFactory.getLog(BasicMongoPersistentProperty.class);
private static final Logger LOG = LoggerFactory.getLogger(BasicMongoPersistentProperty.class);
private static final String ID_FIELD_NAME = "_id";
private static final Set<Class<?>> SUPPORTED_ID_TYPES = new HashSet<Class<?>>();

View File

@@ -28,6 +28,7 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty {
private Boolean isIdProperty;
private Boolean isAssociation;
private String fieldName;
/**
@@ -57,6 +58,18 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty
return this.isIdProperty;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty#isAssociation()
*/
@Override
public boolean isAssociation() {
if (this.isAssociation == null) {
this.isAssociation = super.isAssociation();
}
return this.isAssociation;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty#getFieldName()

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright 2011-2012 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.
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.mapping;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -24,19 +24,21 @@ import java.lang.annotation.Target;
import org.springframework.data.annotation.Reference;
/**
* An annotation that indicates the annotated field is to be stored using a com.mongodb.DBRef
* An annotation that indicates the annotated field is to be stored using a {@link com.mongodb.DBRef}.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Jon Brisbin
* @authot Oliver Gierke
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@Reference
public @interface DBRef {
String collection() default "";
String id() default "";
/**
* The database the referred entity resides in.
*
* @return
*/
String db() default "";
}

View File

@@ -15,12 +15,13 @@
*/
package org.springframework.data.mongodb.core.mapping.event;
import com.mongodb.DBObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.core.GenericTypeResolver;
import com.mongodb.DBObject;
/**
* Base class to implement domain class specific {@link ApplicationListener}s.
*
@@ -29,7 +30,7 @@ import org.springframework.core.GenericTypeResolver;
*/
public abstract class AbstractMongoEventListener<E> implements ApplicationListener<MongoMappingEvent<?>> {
protected final Log LOG = LogFactory.getLog(getClass());
private static final Logger LOG = LoggerFactory.getLogger(AbstractMongoEventListener.class);
private final Class<?> domainClass;
/**

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,
@@ -15,22 +15,31 @@
*/
package org.springframework.data.mongodb.core.mapping.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.DBObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class LoggingEventListener extends AbstractMongoEventListener<Object> {
private Log log = LogFactory.getLog(getClass());
private static final Logger log = LoggerFactory.getLogger(LoggingEventListener.class);
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onBeforeConvert(java.lang.Object)
*/
@Override
public void onBeforeConvert(Object source) {
log.info("onBeforeConvert: " + source);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onBeforeSave(java.lang.Object, com.mongodb.DBObject)
*/
@Override
public void onBeforeSave(Object source, DBObject dbo) {
try {
@@ -39,19 +48,30 @@ public class LoggingEventListener extends AbstractMongoEventListener<Object> {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onAfterSave(java.lang.Object, com.mongodb.DBObject)
*/
@Override
public void onAfterSave(Object source, DBObject dbo) {
log.info("onAfterSave: " + source + ", " + dbo);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onAfterLoad(com.mongodb.DBObject)
*/
@Override
public void onAfterLoad(DBObject dbo) {
log.info("onAfterLoad: " + dbo);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onAfterConvert(com.mongodb.DBObject, java.lang.Object)
*/
@Override
public void onAfterConvert(DBObject dbo, Object source) {
log.info("onAfterConvert: " + dbo + ", " + source);
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.mapping.event;
import java.util.Set;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* javax.validation dependant entities validator. When it is registered as Spring component its automatically invoked
* before entities are saved in database.
*
* @author Maciej Walkowiak
*/
public class ValidatingMongoEventListener extends AbstractMongoEventListener<Object> {
private static final Logger LOG = LoggerFactory.getLogger(ValidatingMongoEventListener.class);
private final Validator validator;
/**
* Creates a new {@link ValidatingMongoEventListener} using the given {@link Validator}.
*
* @param validator must not be {@literal null}.
*/
public ValidatingMongoEventListener(Validator validator) {
Assert.notNull(validator);
this.validator = validator;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onBeforeSave(java.lang.Object, com.mongodb.DBObject)
*/
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void onBeforeSave(Object source, DBObject dbo) {
LOG.debug("Validating object: {}", source);
Set violations = validator.validate(source);
if (!violations.isEmpty()) {
LOG.info("During object: {} validation violations found: {}", source, violations);
throw new ConstraintViolationException(violations);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2012 the original author or authors.
* 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.
@@ -26,39 +26,33 @@ 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.
*
* @param <T> The class in which the results are mapped onto, accessible via an interator.
*/
public class MapReduceResults<T> implements Iterable<T> {
private final List<T> mappedResults;
private final DBObject rawResults;
private final String outputCollection;
private final MapReduceTiming mapReduceTiming;
private final MapReduceCounts mapReduceCounts;
/**
* 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}.
*/
private DBObject rawResults;
private MapReduceTiming mapReduceTiming;
private MapReduceCounts mapReduceCounts;
private String outputCollection;
public MapReduceResults(List<T> mappedResults, DBObject rawResults) {
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
this.mappedResults = mappedResults;
this.rawResults = rawResults;
this.mapReduceTiming = parseTiming(rawResults);
this.mapReduceCounts = parseCounts(rawResults);
this.outputCollection = parseOutputCollection(rawResults);
parseTiming(rawResults);
parseCounts(rawResults);
if (rawResults.get("result") != null) {
this.outputCollection = (String) rawResults.get("result");
}
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<T> iterator() {
return mappedResults.iterator();
}
@@ -79,71 +73,28 @@ public class MapReduceResults<T> implements Iterable<T> {
return rawResults;
}
private MapReduceTiming parseTiming(DBObject rawResults) {
protected void parseTiming(DBObject rawResults) {
DBObject timing = (DBObject) rawResults.get("timing");
if (timing == null) {
return new MapReduceTiming(-1, -1, -1);
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.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);
}
/**
* 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) {
protected void parseCounts(DBObject rawResults) {
DBObject counts = (DBObject) rawResults.get("counts");
if (counts == null) {
return new MapReduceCounts(-1, -1, -1);
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.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

@@ -15,8 +15,6 @@
*/
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;
@@ -31,7 +29,6 @@ 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.ObjectUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -432,9 +429,11 @@ public class Criteria implements CriteriaDefinition {
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.CriteriaDefinition#getCriteriaObject()
*/
* (non-Javadoc)
*
* @see org.springframework.datastore.document.mongodb.query.Criteria#
* getCriteriaObject(java.lang.String)
*/
public DBObject getCriteriaObject() {
if (this.criteriaChain.size() == 1) {
return criteriaChain.get(0).getSingleCriteriaObject();
@@ -497,82 +496,4 @@ public class Criteria implements CriteriaDefinition {
}
}
/*
* (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

@@ -15,9 +15,6 @@
*/
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;
@@ -143,60 +140,4 @@ public class Query {
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

@@ -1,110 +0,0 @@
/*
* 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

@@ -0,0 +1,106 @@
/*
* 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.gridfs;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.util.Assert;
/**
* Value object to abstract Ant paths.
*
* @author Oliver Gierke
*/
class AntPath {
private static final String PREFIX_DELIMITER = ":";
private static final Pattern WILDCARD_PATTERN = Pattern.compile("\\?|\\*\\*|\\*");
private final String path;
/**
* Creates a new {@link AntPath} from the given path.
*
* @param path must not be {@literal null}.
*/
public AntPath(String path) {
Assert.notNull(path);
this.path = path;
}
/**
* Returns whether the path is a pattern.
*
* @return
*/
public boolean isPattern() {
String path = stripPrefix(this.path);
return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
}
private static String stripPrefix(String path) {
int index = path.indexOf(PREFIX_DELIMITER);
return (index > -1 ? path.substring(index + 1) : path);
}
/**
* Returns the regular expression equivalent of this Ant path.
*
* @return
*/
public String toRegex() {
StringBuilder patternBuilder = new StringBuilder();
Matcher m = WILDCARD_PATTERN.matcher(path);
int end = 0;
while (m.find()) {
patternBuilder.append(quote(path, end, m.start()));
String match = m.group();
if ("?".equals(match)) {
patternBuilder.append('.');
} else if ("**".equals(match)) {
patternBuilder.append(".*");
} else if ("*".equals(match)) {
patternBuilder.append("[^/]*");
}
end = m.end();
}
patternBuilder.append(quote(path, end, path.length()));
return patternBuilder.toString();
}
private static String quote(String s, int start, int end) {
if (start == end) {
return "";
}
return Pattern.quote(s.substring(start, end));
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return path;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.gridfs;
import org.springframework.data.mongodb.core.query.Criteria;
/**
* GridFs-specific helper class to define {@link Criteria}s.
*
* @author Oliver Gierke
*/
public class GridFsCriteria extends Criteria {
/**
* Creates a new {@link GridFsCriteria} for the given key.
*
* @param key
*/
public GridFsCriteria(String key) {
super(key);
}
/**
* Creates a {@link GridFsCriteria} for restrictions on the file's metadata.
*
* @return
*/
public static GridFsCriteria whereMetaData() {
return new GridFsCriteria("metadata");
}
/**
* Creates a {@link GridFsCriteria} for restrictions on a single file's metadata item.
*
* @param metadataKey
* @return
*/
public static GridFsCriteria whereMetaData(String metadataKey) {
String extension = metadataKey == null ? "" : "." + metadataKey;
return new GridFsCriteria(String.format("metadata%s", extension));
}
/**
* Creates a {@link GridFsCriteria} for restrictions on the file's name.
*
* @return
*/
public static GridFsCriteria whereFilename() {
return new GridFsCriteria("filename");
}
/**
* Creates a {@link GridFsCriteria} for restrictions on the file's content type.
*
* @return
*/
public static GridFsCriteria whereContentType() {
return new GridFsCriteria("contentType");
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.gridfs;
import java.io.InputStream;
import java.util.List;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.DBObject;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSFile;
/**
* Collection of operations to store and read files from MongoDB GridFS.
*
* @author Oliver Gierke
*/
public interface GridFsOperations extends ResourcePatternResolver {
/**
* Stores the given content into a file with the given name.
*
* @param content must not be {@literal null}.
* @param filename must not be {@literal null} or empty.
* @return the {@link GridFSFile} just created
*/
GridFSFile store(InputStream content, String filename);
/**
* Stores the given content into a file with the given name using the given metadata. The metadata object will be
* marshalled before writing.
*
* @param content must not be {@literal null}.
* @param filename must not be {@literal null} or empty.
* @param metadata
* @return the {@link GridFSFile} just created
*/
GridFSFile store(InputStream content, String filename, Object metadata);
/**
* Stores the given content into a file with the given name using the given metadata.
*
* @param content must not be {@literal null}.
* @param filename must not be {@literal null} or empty.
* @param metadata must not be {@literal null}.
* @return the {@link GridFSFile} just created
*/
GridFSFile store(InputStream content, String filename, DBObject metadata);
/**
* Returns all files matching the given query.
*
* @param query
* @return
*/
List<GridFSDBFile> find(Query query);
/**
* Returns a single file matching the given query or {@literal null} in case no file matches.
*
* @param query
* @return
*/
GridFSDBFile findOne(Query query);
/**
* Deletes all files matching the given {@link Query}.
*
* @param query
*/
void delete(Query query);
/**
* Returns all {@link GridFsResource} with the given file name.
*
* @param filename
* @return
* @see ResourcePatternResolver#getResource(String)
*/
GridFsResource getResource(String filename);
/**
* Returns all {@link GridFsResource}s matching the given file name pattern.
*
* @param filenamePattern
* @return
* @see ResourcePatternResolver#getResources(String)
*/
GridFsResource[] getResources(String filenamePattern);
}

View File

@@ -0,0 +1,88 @@
/*
* 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.gridfs;
import java.io.IOException;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import com.mongodb.gridfs.GridFSDBFile;
/**
* {@link GridFSDBFile} based {@link Resource} implementation.
*
* @author Oliver Gierke
*/
public class GridFsResource extends InputStreamResource {
private final GridFSDBFile file;
/**
* Creates a new {@link GridFsResource} from the given {@link GridFSDBFile}.
*
* @param file must not be {@literal null}.
*/
public GridFsResource(GridFSDBFile file) {
super(file.getInputStream());
this.file = file;
}
/*
* (non-Javadoc)
* @see org.springframework.core.io.AbstractResource#contentLength()
*/
@Override
public long contentLength() throws IOException {
return file.getLength();
}
/*
* (non-Javadoc)
* @see org.springframework.core.io.AbstractResource#getFilename()
*/
@Override
public String getFilename() throws IllegalStateException {
return file.getFilename();
}
/*
* (non-Javadoc)
* @see org.springframework.core.io.AbstractResource#lastModified()
*/
@Override
public long lastModified() throws IOException {
return file.getUploadDate().getTime();
}
/**
* Returns the {@link Resource}'s id.
*
* @return
*/
public Object getId() {
return file.getId();
}
/**
* Returns the {@link Resource}'s content type.
*
* @return
*/
public String getContentType() {
return file.getContentType();
}
}

View File

@@ -0,0 +1,194 @@
/*
* 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.gridfs;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.gridfs.GridFsCriteria.*;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSFile;
import com.mongodb.gridfs.GridFSInputFile;
/**
* {@link GridFsOperations} implementation to store content into MongoDB GridFS.
*
* @author Oliver Gierke
*/
public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver {
private final MongoDbFactory dbFactory;
private final String bucket;
private final MongoConverter converter;
private final QueryMapper queryMapper;
/**
* Creates a new {@link GridFsTemplate} using the given {@link MongoDbFactory} and {@link MongoConverter}.
*
* @param dbFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
*/
public GridFsTemplate(MongoDbFactory dbFactory, MongoConverter converter) {
this(dbFactory, converter, null);
}
/**
* Creates a new {@link GridFsTemplate} using the given {@link MongoDbFactory} and {@link MongoConverter}.
*
* @param dbFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param bucket
*/
public GridFsTemplate(MongoDbFactory dbFactory, MongoConverter converter, String bucket) {
Assert.notNull(dbFactory);
Assert.notNull(converter);
this.dbFactory = dbFactory;
this.converter = converter;
this.bucket = bucket;
this.queryMapper = new QueryMapper(converter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.gridfs.GridFsOperations#store(java.io.InputStream, java.lang.String)
*/
public GridFSFile store(InputStream content, String filename) {
return store(content, filename, (Object) null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.gridfs.GridFsOperations#store(java.io.InputStream, java.lang.String, java.lang.Object)
*/
public GridFSFile store(InputStream content, String filename, Object metadata) {
DBObject dbObject = new BasicDBObject();
converter.write(metadata, dbObject);
return store(content, filename, dbObject);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.gridfs.GridFsOperations#store(java.io.InputStream, java.lang.String, com.mongodb.DBObject)
*/
public GridFSFile store(InputStream content, String filename, DBObject metadata) {
Assert.notNull(content);
Assert.hasText(filename);
Assert.notNull(metadata);
GridFSInputFile file = getGridFs().createFile(content);
file.setFilename(filename);
file.setMetaData(metadata);
file.save();
return file;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.gridfs.GridFsOperations#find(com.mongodb.DBObject)
*/
public List<GridFSDBFile> find(Query query) {
return getGridFs().find(getMappedQuery(query));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.gridfs.GridFsOperations#findOne(com.mongodb.DBObject)
*/
public GridFSDBFile findOne(Query query) {
return getGridFs().findOne(getMappedQuery(query));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.gridfs.GridFsOperations#delete(org.springframework.data.mongodb.core.query.Query)
*/
public void delete(Query query) {
getGridFs().remove(getMappedQuery(query));
}
/*
* (non-Javadoc)
* @see org.springframework.core.io.ResourceLoader#getClassLoader()
*/
public ClassLoader getClassLoader() {
return dbFactory.getClass().getClassLoader();
}
/*
* (non-Javadoc)
* @see org.springframework.core.io.ResourceLoader#getResource(java.lang.String)
*/
public GridFsResource getResource(String location) {
return new GridFsResource(findOne(query(whereFilename().is(location))));
}
/*
* (non-Javadoc)
* @see org.springframework.core.io.support.ResourcePatternResolver#getResources(java.lang.String)
*/
public GridFsResource[] getResources(String locationPattern) {
if (!StringUtils.hasText(locationPattern)) {
return new GridFsResource[0];
}
AntPath path = new AntPath(locationPattern);
if (path.isPattern()) {
List<GridFSDBFile> files = find(query(whereFilename().regex(path.toRegex())));
List<GridFsResource> resources = new ArrayList<GridFsResource>(files.size());
for (GridFSDBFile file : files) {
resources.add(new GridFsResource(file));
}
return resources.toArray(new GridFsResource[resources.size()]);
}
return new GridFsResource[] { getResource(locationPattern) };
}
private DBObject getMappedQuery(Query query) {
return query == null ? null : queryMapper.getMappedObject(query.getQueryObject(), null);
}
private GridFS getGridFs() {
DB db = dbFactory.getDb();
return bucket == null ? new GridFS(db) : new GridFS(db, bucket);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-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.
@@ -15,13 +15,15 @@
*/
package org.springframework.data.mongodb.monitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.mongodb.core.MongoDbUtils;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.mongodb.core.MongoDbUtils;
/**
* Base class to encapsulate common configuration settings when connecting to a database
@@ -30,7 +32,7 @@ import org.springframework.data.mongodb.core.MongoDbUtils;
*/
public abstract class AbstractMonitor {
private final Log logger = LogFactory.getLog(getClass());
private final Logger logger = LoggerFactory.getLogger(getClass());
protected Mongo mongo;
private String username;
@@ -51,7 +53,6 @@ public abstract class AbstractMonitor {
* @param password The password to use
*/
public void setPassword(String password) {
this.password = password;
}
@@ -65,6 +66,6 @@ public abstract class AbstractMonitor {
}
public DB getDb(String databaseName) {
return MongoDbUtils.getDB(mongo, databaseName, username, password == null ? null : password.toCharArray());
return MongoDbUtils.getDB(mongo, databaseName, new UserCredentials(username, password));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -32,22 +32,19 @@ public interface MongoRepository<T, ID extends Serializable> extends PagingAndSo
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.Repository#save(java.lang.Iterable)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
*/
List<T> save(Iterable<? extends T> entites);
<S extends T> List<S> save(Iterable<S> entites);
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.Repository#findAll()
*/
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findAll()
*/
List<T> findAll();
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
*/
* (non-Javadoc)
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
*/
List<T> findAll(Sort sort);
}

View File

@@ -0,0 +1,76 @@
/*
* 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.repository.cdi;
import java.lang.annotation.Annotation;
import java.util.Set;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory;
import org.springframework.data.repository.cdi.CdiRepositoryBean;
import org.springframework.util.Assert;
/**
* {@link CdiRepositoryBean} to create Mongo repository instances.
*
* @author Oliver Gierke
*/
public class MongoRepositoryBean<T> extends CdiRepositoryBean<T> {
private final Bean<MongoOperations> operations;
/**
* Creates a new {@link MongoRepositoryBean}.
*
* @param operations must not be {@literal null}.
* @param qualifiers must not be {@literal null}.
* @param repositoryType must not be {@literal null}.
* @param beanManager must not be {@literal null}.
*/
public MongoRepositoryBean(Bean<MongoOperations> operations, Set<Annotation> qualifiers, Class<T> repositoryType,
BeanManager beanManager) {
super(qualifiers, repositoryType, beanManager);
Assert.notNull(operations);
this.operations = operations;
}
/*
* (non-Javadoc)
* @see javax.enterprise.inject.spi.Bean#getScope()
*/
public Class<? extends Annotation> getScope() {
return operations.getScope();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(javax.enterprise.context.spi.CreationalContext, java.lang.Class)
*/
@Override
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {
MongoOperations mongoOperations = getDependencyInstance(operations, MongoOperations.class);
MongoRepositoryFactory factory = new MongoRepositoryFactory(mongoOperations);
return factory.getRepository(repositoryType);
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.repository.cdi;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.UnsatisfiedResolutionException;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.ProcessBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.repository.cdi.CdiRepositoryExtensionSupport;
/**
* CDI extension to export Mongo repositories.
*
* @author Oliver Gierke
*/
public class MongoRepositoryExtension extends CdiRepositoryExtensionSupport {
private static final Logger LOG = LoggerFactory.getLogger(MongoRepositoryExtension.class);
private final Map<Set<Annotation>, Bean<MongoOperations>> mongoOperations = new HashMap<Set<Annotation>, Bean<MongoOperations>>();
public MongoRepositoryExtension() {
LOG.info("Activating CDI extension for Spring Data MongoDB repositories.");
}
@SuppressWarnings("unchecked")
<X> void processBean(@Observes ProcessBean<X> processBean) {
Bean<X> bean = processBean.getBean();
for (Type type : bean.getTypes()) {
if (type instanceof Class<?> && MongoOperations.class.isAssignableFrom((Class<?>) type)) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Discovered %s with qualifiers %s.", MongoOperations.class.getName(),
bean.getQualifiers()));
}
// Store the EntityManager bean using its qualifiers.
mongoOperations.put(new HashSet<Annotation>(bean.getQualifiers()), (Bean<MongoOperations>) bean);
}
}
}
void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
for (Entry<Class<?>, Set<Annotation>> entry : getRepositoryTypes()) {
Class<?> repositoryType = entry.getKey();
Set<Annotation> qualifiers = entry.getValue();
// Create the bean representing the repository.
Bean<?> repositoryBean = createRepositoryBean(repositoryType, qualifiers, beanManager);
if (LOG.isInfoEnabled()) {
LOG.info(String.format("Registering bean for %s with qualifiers %s.", repositoryType.getName(), qualifiers));
}
// Register the bean to the container.
afterBeanDiscovery.addBean(repositoryBean);
}
}
/**
* Creates a {@link Bean}.
*
* @param <T> The type of the repository.
* @param repositoryType The class representing the repository.
* @param beanManager The BeanManager instance.
* @return The bean.
*/
private <T> Bean<T> createRepositoryBean(Class<T> repositoryType, Set<Annotation> qualifiers, BeanManager beanManager) {
// Determine the MongoOperations bean which matches the qualifiers of the repository.
Bean<MongoOperations> mongoOperations = this.mongoOperations.get(qualifiers);
if (mongoOperations == null) {
throw new UnsatisfiedResolutionException(String.format("Unable to resolve a bean for '%s' with qualifiers %s.",
MongoOperations.class.getName(), qualifiers));
}
// Construct and return the repository bean.
return new MongoRepositoryBean<T>(mongoOperations, qualifiers, repositoryType, beanManager);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2012 the original author or authors.
* 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.
@@ -85,7 +85,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
} else if (method.isGeoNearQuery()) {
return new GeoNearExecution(accessor).execute(query);
} else if (method.isCollectionQuery()) {
return new CollectionExecution(accessor.getPageable()).execute(query);
return new CollectionExecution().execute(query);
} else if (method.isPageQuery()) {
return new PagedExecution(accessor.getPageable()).execute(query);
} else {
@@ -133,23 +133,12 @@ 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.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);
}
}

View File

@@ -22,6 +22,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.util.Assert;
@@ -110,6 +111,14 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return writer.convertToMongoType(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#hasBindableNullValue()
*/
public boolean hasBindableNullValue() {
return delegate.hasBindableNullValue();
}
/**
* Custom {@link Iterator} to convert items before returning them.
*
@@ -129,37 +138,33 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#hasNext()
*/
* (non-Javadoc)
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return delegate.hasNext();
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#next()
*/
* (non-Javadoc)
* @see java.util.Iterator#next()
*/
public Object next() {
return delegate.next();
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.ConvertingParameterAccessor.PotentiallConvertingIterator#nextConverted()
*/
public Object nextConverted() {
return getConvertedValue(next());
public Object nextConverted(MongoPersistentProperty property) {
return property.isAssociation() ? writer.toDBRef(next(), property) : getConvertedValue(next());
}
/*
* (non-Javadoc)
*
* @see java.util.Iterator#remove()
*/
* (non-Javadoc)
* @see java.util.Iterator#remove()
*/
public void remove() {
delegate.remove();
}
@@ -177,6 +182,6 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
*
* @return
*/
Object nextConverted();
Object nextConverted(MongoPersistentProperty property);
}
}

View File

@@ -20,8 +20,8 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
@@ -46,7 +46,7 @@ import org.springframework.util.Assert;
*/
class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class);
private static final Logger LOG = LoggerFactory.getLogger(MongoQueryCreator.class);
private final MongoParameterAccessor accessor;
private final boolean isGeoNearQuery;
@@ -98,7 +98,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
}
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
Criteria criteria = from(part.getType(),
MongoPersistentProperty property = path.getLeafProperty();
Criteria criteria = from(part.getType(), property,
where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
(PotentiallyConvertingIterator) iterator);
@@ -117,11 +118,13 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
}
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
MongoPersistentProperty property = path.getLeafProperty();
return new Criteria().andOperator(
base,
from(part.getType(), where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
(PotentiallyConvertingIterator) iterator));
Criteria criteria = from(part.getType(), property,
where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
(PotentiallyConvertingIterator) iterator);
return criteria.andOperator(criteria);
}
/*
@@ -156,44 +159,51 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
QueryUtils.applySorting(query, sort);
if (LOG.isDebugEnabled()) {
LOG.debug("Created query " + query);
LOG.debug("Created query " + query.getQueryObject());
}
return query;
}
/**
* Populates the given {@link CriteriaDefinition} depending on the {@link Type} given.
* Populates the given {@link CriteriaDefinition} depending on the {@link Part} given.
*
* @param type
* @param part
* @param property
* @param criteria
* @param parameters
* @return
*/
private Criteria from(Type type, Criteria criteria, PotentiallyConvertingIterator parameters) {
private Criteria from(Type type, MongoPersistentProperty property, Criteria criteria,
PotentiallyConvertingIterator parameters) {
switch (type) {
case AFTER:
case GREATER_THAN:
return criteria.gt(parameters.nextConverted());
return criteria.gt(parameters.nextConverted(property));
case GREATER_THAN_EQUAL:
return criteria.gte(parameters.nextConverted());
return criteria.gte(parameters.nextConverted(property));
case BEFORE:
case LESS_THAN:
return criteria.lt(parameters.nextConverted());
return criteria.lt(parameters.nextConverted(property));
case LESS_THAN_EQUAL:
return criteria.lte(parameters.nextConverted());
return criteria.lte(parameters.nextConverted(property));
case BETWEEN:
return criteria.gt(parameters.nextConverted()).lt(parameters.nextConverted());
return criteria.gt(parameters.nextConverted(property)).lt(parameters.nextConverted(property));
case IS_NOT_NULL:
return criteria.ne(null);
case IS_NULL:
return criteria.is(null);
case NOT_IN:
return criteria.nin(nextAsArray(parameters));
return criteria.nin(nextAsArray(parameters, property));
case IN:
return criteria.in(nextAsArray(parameters));
return criteria.in(nextAsArray(parameters, property));
case LIKE:
case STARTING_WITH:
case ENDING_WITH:
case CONTAINING:
String value = parameters.next().toString();
return criteria.regex(toLikeRegex(value));
return criteria.regex(toLikeRegex(value, type));
case REGEX:
return criteria.regex(parameters.next().toString());
case EXISTS:
@@ -224,9 +234,9 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
Object parameter = parameters.next();
return criteria.within((Shape) parameter);
case SIMPLE_PROPERTY:
return criteria.is(parameters.nextConverted());
return criteria.is(parameters.nextConverted(property));
case NEGATING_SIMPLE_PROPERTY:
return criteria.not().is(parameters.nextConverted());
return criteria.not().is(parameters.nextConverted(property));
}
throw new IllegalArgumentException("Unsupported keyword!");
@@ -252,8 +262,8 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
parameter.getClass()));
}
private Object[] nextAsArray(PotentiallyConvertingIterator iterator) {
Object next = iterator.nextConverted();
private Object[] nextAsArray(PotentiallyConvertingIterator iterator, MongoPersistentProperty property) {
Object next = iterator.nextConverted(property);
if (next instanceof Collection) {
return ((Collection<?>) next).toArray();
@@ -264,8 +274,20 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
return new Object[] { next };
}
private String toLikeRegex(String source) {
private String toLikeRegex(String source, Type type) {
switch (type) {
case STARTING_WITH:
source = source + "*";
break;
case ENDING_WITH:
source = "*" + source;
break;
case CONTAINING:
source = "*" + source + "*";
break;
}
return source.replaceAll("\\*", ".*");
}
}
}

View File

@@ -18,8 +18,8 @@ package org.springframework.data.mongodb.repository.query;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Query;
@@ -34,7 +34,7 @@ import com.mongodb.util.JSON;
public class StringBasedMongoQuery extends AbstractMongoQuery {
private static final Pattern PLACEHOLDER = Pattern.compile("\\?(\\d+)");
private static final Log LOG = LogFactory.getLog(StringBasedMongoQuery.class);
private static final Logger LOG = LoggerFactory.getLogger(StringBasedMongoQuery.class);
private final String query;
private final String fieldSpec;

View File

@@ -19,8 +19,8 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.index.Index;
@@ -43,7 +43,7 @@ import org.springframework.util.Assert;
class IndexEnsuringQueryCreationListener implements QueryCreationListener<PartTreeMongoQuery> {
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 static final Logger LOG = LoggerFactory.getLogger(IndexEnsuringQueryCreationListener.class);
private final MongoOperations operations;

View File

@@ -81,7 +81,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
protected Object getTargetRepository(RepositoryMetadata metadata) {
Class<?> repositoryInterface = metadata.getRepositoryInterface();
MongoEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainClass());
MongoEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
if (isQueryDslRepository(repositoryInterface)) {
return new QueryDslMongoRepository(entityInformation, mongoOperations);

View File

@@ -19,7 +19,6 @@ 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;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
@@ -28,7 +27,6 @@ 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;
@@ -39,7 +37,6 @@ 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;
import com.mysema.query.mongodb.MongodbSerializer;
@@ -165,13 +162,9 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
*/
private MongodbQuery<T> createQueryFor(Predicate predicate) {
DBCollection collection = getMongoOperations().getCollection(getEntityInformation().getCollectionName());
MongodbQuery<T> query = new MongodbQuery<T>(collection, new Transformer<DBObject, T>() {
public T transform(DBObject input) {
Class<T> type = getEntityInformation().getJavaType();
return getMongoOperations().getConverter().read(type, input);
}
}, serializer);
Class<T> domainType = getEntityInformation().getJavaType();
MongodbQuery<T> query = new SpringDataMongodbQuery<T>(getMongoOperations(), serializer, domainType);
return query.where(predicate);
}
@@ -236,7 +229,6 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
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}.
@@ -246,7 +238,6 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
public SpringDataMongodbSerializer(MongoConverter converter) {
this.mappingContext = converter.getMappingContext();
this.converter = converter;
this.mapper = new QueryMapper(converter);
}
@Override
@@ -261,10 +252,6 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
@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

@@ -15,14 +15,11 @@
*/
package org.springframework.data.mongodb.repository.support;
import org.apache.commons.collections15.Transformer;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.util.Assert;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mysema.query.mongodb.MongodbQuery;
import com.mysema.query.mongodb.MongodbSerializer;
import com.mysema.query.types.EntityPath;
@@ -75,11 +72,6 @@ public abstract class QuerydslRepositorySupport {
Assert.notNull(path);
Assert.hasText(collection);
DBCollection dbCollection = template.getCollection(collection);
return new MongodbQuery<T>(dbCollection, new Transformer<DBObject, T>() {
public T transform(DBObject input) {
return template.getConverter().read(path.getType(), input);
}
}, serializer);
return new SpringDataMongodbQuery<T>(template, serializer, path.getType(), collection);
}
}

View File

@@ -20,7 +20,9 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -30,9 +32,9 @@ import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.mongodb.repository.query.QueryUtils;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.util.Assert;
/**
@@ -40,7 +42,7 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
*/
public class SimpleMongoRepository<T, ID extends Serializable> implements PagingAndSortingRepository<T, ID> {
public class SimpleMongoRepository<T, ID extends Serializable> implements MongoRepository<T, ID> {
private final MongoOperations mongoOperations;
private final MongoEntityInformation<T, ID> entityInformation;
@@ -63,7 +65,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
*/
public T save(T entity) {
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null!");
@@ -75,13 +77,13 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
*/
public List<T> save(Iterable<? extends T> entities) {
public <S extends T> List<S> save(Iterable<S> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
List<T> result = new ArrayList<T>();
List<S> result = new ArrayList<S>();
for (T entity : entities) {
for (S entity : entities) {
save(entity);
result.add(entity);
}
@@ -171,10 +173,23 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
* @see org.springframework.data.repository.CrudRepository#findAll()
*/
public List<T> findAll() {
return findAll(new Query());
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
*/
public Iterable<T> findAll(Iterable<ID> ids) {
Set<ID> parameters = new HashSet<ID>();
for (ID id : ids) {
parameters.add(id);
}
return findAll(new Query(new Criteria(entityInformation.getIdAttribute()).in(parameters)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable)

View File

@@ -0,0 +1,75 @@
/*
* 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.repository.support;
import org.springframework.data.mongodb.core.MongoOperations;
import com.google.common.base.Function;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mysema.query.mongodb.MongodbQuery;
import com.mysema.query.mongodb.MongodbSerializer;
/**
* Spring Data specfic {@link MongodbQuery} implementation.
*
* @author Oliver Gierke
*/
class SpringDataMongodbQuery<T> extends MongodbQuery<T> {
private final MongoOperations operations;
/**
* Creates a new {@link SpringDataMongodbQuery}.
*
* @param operations must not be {@literal null}.
* @param serializer must not be {@literal null}.
* @param type must not be {@literal null}.
*/
public SpringDataMongodbQuery(final MongoOperations operations, final MongodbSerializer serializer,
final Class<? extends T> type) {
this(operations, serializer, type, operations.getCollectionName(type));
}
/**
* Creates a new {@link SpringDataMongodbQuery} to query the given collection.
*
* @param operations must not be {@literal null}.
* @param serializer must not be {@literal null}.
* @param type must not be {@literal null}.
* @param collectionName must not be {@literal null} or empty.
*/
public SpringDataMongodbQuery(final MongoOperations operations, final MongodbSerializer serializer,
final Class<? extends T> type, String collectionName) {
super(operations.getCollection(collectionName), new Function<DBObject, T>() {
public T apply(DBObject input) {
return operations.getConverter().read(type, input);
}
}, serializer);
this.operations = operations;
}
/*
* (non-Javadoc)
* @see com.mysema.query.mongodb.MongodbQuery#getCollection(java.lang.Class)
*/
@Override
protected DBCollection getCollection(Class<?> type) {
return operations.getCollection(operations.getCollectionName(type));
}
}

View File

@@ -0,0 +1 @@
org.springframework.data.mongodb.repository.cdi.MongoRepositoryExtension

View File

@@ -1,2 +1,3 @@
http\://www.springframework.org/schema/data/mongo/spring-mongo-1.1.xsd=org/springframework/data/mongodb/config/spring-mongo-1.1.xsd
http\://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd=org/springframework/data/mongodb/config/spring-mongo-1.0.xsd
http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.0.xsd
http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-1.1.xsd

View File

@@ -14,7 +14,7 @@
<xsd:import namespace="http://www.springframework.org/schema/context"
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-1.0.xsd" />
schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository.xsd" />
<xsd:element name="mongo" type="mongoType">
<xsd:annotation>
@@ -44,9 +44,8 @@ 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><![CDATA[
The reference to a Mongo instance. If not configured a default com.mongodb.Mongo instance will be created.
]]>
<xsd:documentation>
The reference to a Mongo. Will default to 'mongo'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>

View File

@@ -0,0 +1,479 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/mongo"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
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">
<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" />
<xsd:import namespace="http://www.springframework.org/schema/data/repository"
schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository.xsd" />
<xsd:element name="mongo" type="mongoType">
<xsd:annotation>
<xsd:documentation source="org.springframework.data.mongodb.core.core.MongoFactoryBean"><![CDATA[
Defines a Mongo instance used for accessing MongoDB'.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.mongodb.Mongo"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="db-factory">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a MongoDbFactory for connecting to a specific database
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the mongo definition (by default "mongoDbFactory").]]></xsd:documentation>
</xsd:annotation>
</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>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="dbname" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the database to connect to. Default is 'db'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="port" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The port to connect to MongoDB server. Default is 27017
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="host" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The host to connect to a MongoDB server. Default is localhost
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="username" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The username to use when connecting to a MongoDB server.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="password" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The password to use when connecting to a MongoDB server.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="uri" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The Mongo URI string.]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<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>
<xsd:complexType name="mongo-repository">
<xsd:complexContent>
<xsd:extension base="repository:repository">
<xsd:attributeGroup ref="mongo-repository-attributes"/>
<xsd:attributeGroup ref="repository:repository-attributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:attributeGroup name="mongo-repository-attributes">
<xsd:attribute name="mongo-template-ref" type="mongoTemplateRef" default="mongoTemplate">
<xsd:annotation>
<xsd:documentation>
The reference to a MongoTemplate. Will default to 'mongoTemplate'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="create-query-indexes" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation>
Enables creation of indexes for queries that get derived from the method name
and thus reference domain class properties. Defaults to false.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:element name="repositories">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="repository:repositories">
<xsd:sequence>
<xsd:element name="repository" minOccurs="0" maxOccurs="unbounded" type="mongo-repository"/>
</xsd:sequence>
<xsd:attributeGroup ref="mongo-repository-attributes"/>
<xsd:attributeGroup ref="repository:repository-attributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="mapping-converter">
<xsd:annotation>
<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>
<xsd:element name="custom-converters" minOccurs="0">
<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]]>
</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>
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the MappingMongoConverter instance (by default "mappingConverter").]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="base-package" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The base package in which to scan for entities annotated with @Document
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="db-factory-ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
The reference to a DbFactory.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="org.springframework.data.mongodb.MongoDbFactory" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</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>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mapping-context-ref" type="mappingContextRef" use="optional">
<xsd:annotation>
<xsd:documentation source="org.springframework.data.mapping.model.MappingContext">
The reference to a MappingContext. Will default to 'mappingContext'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mongo-template-ref" type="mongoTemplateRef" use="optional">
<xsd:annotation>
<xsd:documentation source="org.springframework.data.mongodb.core.core.MongoTemplate">
The reference to a MongoTemplate. Will default to 'mongoTemplate'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="disable-validation" use="optional">
<xsd:annotation>
<xsd:documentation source="org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener">
Disables JSR-303 validation on MongoDB documents before they are saved. By default it is set to false.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="jmx">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a JMX Model MBeans for monitoring a MongoDB server'.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="mongo-ref" type="mongoRef" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the Mongo object that determines what server to monitor. (by default "mongo").]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:simpleType name="mappingContextRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="org.springframework.data.mapping.model.MappingContext"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:simpleType name="mongoTemplateRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="org.springframework.data.mongodb.core.core.MongoTemplate"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<xsd:simpleType name="mongoRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="org.springframework.data.mongodb.core.core.MongoFactoryBean"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<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>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="NONE" />
<xsd:enumeration value="NORMAL" />
<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">
<xsd:annotation>
<xsd:documentation><![CDATA[
The Mongo driver options
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.mongodb.MongoOptions"/>
</tool:annotation>
</xsd:appinfo>
</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[
The name of the mongo definition (by default "mongo").]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="port" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The port to connect to MongoDB server. Default is 27017
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="host" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The host to connect to a MongoDB server. Default is localhost
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="replica-set" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The comma delimited list of host:port entries to use for replica set/pairs.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="optionsType">
<xsd:attribute name="connections-per-host" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of connections allowed per host. Will block if run out. Default is 10. System property MONGO.POOLSIZE can override
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="threads-allowed-to-block-for-connection-multiplier" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The multiplier for connectionsPerHost for # of threads that can block. Default is 5.
If connectionsPerHost is 10, and threadsAllowedToBlockForConnectionMultiplier is 5,
then 50 threads can block more than that and an exception will be thrown.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-wait-time" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The max wait time of a blocking thread for a connection. Default is 12000 ms (2 minutes)
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="connect-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The connect timeout in milliseconds. 0 is default and infinite.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="socket-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The socket timeout. 0 is default and infinite.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="socket-keep-alive" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The keep alive flag, controls whether or not to have socket keep alive timeout. Defaults to false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-connect-retry" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
This controls whether or not on a connect, the system retries automatically. Default is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-auto-connect-retry-time" type="xsd:long">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maximum amount of time in millisecons to spend retrying to open connection to the same server. Default is 0, which means to use the default 15s if autoConnectRetry is on.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="write-number" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
This specifies the number of servers to wait for on the write operation, and exception raising behavior. The 'w' option to the getlasterror command. Defaults to 0.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="write-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
This controls timeout for write operations in milliseconds. The 'wtimeout' option to the getlasterror command. Defaults to 0 (indefinite). Greater than zero is number of milliseconds to wait.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="write-fsync" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
This controls whether or not to fsync. The 'fsync' option to the getlasterror command. Defaults to false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="slave-ok" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
This controls if the driver is allowed to read from secondaries or slaves. Defaults to false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:group name="beanElementGroup">
<xsd:choice>
<xsd:element ref="beans:bean"/>
<xsd:element ref="beans:ref"/>
</xsd:choice>
</xsd:group>
<xsd:complexType name="customConverterType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Element defining a custom converterr.
]]></xsd:documentation>
</xsd:annotation>
<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1"/>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
A reference to a custom converter.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

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.
@@ -37,7 +37,7 @@ import org.springframework.stereotype.Component;
import com.mongodb.DBObject;
/**
* Integration tests for {@link MongoParser}.
* Integration tests for {@link MappingMongoConverterParser}.
*
* @author Oliver Gierke
*/

View File

@@ -0,0 +1,68 @@
/*
* 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 org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
/**
* Integration test for creation of instance of
* {@link org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener} by defining
* {@code <mongo:mapping-converter />} in context XML.
*
* @see DATAMONGO-36
* @author Maciej Walkowiak
*/
public class MappingMongoConverterParserValidationIntegrationTests {
DefaultListableBeanFactory factory;
BeanDefinitionReader reader;
@Before
public void setUp() {
factory = new DefaultListableBeanFactory();
reader = new XmlBeanDefinitionReader(factory);
}
@Test
public void validatingEventListenerCreatedWithDefaultConfig() {
reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-default.xml"));
assertThat(factory.getBean(BeanNames.VALIDATING_EVENT_LISTENER), is(not(nullValue())));
}
@Test
public void validatingEventListenerCreatedWhenValidationEnabled() {
reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-validation-enabled.xml"));
assertThat(factory.getBean(BeanNames.VALIDATING_EVENT_LISTENER), is(not(nullValue())));
}
@Test(expected = NoSuchBeanDefinitionException.class)
public void validatingEventListenersIsNotCreatedWhenDisabled() {
reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-validation-disabled.xml"));
factory.getBean(BeanNames.VALIDATING_EVENT_LISTENER);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2012 the original author or authors.
* 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.
@@ -16,7 +16,6 @@
package org.springframework.data.mongodb.config;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
@@ -30,7 +29,6 @@ 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;
@@ -38,45 +36,47 @@ import com.mongodb.ServerAddress;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MongoNamespaceReplicaSetTests {
public class MongoNamespaceReplicaSetTests extends NamespaceTestSupport {
@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 = (List<ServerAddress>) ReflectionTestUtils.getField(mfb, "replicaSetSeeds");
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());
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");
List<ServerAddress> replicaSetSeeds = readField("replicaSetSeeds", mfb);
assertNotNull(replicaSetSeeds);
assertEquals("192.168.174.130", replicaSetSeeds.get(0).getHost());
assertEquals(27017, replicaSetSeeds.get(0).getPort());
assertEquals("192.168.174.130", replicaSetSeeds.get(1).getHost());
assertEquals(27018, replicaSetSeeds.get(1).getPort());
assertEquals("192.168.174.130", replicaSetSeeds.get(2).getHost());
assertEquals(27019, replicaSetSeeds.get(2).getPort());
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();
@@ -88,5 +88,6 @@ public class MongoNamespaceReplicaSetTests {
MongoTemplate template = new MongoTemplate(mongo, "admin");
CommandResult result = template.executeCommand("{replSetGetStatus : 1}");
assertEquals("blort", result.getString("set"));
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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

@@ -1,80 +0,0 @@
/*
* 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

@@ -1,81 +0,0 @@
/*
* 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

@@ -1,65 +0,0 @@
/*
* 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

@@ -37,6 +37,7 @@ import org.springframework.data.mongodb.core.query.NearQuery;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
/**
* Abstract base class for unit tests to specify behaviour we expect from {@link MongoOperations}. Subclasses return
@@ -80,6 +81,10 @@ public abstract class MongoOperationsUnitTests {
public Object convertToMongoType(Object obj) {
return null;
}
public DBRef toDBRef(Object object, MongoPersistentProperty referingProperty) {
return null;
}
};
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2012 the original author or authors.
* 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.
@@ -39,7 +39,6 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
@@ -48,6 +47,7 @@ import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.Index.Duplicates;
import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Criteria;
@@ -72,7 +72,6 @@ import com.mongodb.WriteResult;
*
* @author Oliver Gierke
* @author Thomas Risberg
* @author Amol Nayak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@@ -162,112 +161,6 @@ public class MongoTemplateTests {
mongoTemplate.updateFirst(q, u, Person.class);
}
/**
* @see DATAMONGO-480
*/
@Test
public void throwsExceptionForDuplicateIds() {
MongoTemplate template = new MongoTemplate(factory);
template.setWriteResultChecking(WriteResultChecking.EXCEPTION);
Person person = new Person(new ObjectId(), "Amol");
person.setAge(28);
template.insert(person);
try {
template.insert(person);
fail("Expected DataIntegrityViolationException!");
} catch (DataIntegrityViolationException e) {
assertThat(e.getMessage(), containsString("E11000 duplicate key error index: database.person.$_id_ dup key:"));
}
}
/**
* @see DATAMONGO-480
*/
@Test
public void throwsExceptionForUpdateWithInvalidPushOperator() {
MongoTemplate template = new MongoTemplate(factory);
template.setWriteResultChecking(WriteResultChecking.EXCEPTION);
ObjectId id = new ObjectId();
Person person = new Person(id, "Amol");
person.setAge(28);
template.insert(person);
try {
Query query = new Query(Criteria.where("firstName").is("Amol"));
Update upd = new Update().push("age", 29);
template.updateFirst(query, upd, Person.class);
fail("Expected DataIntegrityViolationException!");
} catch (DataIntegrityViolationException e) {
assertThat(e.getMessage(),
is("Execution of update with '{ \"$push\" : { \"age\" : 29}}'' using '{ \"firstName\" : \"Amol\"}' "
+ "query failed: Cannot apply $push/$pushAll modifier to non-array"));
}
}
/**
* @see DATAMONGO-480
*/
@Test
public void throwsExceptionForIndexViolationIfConfigured() {
MongoTemplate template = new MongoTemplate(factory);
template.setWriteResultChecking(WriteResultChecking.EXCEPTION);
template.indexOps(Person.class).ensureIndex(new Index().on("firstName", Order.DESCENDING).unique());
Person person = new Person(new ObjectId(), "Amol");
person.setAge(28);
template.save(person);
person = new Person(new ObjectId(), "Amol");
person.setAge(28);
try {
template.save(person);
fail("Expected DataIntegrityViolationException!");
} catch (DataIntegrityViolationException e) {
assertThat(e.getMessage(),
containsString("E11000 duplicate key error index: database.person.$firstName_-1 dup key:"));
}
}
/**
* @see DATAMONGO-480
*/
@Test
public void rejectsDuplicateIdInInsertAll() {
MongoTemplate template = new MongoTemplate(factory);
template.setWriteResultChecking(WriteResultChecking.EXCEPTION);
ObjectId id = new ObjectId();
Person person = new Person(id, "Amol");
person.setAge(28);
List<Person> records = new ArrayList<Person>();
records.add(person);
records.add(person);
try {
template.insertAll(records);
fail("Expected DataIntegrityViolationException!");
} catch (DataIntegrityViolationException e) {
assertThat(
e.getMessage(),
startsWith("Insert list failed: E11000 duplicate key error index: database.person.$_id_ dup key: { : ObjectId"));
}
}
@Test
public void testEnsureIndex() throws Exception {
@@ -298,14 +191,17 @@ public class MongoTemplateTests {
assertThat(dropDupes, is(true));
List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo();
System.out.println(indexInfoList);
assertThat(indexInfoList.size(), is(2));
IndexInfo ii = indexInfoList.get(1);
assertThat(ii.isUnique(), is(true));
assertThat(ii.isDropDuplicates(), is(true));
assertThat(ii.isSparse(), is(false));
assertThat(ii.getFieldSpec().containsKey("age"), is(true));
assertThat(ii.getFieldSpec().containsValue(Order.DESCENDING), is(true));
List<IndexField> indexFields = ii.getIndexFields();
IndexField field = indexFields.get(0);
assertThat(field, is(IndexField.create("age", Order.DESCENDING)));
}
@Test
@@ -1015,6 +911,8 @@ public class MongoTemplateTests {
assertThat(lastMongoAction.getEntityClass().toString(), is(PersonWithIdPropertyOfTypeObjectId.class.toString()));
assertThat(lastMongoAction.getMongoActionOperation(), is(MongoActionOperation.UPDATE));
assertThat(lastMongoAction.getQuery(), equalTo(q.getQueryObject()));
assertThat(lastMongoAction.getDocument(), equalTo(u.getUpdateObject()));
}
private class FsyncSafeWriteConcernResolver implements WriteConcernResolver {
@@ -1040,7 +938,7 @@ public class MongoTemplateTests {
DBRef first = new DBRef(factory.getDb(), "foo", new ObjectId());
DBRef second = new DBRef(factory.getDb(), "bar", new ObjectId());
template.updateFirst(null, update("dbRefs", Arrays.asList(first, second)), ClassWithDBRefs.class);
template.updateFirst(null, Update.update("dbRefs", Arrays.asList(first, second)), ClassWithDBRefs.class);
}
class ClassWithDBRefs {
@@ -1203,59 +1101,12 @@ public class MongoTemplateTests {
template.save(second);
Query query = query(where("field").not().regex("Matthews"));
System.out.println(query.getQueryObject());
List<Sample> result = template.find(query, Sample.class);
assertThat(result.size(), is(1));
assertThat(result.get(0).field, is("Beauford"));
}
/**
* @see DATAMONGO-447
*/
@Test
public void storesAndRemovesTypeWithComplexId() {
MyId id = new MyId();
id.first = "foo";
id.second = "bar";
TypeWithMyId source = new TypeWithMyId();
source.id = id;
template.save(source);
template.remove(query(where("id").is(id)), TypeWithMyId.class);
}
/**
* @see DATAMONGO-539
*/
@Test
public void removesObjectFromExplicitCollection() {
String collectionName = "explicit";
template.remove(new Query(), collectionName);
PersonWithConvertedId person = new PersonWithConvertedId();
person.name = "Dave";
template.save(person, collectionName);
assertThat(template.findAll(PersonWithConvertedId.class, collectionName).isEmpty(), is(false));
template.remove(person, collectionName);
assertThat(template.findAll(PersonWithConvertedId.class, collectionName).isEmpty(), is(true));
}
static class MyId {
String first;
String second;
}
static class TypeWithMyId {
@Id
MyId id;
}
public static class Sample {
@Id
@@ -1273,12 +1124,6 @@ public class MongoTemplateTests {
}
}
static class PersonWithConvertedId {
String id;
String name;
}
static enum DateTimeToDateConverter implements Converter<DateTime, Date> {
INSTANCE;

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.
@@ -17,10 +17,11 @@ package org.springframework.data.mongodb.core;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.math.BigInteger;
import java.util.regex.Pattern;
import java.util.Collections;
import org.bson.types.ObjectId;
import org.junit.Before;
@@ -30,10 +31,17 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.DB;
@@ -52,20 +60,24 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
MongoTemplate template;
@Mock
MongoDbFactory factory;
@Mock
Mongo mongo;
@Mock
DB db;
@Mock
DBCollection collection;
MappingMongoConverter converter;
@Before
public void setUp() {
this.template = new MongoTemplate(mongo, "database");
when(mongo.getDB("database")).thenReturn(db);
this.converter = new MappingMongoConverter(factory, new MongoMappingContext());
this.template = new MongoTemplate(factory, converter);
when(factory.getDb()).thenReturn(db);
when(db.getCollection(Mockito.any(String.class))).thenReturn(collection);
}
@@ -127,6 +139,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
@Test
public void autogeneratesIdForEntityWithAutogeneratableId() {
this.converter.afterPropertiesSet();
MongoTemplate template = spy(this.template);
doReturn(new ObjectId()).when(template).saveDBObject(Mockito.any(String.class), Mockito.any(DBObject.class),
Mockito.any(Class.class));
@@ -138,28 +152,24 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
}
/**
* @see DATAMONGO-474
* @see DATAMONGO-374
*/
@Test
public void setsUnpopulatedIdField() {
public void convertsUpdateConstraintsUsingConverters() {
NotAutogenerateableId entity = new NotAutogenerateableId();
CustomConversions conversions = new CustomConversions(Collections.singletonList(MyConverter.INSTANCE));
this.converter.setCustomConversions(conversions);
this.converter.afterPropertiesSet();
template.populateIdIfNecessary(entity, 5);
assertThat(entity.id, is(5));
}
Query query = new Query();
Update update = new Update().set("foo", new AutogenerateableId());
/**
* @see DATAMONGO-474
*/
@Test
public void doesNotSetAlreadyPopulatedId() {
template.updateFirst(query, update, Wrapper.class);
NotAutogenerateableId entity = new NotAutogenerateableId();
entity.id = 5;
QueryMapper queryMapper = new QueryMapper(converter);
DBObject reference = queryMapper.getMappedObject(update.getUpdateObject(), null);
template.populateIdIfNecessary(entity, 7);
assertThat(entity.id, is(5));
verify(collection, times(1)).update(Mockito.any(DBObject.class), eq(reference), anyBoolean(), anyBoolean());
}
class AutogenerateableId {
@@ -172,12 +182,22 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
@Id
Integer id;
}
public Pattern getId() {
return Pattern.compile(".");
enum MyConverter implements Converter<AutogenerateableId, String> {
INSTANCE;
public String convert(AutogenerateableId source) {
return source.toString();
}
}
class Wrapper {
AutogenerateableId foo;
}
/**
* Mocks out the {@link MongoTemplate#getDb()} method to return the {@link DB} mock instead of executing the actual
* behaviour.
@@ -191,10 +211,9 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
return template;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoOperationsUnitTests#getOperationsForExceptionHandling()
*/
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.core.MongoOperationsUnitTests#getOperations()
*/
@Override
protected MongoOperations getOperationsForExceptionHandling() {
MongoTemplate template = spy(this.template);
@@ -202,10 +221,9 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
return template;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoOperationsUnitTests#getOperations()
*/
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.core.MongoOperationsUnitTests#getOperations()
*/
@Override
protected MongoOperations getOperations() {
return this.template;

View File

@@ -1,67 +0,0 @@
/*
* 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 static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import java.util.Arrays;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Unit tests for {@link SerializationUtils}.
*
* @author Oliver Gierke
*/
public class SerializationUtilsUnitTests {
@Test
public void writesSimpleDBObject() {
DBObject dbObject = new BasicDBObject("foo", "bar");
assertThat(serializeToJsonSafely(dbObject), is("{ \"foo\" : \"bar\"}"));
}
@Test
public void writesComplexObjectAsPlainToString() {
DBObject dbObject = new BasicDBObject("foo", new Complex());
assertThat(serializeToJsonSafely(dbObject),
startsWith("{ \"foo\" : { $java : org.springframework.data.mongodb.core.SerializationUtilsUnitTests$Complex"));
}
@Test
public void writesCollection() {
DBObject dbObject = new BasicDBObject("foo", Arrays.asList("bar", new Complex()));
Matcher<String> expectedOutput = allOf(
startsWith("{ \"foo\" : [ \"bar\", { $java : org.springframework.data.mongodb.core.SerializationUtilsUnitTests$Complex"),
endsWith(" } ] }"));
assertThat(serializeToJsonSafely(dbObject), is(expectedOutput));
}
static class Complex {
}
}

View File

@@ -2,12 +2,14 @@ package org.springframework.data.mongodb.core;
import java.util.ArrayList;
import java.util.List;
import com.mongodb.Mongo;
import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import com.mongodb.Mongo;
public class TestMongoConfiguration extends AbstractMongoConfiguration {
@@ -24,15 +26,15 @@ public class TestMongoConfiguration extends AbstractMongoConfiguration {
@Override
public String getMappingBasePackage() {
return "org.springframework.data.mongodb.core.core.mapping";
return MongoMappingContext.class.getPackage().getName();
}
@Override
protected void afterMappingMongoConverterCreation(MappingMongoConverter converter) {
public CustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new org.springframework.data.mongodb.core.PersonReadConverter());
converters.add(new org.springframework.data.mongodb.core.PersonWriteConverter());
converter.setCustomConversions(new CustomConversions(converters));
return new CustomConversions(converters);
}
}

View File

@@ -3,7 +3,6 @@ package org.springframework.data.mongodb.core.convert;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.net.URL;
import java.text.DateFormat;
import java.text.Format;
import java.util.Arrays;
@@ -153,25 +152,6 @@ public class CustomConversionsUnitTests {
assertThat(conversions.isSimpleType(Binary.class), is(true));
}
/**
* @see DATAMONGO-462
*/
@Test
public void hasWriteConverterForURL() {
CustomConversions conversions = new CustomConversions();
assertThat(conversions.hasCustomWriteTarget(URL.class), is(true));
}
/**
* @see DATAMONGO-462
*/
@Test
public void readTargetForURL() {
CustomConversions conversions = new CustomConversions();
assertThat(conversions.hasCustomReadTarget(String.class, URL.class), is(true));
}
enum FormatToStringConverter implements Converter<Format, String> {
INSTANCE;

View File

@@ -1,44 +0,0 @@
/*
* 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 org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
/**
* Unit tests for {@link MappedConstructor}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class MappedConstructorUnitTests {
@Mock
MongoPersistentEntity<?> entity;
@Mock
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
@Test(expected = MappingException.class)
public void rejectsEntityWithoutPersistenceConstructor() {
new MappedConstructor(entity, mappingContext);
}
}

View File

@@ -21,7 +21,6 @@ import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -46,16 +45,20 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.MappingInstantiationException;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.PersonPojoStringId;
import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
/**
* Unit tests for {@link MappingMongoConverter}.
@@ -115,30 +118,12 @@ public class MappingMongoConverterUnitTests {
DBObject dbObject = new BasicDBObject();
converter.write(person, dbObject);
assertThat(dbObject.get("birthDate"), is(instanceOf(Date.class)));
assertThat(dbObject.get("birthDate"), is(Date.class));
Person result = converter.read(Person.class, dbObject);
assertThat(result.birthDate, is(notNullValue()));
}
@Test
public void convertsCustomTypeOnConvertToMongoType() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(new LocalDateToDateConverter());
converters.add(new DateToLocalDateConverter());
CustomConversions conversions = new CustomConversions(converters);
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
converter = new MappingMongoConverter(factory, mappingContext);
converter.setCustomConversions(conversions);
converter.afterPropertiesSet();
LocalDate date = new LocalDate();
converter.convertToMongoType(date);
}
/**
* @see DATAMONGO-130
*/
@@ -176,7 +161,7 @@ public class MappingMongoConverterUnitTests {
dbObject.put("birthDate", new LocalDate());
dbObject.put(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, Person.class.getName());
assertThat(converter.read(Contact.class, dbObject), is(instanceOf(Person.class)));
assertThat(converter.read(Contact.class, dbObject), is(Person.class));
}
/**
@@ -189,7 +174,7 @@ public class MappingMongoConverterUnitTests {
dbObject.put("birthDate", new LocalDate());
dbObject.put(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, Person.class.getName());
assertThat(converter.read(BirthDateContainer.class, dbObject), is(instanceOf(BirthDateContainer.class)));
assertThat(converter.read(BirthDateContainer.class, dbObject), is(BirthDateContainer.class));
}
@Test
@@ -217,7 +202,7 @@ public class MappingMongoConverterUnitTests {
DBObject result = new BasicDBObject();
converter.write(value, result);
assertThat(result.get("sampleEnum"), is(instanceOf(String.class)));
assertThat(result.get("sampleEnum"), is(String.class));
assertThat(result.get("sampleEnum").toString(), is("FIRST"));
}
@@ -233,7 +218,7 @@ public class MappingMongoConverterUnitTests {
DBObject result = new BasicDBObject();
converter.write(value, result);
assertThat(result.get("enums"), is(instanceOf(BasicDBList.class)));
assertThat(result.get("enums"), is(BasicDBList.class));
BasicDBList enums = (BasicDBList) result.get("enums");
assertThat(enums.size(), is(1));
@@ -263,7 +248,7 @@ public class MappingMongoConverterUnitTests {
ClassWithEnumProperty result = converter.read(ClassWithEnumProperty.class, dbObject);
assertThat(result.enums, is(instanceOf(List.class)));
assertThat(result.enums, is(List.class));
assertThat(result.enums.size(), is(1));
assertThat(result.enums, hasItem(SampleEnum.FIRST));
}
@@ -328,7 +313,7 @@ public class MappingMongoConverterUnitTests {
converter.write(wrapper, dbObject);
Object result = dbObject.get("contacts");
assertThat(result, is(instanceOf(BasicDBList.class)));
assertThat(result, is(BasicDBList.class));
BasicDBList contacts = (BasicDBList) result;
DBObject personDbObject = (DBObject) contacts.get(0);
assertThat(personDbObject.get("foo").toString(), is("Oliver"));
@@ -351,7 +336,7 @@ public class MappingMongoConverterUnitTests {
assertThat(result.contacts, is(notNullValue()));
assertThat(result.contacts.size(), is(1));
Contact contact = result.contacts.get(0);
assertThat(contact, is(instanceOf(Person.class)));
assertThat(contact, is(Person.class));
assertThat(((Person) contact).firstname, is("Oliver"));
}
@@ -365,7 +350,7 @@ public class MappingMongoConverterUnitTests {
converter.write(wrapper, dbObject);
Object localeField = dbObject.get("locale");
assertThat(localeField, is(instanceOf(String.class)));
assertThat(localeField, is(String.class));
assertThat((String) localeField, is("en_US"));
LocaleWrapper read = converter.read(LocaleWrapper.class, dbObject);
@@ -473,13 +458,13 @@ public class MappingMongoConverterUnitTests {
DBObject dbo1 = new BasicDBObject();
converter.write(p1, dbo1);
assertThat(dbo1.get("_id"), is(instanceOf(String.class)));
assertThat(dbo1.get("_id"), is(String.class));
PersonPojoStringId p2 = new PersonPojoStringId(new ObjectId().toString(), "Text-1");
DBObject dbo2 = new BasicDBObject();
converter.write(p2, dbo2);
assertThat(dbo2.get("_id"), is(instanceOf(ObjectId.class)));
assertThat(dbo2.get("_id"), is(ObjectId.class));
}
/**
@@ -493,8 +478,8 @@ public class MappingMongoConverterUnitTests {
ClassWithSortedMap result = converter.read(ClassWithSortedMap.class, wrapper);
assertThat(result, is(instanceOf(ClassWithSortedMap.class)));
assertThat(result.map, is(instanceOf(SortedMap.class)));
assertThat(result, is(ClassWithSortedMap.class));
assertThat(result.map, is(SortedMap.class));
}
/**
@@ -760,7 +745,7 @@ public class MappingMongoConverterUnitTests {
assertThat(result.containsField("Foo"), is(true));
assertThat(result.get("Foo"), is(notNullValue()));
assertThat(result.get("Foo"), is(instanceOf(BasicDBList.class)));
assertThat(result.get("Foo"), is(BasicDBList.class));
BasicDBList list = (BasicDBList) result.get("Foo");
@@ -811,11 +796,11 @@ public class MappingMongoConverterUnitTests {
converter.write(wrapper, result);
Object mapObject = result.get("mapOfObjects");
assertThat(mapObject, is(instanceOf(BasicDBObject.class)));
assertThat(mapObject, is(BasicDBObject.class));
DBObject map = (DBObject) mapObject;
Object valueObject = map.get("foo");
assertThat(valueObject, is(instanceOf(BasicDBList.class)));
assertThat(valueObject, is(BasicDBList.class));
List<Object> list = (List<Object>) valueObject;
assertThat(list.size(), is(1));
@@ -906,7 +891,7 @@ public class MappingMongoConverterUnitTests {
converter.write(wrapper, result);
Object contacts = result.get("contacts");
assertThat(contacts, is(instanceOf(Collection.class)));
assertThat(contacts, is(Collection.class));
assertThat(((Collection<?>) contacts).size(), is(2));
assertThat(((Collection<Object>) contacts), hasItem(nullValue()));
}
@@ -969,7 +954,7 @@ public class MappingMongoConverterUnitTests {
Item read = converter.read(Item.class, result);
assertThat(read.attributes.size(), is(1));
assertThat(read.attributes.get(0).key, is(attribute.key));
assertThat(read.attributes.get(0).value, is(instanceOf(Collection.class)));
assertThat(read.attributes.get(0).value, is(Collection.class));
@SuppressWarnings("unchecked")
Collection<String> values = (Collection<String>) read.attributes.get(0).value;
@@ -1028,92 +1013,70 @@ public class MappingMongoConverterUnitTests {
address.street = "Foo";
Object result = converter.convertToMongoType(Collections.singleton(address));
assertThat(result, is(instanceOf(BasicDBList.class)));
assertThat(result, is(BasicDBList.class));
Set<?> readResult = converter.read(Set.class, (BasicDBList) result);
assertThat(readResult.size(), is(1));
assertThat(readResult.iterator().next(), is(instanceOf(Map.class)));
assertThat(readResult.iterator().next(), is(Map.class));
}
/**
* @see DATAMONGO-462
* @see DATAMONGO-402
*/
@Test
public void readsURLsAsStringsByDefault() throws Exception {
DBObject dbObject = new BasicDBObject("url", new URL("http://springsource.org"));
URLWrapper result = converter.read(URLWrapper.class, dbObject);
assertThat(result.url, is(new URL("http://springsource.org")));
public void readsMemberClassCorrectly() {
DBObject dbObject = new BasicDBObject("inner", new BasicDBObject("value", "FOO!"));
Outer outer = converter.read(Outer.class, dbObject);
assertThat(outer.inner, is(notNullValue()));
assertThat(outer.inner.value, is("FOO!"));
assertSyntheticFieldValueOf(outer.inner, outer);
}
/**
* @see DATAMONGO-462
* @see DATAMONGO-347
*/
@Test
public void writesURLsAsStringsByDefault() throws Exception {
public void createsSimpleDBRefCorrectly() {
URLWrapper wrapper = new URLWrapper();
wrapper.url = new URL("http://springsource.org");
DBObject sink = new BasicDBObject();
Person person = new Person();
person.id = "foo";
converter.write(wrapper, sink);
assertThat(sink.get("url"), is((Object) "http://springsource.org"));
DBRef dbRef = converter.toDBRef(person, null);
assertThat(dbRef.getId(), is((Object) "foo"));
assertThat(dbRef.getRef(), is("person"));
}
/**
* @see DATAMONGO-485
* @see DATAMONGO-347
*/
@Test
public void writesComplexIdCorrectly() {
public void createsDBRefWithClientSpecCorrectly() {
ComplexId id = new ComplexId();
id.innerId = 4711L;
PropertyPath path = PropertyPath.from("person", PersonClient.class);
MongoPersistentProperty property = mappingContext.getPersistentPropertyPath(path).getLeafProperty();
ClassWithComplexId entity = new ClassWithComplexId();
entity.complexId = id;
Person person = new Person();
person.id = "foo";
DBObject dbObject = new BasicDBObject();
converter.write(entity, dbObject);
Object idField = dbObject.get("_id");
assertThat(idField, is(notNullValue()));
assertThat(idField, is(instanceOf(DBObject.class)));
assertThat(((DBObject) idField).get("innerId"), is((Object) 4711L));
DBRef dbRef = converter.toDBRef(person, property);
assertThat(dbRef.getId(), is((Object) "foo"));
assertThat(dbRef.getRef(), is("person"));
}
/**
* @see DATAMONGO-485
*/
@Test
public void readsComplexIdCorrectly() {
private static void assertSyntheticFieldValueOf(Object target, Object expected) {
DBObject innerId = new BasicDBObject("innerId", 4711L);
DBObject entity = new BasicDBObject("_id", innerId);
for (int i = 0; i < 10; i++) {
try {
assertThat(ReflectionTestUtils.getField(target, "this$" + i), is(expected));
return;
} catch (IllegalArgumentException e) {
// Suppress and try next
}
}
ClassWithComplexId result = converter.read(ClassWithComplexId.class, entity);
assertThat(result.complexId, is(notNullValue()));
assertThat(result.complexId.innerId, is(4711L));
}
/**
* @see DATAMONGO-489
*/
@Test
public void readsArraysAsMapValuesCorrectly() {
BasicDBList list = new BasicDBList();
list.add("Foo");
list.add("Bar");
DBObject map = new BasicDBObject("key", list);
DBObject wrapper = new BasicDBObject("mapOfStrings", map);
ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, wrapper);
assertThat(result.mapOfStrings, is(notNullValue()));
String[] values = result.mapOfStrings.get("key");
assertThat(values, is(notNullValue()));
assertThat(values, is(arrayWithSize(2)));
fail(String.format("Didn't find synthetic field on %s!", target));
}
static class GenericType<T> {
@@ -1152,6 +1115,10 @@ public class MappingMongoConverterUnitTests {
}
static class Person implements Contact {
@Id
String id;
LocalDate birthDate;
@Field("foo")
@@ -1177,7 +1144,6 @@ public class MappingMongoConverterUnitTests {
Map<Locale, String> map;
Map<String, List<String>> mapOfLists;
Map<String, Object> mapOfObjects;
Map<String, String[]> mapOfStrings;
}
static class ClassWithNestedMaps {
@@ -1248,18 +1214,19 @@ public class MappingMongoConverterUnitTests {
Object value;
}
static class URLWrapper {
URL url;
static class Outer {
class Inner {
String value;
}
Inner inner;
}
static class ClassWithComplexId {
static class PersonClient {
@Id
ComplexId complexId;
}
static class ComplexId {
Long innerId;
@org.springframework.data.mongodb.core.mapping.DBRef
Person person;
}
private class LocalDateToDateConverter implements Converter<LocalDate, Date> {

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2011-2012 the original author or authors.
* 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
* 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,18 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.query;
package org.springframework.data.mongodb.core.convert;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.core.DBObjectUtils.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bson.types.ObjectId;
import org.junit.Before;
@@ -35,10 +32,11 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.QueryMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
@@ -51,6 +49,7 @@ import com.mongodb.QueryBuilder;
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings("unused")
public class QueryMapperUnitTests {
QueryMapper mapper;
@@ -85,15 +84,15 @@ public class QueryMapperUnitTests {
public void convertsStringIntoObjectId() {
DBObject query = new BasicDBObject("_id", new ObjectId().toString());
DBObject result = mapper.getMappedObject(query, context.getPersistentEntity(IdWrapper.class));
assertThat(result.get("_id"), is(instanceOf(ObjectId.class)));
DBObject result = mapper.getMappedObject(query, null);
assertThat(result.get("_id"), is(ObjectId.class));
}
@Test
public void handlesBigIntegerIdsCorrectly() {
DBObject dbObject = new BasicDBObject("id", new BigInteger("1"));
DBObject result = mapper.getMappedObject(dbObject, context.getPersistentEntity(IdWrapper.class));
DBObject result = mapper.getMappedObject(dbObject, null);
assertThat(result.get("_id"), is((Object) "1"));
}
@@ -102,7 +101,7 @@ public class QueryMapperUnitTests {
ObjectId id = new ObjectId();
DBObject dbObject = new BasicDBObject("id", new BigInteger(id.toString(), 16));
DBObject result = mapper.getMappedObject(dbObject, context.getPersistentEntity(IdWrapper.class));
DBObject result = mapper.getMappedObject(dbObject, null);
assertThat(result.get("_id"), is((Object) id));
}
@@ -116,9 +115,9 @@ public class QueryMapperUnitTests {
DBObject result = mapper.getMappedObject(criteria.getCriteriaObject(), context.getPersistentEntity(Sample.class));
Object object = result.get("_id");
assertThat(object, is(instanceOf(DBObject.class)));
assertThat(object, is(DBObject.class));
DBObject dbObject = (DBObject) object;
assertThat(dbObject.get("$ne"), is(instanceOf(ObjectId.class)));
assertThat(dbObject.get("$ne"), is(ObjectId.class));
}
/**
@@ -130,7 +129,7 @@ public class QueryMapperUnitTests {
DBObject result = mapper.getMappedObject(query.getQueryObject(), null);
Object object = result.get("foo");
assertThat(object, is(instanceOf(String.class)));
assertThat(object, is(String.class));
}
@Test
@@ -139,10 +138,10 @@ public class QueryMapperUnitTests {
DBObject result = mapper.getMappedObject(query.getQueryObject(), null);
Object object = result.get("foo");
assertThat(object, is(instanceOf(DBObject.class)));
assertThat(object, is(DBObject.class));
Object ne = ((DBObject) object).get("$ne");
assertThat(ne, is(instanceOf(String.class)));
assertThat(ne, is(String.class));
assertThat(ne.toString(), is(Enum.INSTANCE.name()));
}
@@ -153,14 +152,14 @@ public class QueryMapperUnitTests {
DBObject result = mapper.getMappedObject(query.getQueryObject(), null);
Object object = result.get("foo");
assertThat(object, is(instanceOf(DBObject.class)));
assertThat(object, is(DBObject.class));
Object in = ((DBObject) object).get("$in");
assertThat(in, is(instanceOf(BasicDBList.class)));
assertThat(in, is(BasicDBList.class));
BasicDBList list = (BasicDBList) in;
assertThat(list.size(), is(1));
assertThat(list.get(0), is(instanceOf(String.class)));
assertThat(list.get(0), is(String.class));
assertThat(list.get(0).toString(), is(Enum.INSTANCE.name()));
}
@@ -200,68 +199,6 @@ public class QueryMapperUnitTests {
assertThat(result, is(query.getQueryObject()));
}
@Test
public void doesNotHandleNestedFieldsWithDefaultIdNames() {
BasicDBObject dbObject = new BasicDBObject("id", new ObjectId().toString());
dbObject.put("nested", new BasicDBObject("id", new ObjectId().toString()));
MongoPersistentEntity<?> entity = context.getPersistentEntity(ClassWithDefaultId.class);
DBObject result = mapper.getMappedObject(dbObject, entity);
assertThat(result.get("_id"), is(instanceOf(ObjectId.class)));
assertThat(((DBObject) result.get("nested")).get("id"), is(instanceOf(String.class)));
}
/**
* @see DATAMONGO-493
*/
@Test
public void doesNotTranslateNonIdPropertiesFor$NeCriteria() {
ObjectId accidentallyAnObjectId = new ObjectId();
Query query = Query.query(Criteria.where("id").is("id_value").and("publishers")
.ne(accidentallyAnObjectId.toString()));
DBObject dbObject = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(UserEntity.class));
assertThat(dbObject.get("publishers"), is(instanceOf(DBObject.class)));
DBObject publishers = (DBObject) dbObject.get("publishers");
assertThat(publishers.containsField("$ne"), is(true));
assertThat(publishers.get("$ne"), is(instanceOf(String.class)));
}
/**
* @see DATAMONGO-494
*/
@Test
public void usesEntityMetadataInOr() {
Query query = query(new Criteria().orOperator(where("foo").is("bar")));
DBObject result = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Sample.class));
assertThat(result.keySet(), hasSize(1));
assertThat(result.keySet(), hasItem("$or"));
BasicDBList ors = getAsDBList(result, "$or");
assertThat(ors, hasSize(1));
DBObject criterias = getAsDBObject(ors, 0);
assertThat(criterias.keySet(), hasSize(1));
assertThat(criterias.get("_id"), is(notNullValue()));
assertThat(criterias.get("foo"), is(nullValue()));
}
class IdWrapper {
Object id;
}
class ClassWithDefaultId {
String id;
ClassWithDefaultId nested;
}
class Sample {
@Id
@@ -277,9 +214,4 @@ public class QueryMapperUnitTests {
enum Enum {
INSTANCE;
}
class UserEntity {
String id;
List<String> publishers = new ArrayList<String>();
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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 static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.data.mongodb.core.query.Order;
/**
* Unit tests for {@link IndexInfo}.
*
* @author Oliver Gierke
*/
public class IndexInfoUnitTests {
@Test
public void isIndexForFieldsCorrectly() {
IndexField fooField = IndexField.create("foo", Order.ASCENDING);
IndexField barField = IndexField.create("bar", Order.DESCENDING);
IndexInfo info = new IndexInfo(Arrays.asList(fooField, barField), "myIndex", false, false, false);
assertThat(info.isIndexForFields(Arrays.asList("foo", "bar")), is(true));
}
}

View File

@@ -1,82 +0,0 @@
/*
* 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 static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import com.mongodb.DBObject;
/**
* Unit tests for {@link MongoPersistentEntityIndexCreator}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class MongoPersistentEntityIndexCreatorUnitTests {
@Mock
MongoDbFactory factory;
@Test
public void buildsIndexDefinitionUsingFieldName() {
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(Collections.singleton(Person.class));
mappingContext.afterPropertiesSet();
DummyMongoPersistentEntityIndexCreator creator = new DummyMongoPersistentEntityIndexCreator(mappingContext, factory);
assertThat(creator.indexDefinition, is(notNullValue()));
assertThat(creator.indexDefinition.keySet(), hasItem("fieldname"));
assertThat(creator.name, is("indexName"));
}
static class Person {
@Indexed(name = "indexName")
@Field("fieldname")
String field;
}
static class DummyMongoPersistentEntityIndexCreator extends MongoPersistentEntityIndexCreator {
DBObject indexDefinition;
String name;
public DummyMongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, MongoDbFactory mongoDbFactory) {
super(mappingContext, mongoDbFactory);
}
@Override
protected void ensureIndex(String collection, String name, DBObject indexDefinition, boolean unique,
boolean dropDups, boolean sparse) {
this.name = name;
this.indexDefinition = indexDefinition;
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.mapping.event;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
/**
* Class used to test JSR-303 validation
* {@link org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener}
*
* @see DATAMONGO-36
* @author Maciej Walkowiak
*/
public class User {
@Size(min = 10)
private String name;
@Min(18)
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.mapping.event;
import static org.hamcrest.core.IsEqual.*;
import static org.junit.Assert.*;
import javax.validation.ConstraintViolationException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Integration test for {@link ValidatingMongoEventListener}.
*
* @see DATAMONGO-36
* @author Maciej Walkowiak
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ValidatingMongoEventListenerTest {
@Autowired
MongoTemplate mongoTemplate;
@Test
public void shouldThrowConstraintViolationException() {
User user = new User("john", 17);
try {
mongoTemplate.save(user);
fail();
} catch (ConstraintViolationException e) {
assertThat(e.getConstraintViolations().size(), equalTo(2));
}
}
@Test
public void shouldNotThrowAnyExceptions() {
mongoTemplate.save(new User("john smith", 18));
}
}

View File

@@ -1,72 +0,0 @@
/*
* 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.mapreduce;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Collections;
import org.junit.Test;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Unit tests for {@link MapReduceResults}.
*
* @author Oliver Gierke
*/
public class MapReduceResultsUnitTests {
/**
* @see DATAMONGO-428
*/
@Test
public void resolvesOutputCollectionForPlainResult() {
DBObject rawResult = new BasicDBObject("result", "FOO");
MapReduceResults<Object> results = new MapReduceResults<Object>(Collections.emptyList(), rawResult);
assertThat(results.getOutputCollection(), is("FOO"));
}
/**
* @see DATAMONGO-428
*/
@Test
public void resolvesOutputCollectionForDBObjectResult() {
DBObject rawResult = new BasicDBObject("result", new BasicDBObject("collection", "FOO"));
MapReduceResults<Object> results = new MapReduceResults<Object>(Collections.emptyList(), rawResult);
assertThat(results.getOutputCollection(), is("FOO"));
}
/**
* @see DATAMONGO-378
*/
@Test
public void handlesLongTotalInResult() {
DBObject inner = new BasicDBObject("total", 1L);
inner.put("mapTime", 1L);
inner.put("emitLoop", 1);
DBObject source = new BasicDBObject("timing", inner);
new MapReduceResults<Object>(Collections.emptyList(), source);
}
}

View File

@@ -15,10 +15,11 @@
*/
package org.springframework.data.mongodb.core.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import org.junit.Test;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.core.query.Criteria;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -52,13 +53,9 @@ public class CriteriaTests {
assertEquals("{ \"name\" : \"Bubba\" , \"age\" : { \"$lt\" : 21}}", c.getCriteriaObject().toString());
}
@Test
public void equalIfCriteriaMatches() {
Criteria left = new Criteria("name").is("Foo").and("lastname").is("Bar");
Criteria right = new Criteria("name").is("Bar").and("lastname").is("Bar");
assertThat(left, is(not(right)));
assertThat(right, is(not(left)));
@Test(expected = InvalidMongoDbApiUsageException.class)
public void testCriteriaWithMultipleConditionsForSameKey() {
Criteria c = new Criteria("name").gte("M").and("name").ne("A");
c.getCriteriaObject();
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.gridfs;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.regex.Pattern;
import org.junit.Test;
/**
* Unit tests for {@link AntPath}.
*
* @author Oliver Gierke
*/
public class AntPathUnitTests {
@Test
public void buildRegexCorrectly() {
AntPath path = new AntPath("**/foo/*-bar.xml");
String regex = path.toRegex();
assertThat(Pattern.matches(regex, "foo/bar/foo/foo-bar.xml"), is(true));
assertThat(Pattern.matches(regex, "foo/bar/foo/bar/foo-bar.xml"), is(false));
assertThat(regex, is(".*\\Q/foo/\\E[^/]*\\Q-bar.xml\\E"));
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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.gridfs;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.gridfs.GridFsCriteria.*;
import java.io.IOException;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSFile;
/**
* Integration tests for {@link GridFsTemplate}.
*
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:gridfs/gridfs.xml")
public class GridFsTemplateIIntegrationTests {
Resource resource = new ClassPathResource("gridfs/gridfs.xml");
@Autowired
GridFsOperations operations;
@Before
public void setUp() {
operations.delete(null);
}
@Test
public void storesAndFindsSimpleDocument() throws IOException {
GridFSFile reference = operations.store(resource.getInputStream(), "foo.xml");
List<GridFSDBFile> result = operations.find(null);
assertThat(result.size(), is(1));
assertSame(result.get(0), reference);
}
@Test
public void writesMetadataCorrectly() throws IOException {
DBObject metadata = new BasicDBObject("key", "value");
GridFSFile reference = operations.store(resource.getInputStream(), "foo.xml", metadata);
List<GridFSDBFile> result = operations.find(query(whereMetaData("key").is("value")));
assertThat(result.size(), is(1));
assertSame(result.get(0), reference);
}
@Test
public void marshalsComplexMetadata() throws IOException {
Metadata metadata = new Metadata();
metadata.version = "1.0";
GridFSFile reference = operations.store(resource.getInputStream(), "foo.xml", metadata);
List<GridFSDBFile> result = operations.find(query(whereFilename().is("foo.xml")));
assertThat(result.size(), is(1));
assertSame(result.get(0), reference);
}
@Test
public void findsFilesByResourcePattern() throws IOException {
GridFSFile reference = operations.store(resource.getInputStream(), "foo.xml");
GridFsResource[] resources = operations.getResources("*.xml");
assertThat(resources.length, is(1));
assertThat(resources[0].getId(), is(reference.getId()));
assertThat(resources[0].contentLength(), is(reference.getLength()));
assertThat(resources[0].getContentType(), is(reference.getContentType()));
}
@Test
public void findsFilesByResourceLocation() throws IOException {
GridFSFile reference = operations.store(resource.getInputStream(), "foo.xml");
GridFsResource[] resources = operations.getResources("foo.xml");
assertThat(resources.length, is(1));
assertThat(resources[0].getId(), is(reference.getId()));
assertThat(resources[0].contentLength(), is(reference.getLength()));
assertThat(resources[0].getContentType(), is(reference.getContentType()));
}
private static void assertSame(GridFSFile left, GridFSFile right) {
assertThat(left.getId(), is(right.getId()));
assertThat(left.getMD5(), is(right.getMD5()));
assertThat(left.getMetaData(), is(right.getMetaData()));
}
class Metadata {
String version;
}
}

View File

@@ -0,0 +1,589 @@
/*
* 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.performance;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;
import org.bson.types.ObjectId;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.Constants;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.WriteConcern;
/**
* Test class to execute performance tests for plain MongoDB driver usage, {@link MongoTemplate} and the repositories
* abstraction.
*
* @author Oliver Gierke
*/
public class PerformanceTests {
private static final String DATABASE_NAME = "performance";
private static final int NUMBER_OF_PERSONS = 30000;
private static final StopWatch watch = new StopWatch();
private static final Collection<String> IGNORED_WRITE_CONCERNS = Arrays.asList("MAJORITY", "REPLICAS_SAFE",
"FSYNC_SAFE", "JOURNAL_SAFE");
private static final int COLLECTION_SIZE = 1024 * 1024 * 256; // 256 MB
private static final Collection<String> COLLECTION_NAMES = Arrays.asList("template", "driver", "person");
Mongo mongo;
MongoTemplate operations;
PersonRepository repository;
@Before
public void setUp() throws Exception {
this.mongo = new Mongo();
this.operations = new MongoTemplate(new SimpleMongoDbFactory(this.mongo, DATABASE_NAME));
MongoRepositoryFactoryBean<PersonRepository, Person, ObjectId> factory = new MongoRepositoryFactoryBean<PersonRepository, Person, ObjectId>();
factory.setMongoOperations(operations);
factory.setRepositoryInterface(PersonRepository.class);
factory.afterPropertiesSet();
repository = factory.getObject();
}
@Test
public void writeWithWriteConcerns() {
executeWithWriteConcerns(new WriteConcernCallback() {
public void doWithWriteConcern(String constantName, WriteConcern concern) {
writeHeadline("WriteConcern: " + constantName);
writingObjectsUsingPlainDriver("Writing %s objects using plain driver");
writingObjectsUsingMongoTemplate("Writing %s objects using template");
writingObjectsUsingRepositories("Writing %s objects using repository");
writeFooter();
}
});
}
@Test
public void writeAndRead() {
mongo.setWriteConcern(WriteConcern.SAFE);
for (int i = 3; i > 0; i--) {
setupCollections();
writeHeadline("Plain driver");
writingObjectsUsingPlainDriver("Writing %s objects using plain driver");
readingUsingPlainDriver("Reading all objects using plain driver");
queryUsingPlainDriver("Executing query using plain driver");
writeFooter();
writeHeadline("Template");
writingObjectsUsingMongoTemplate("Writing %s objects using template");
readingUsingTemplate("Reading all objects using template");
queryUsingTemplate("Executing query using template");
writeFooter();
writeHeadline("Repositories");
writingObjectsUsingRepositories("Writing %s objects using repository");
readingUsingRepository("Reading all objects using repository");
queryUsingRepository("Executing query using repository");
writeFooter();
writeFooter();
}
}
private void writeHeadline(String headline) {
System.out.println(headline);
System.out.println("---------------------------------".substring(0, headline.length()));
}
private void writeFooter() {
System.out.println();
}
private void queryUsingTemplate(String template) {
executeWatchedWithTimeAndResultSize(template, new WatchCallback<List<Person>>() {
public List<Person> doInWatch() {
Query query = query(where("addresses.zipCode").regex(".*1.*"));
return operations.find(query, Person.class, "template");
}
});
}
private void queryUsingRepository(String template) {
executeWatchedWithTimeAndResultSize(template, new WatchCallback<List<Person>>() {
public List<Person> doInWatch() {
return repository.findByAddressesZipCodeContaining("1");
}
});
}
private void executeWithWriteConcerns(WriteConcernCallback callback) {
Constants constants = new Constants(WriteConcern.class);
for (String constantName : constants.getNames(null)) {
if (IGNORED_WRITE_CONCERNS.contains(constantName)) {
continue;
}
WriteConcern writeConcern = (WriteConcern) constants.asObject(constantName);
mongo.setWriteConcern(writeConcern);
setupCollections();
callback.doWithWriteConcern(constantName, writeConcern);
}
}
private void setupCollections() {
DB db = this.mongo.getDB(DATABASE_NAME);
for (String collectionName : COLLECTION_NAMES) {
DBCollection collection = db.getCollection(collectionName);
collection.drop();
db.command(getCreateCollectionCommand(collectionName));
collection.ensureIndex(new BasicDBObject("firstname", -1));
collection.ensureIndex(new BasicDBObject("lastname", -1));
}
}
private DBObject getCreateCollectionCommand(String name) {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("createCollection", name);
dbObject.put("capped", false);
dbObject.put("size", COLLECTION_SIZE);
return dbObject;
}
private void writingObjectsUsingPlainDriver(String template) {
final DBCollection collection = mongo.getDB(DATABASE_NAME).getCollection("driver");
final List<DBObject> persons = getPersonDBObjects();
executeWatchedWithTime(template, new WatchCallback<Void>() {
public Void doInWatch() {
for (DBObject person : persons) {
collection.save(person);
}
return null;
}
});
}
private void writingObjectsUsingRepositories(String template) {
final List<Person> persons = getPersonObjects();
executeWatchedWithTime(template, new WatchCallback<Void>() {
public Void doInWatch() {
repository.save(persons);
return null;
}
});
}
private void writingObjectsUsingMongoTemplate(String template) {
final List<Person> persons = getPersonObjects();
executeWatchedWithTime(template, new WatchCallback<Void>() {
public Void doInWatch() {
for (Person person : persons) {
operations.save(person, "template");
}
return null;
}
});
}
private void readingUsingPlainDriver(String template) {
final DBCollection collection = mongo.getDB(DATABASE_NAME).getCollection("driver");
executeWatchedWithTimeAndResultSize(String.format(template, NUMBER_OF_PERSONS), new WatchCallback<List<Person>>() {
public List<Person> doInWatch() {
return toPersons(collection.find());
}
});
}
private void readingUsingTemplate(String template) {
executeWatchedWithTimeAndResultSize(String.format(template, NUMBER_OF_PERSONS), new WatchCallback<List<Person>>() {
public List<Person> doInWatch() {
return operations.findAll(Person.class, "template");
}
});
}
private void readingUsingRepository(String template) {
executeWatchedWithTimeAndResultSize(String.format(template, NUMBER_OF_PERSONS), new WatchCallback<List<Person>>() {
public List<Person> doInWatch() {
return repository.findAll();
}
});
}
private void queryUsingPlainDriver(String template) {
final DBCollection collection = mongo.getDB(DATABASE_NAME).getCollection("driver");
executeWatchedWithTimeAndResultSize(template, new WatchCallback<List<Person>>() {
public List<Person> doInWatch() {
DBObject regex = new BasicDBObject("$regex", Pattern.compile(".*1.*"));
DBObject query = new BasicDBObject("addresses.zipCode", regex);
return toPersons(collection.find(query));
}
});
}
private List<DBObject> getPersonDBObjects() {
List<DBObject> result = new ArrayList<DBObject>(NUMBER_OF_PERSONS);
for (Person person : getPersonObjects()) {
result.add(person.toDBObject());
}
return result;
}
private List<Person> getPersonObjects() {
List<Person> result = new ArrayList<Person>(NUMBER_OF_PERSONS);
watch.start("Created " + NUMBER_OF_PERSONS + " Persons");
for (int i = 0; i < NUMBER_OF_PERSONS; i++) {
Address address = new Address("zip" + i, "city" + i);
Person person = new Person("Firstname" + i, "Lastname" + i, Arrays.asList(address));
person.orders.add(new Order(LineItem.generate()));
person.orders.add(new Order(LineItem.generate()));
result.add(person);
}
watch.stop();
return result;
}
private <T> T executeWatched(String template, WatchCallback<T> callback) {
watch.start(String.format(template, NUMBER_OF_PERSONS));
try {
return callback.doInWatch();
} finally {
watch.stop();
}
}
private <T> void executeWatchedWithTime(String template, WatchCallback<?> callback) {
executeWatched(template, callback);
printStatistics(null);
}
private <T> void executeWatchedWithTimeAndResultSize(String template, WatchCallback<List<T>> callback) {
printStatistics(executeWatched(template, callback));
}
private void printStatistics(Collection<?> result) {
long time = watch.getLastTaskTimeMillis();
StringBuilder builder = new StringBuilder(watch.getLastTaskName());
if (result != null) {
builder.append(" returned ").append(result.size()).append(" results and");
}
builder.append(" took ").append(time).append(" milliseconds");
System.out.println(builder);
}
private static List<Person> toPersons(DBCursor cursor) {
List<Person> persons = new ArrayList<Person>();
while (cursor.hasNext()) {
persons.add(Person.from(cursor.next()));
}
return persons;
}
static class Person {
ObjectId id;
@Indexed
final String firstname, lastname;
final List<Address> addresses;
final Set<Order> orders;
public Person(String firstname, String lastname, List<Address> addresses) {
this.firstname = firstname;
this.lastname = lastname;
this.addresses = addresses;
this.orders = new HashSet<Order>();
}
public static Person from(DBObject source) {
BasicDBList addressesSource = (BasicDBList) source.get("addresses");
List<Address> addresses = new ArrayList<Address>(addressesSource.size());
for (Object addressSource : addressesSource) {
addresses.add(Address.from((DBObject) addressSource));
}
BasicDBList ordersSource = (BasicDBList) source.get("orders");
Set<Order> orders = new HashSet<Order>(ordersSource.size());
for (Object orderSource : ordersSource) {
orders.add(Order.from((DBObject) orderSource));
}
Person person = new Person((String) source.get("firstname"), (String) source.get("lastname"), addresses);
person.orders.addAll(orders);
return person;
}
public DBObject toDBObject() {
DBObject dbObject = new BasicDBObject();
dbObject.put("firstname", firstname);
dbObject.put("lastname", lastname);
dbObject.put("addresses", writeAll(addresses));
dbObject.put("orders", writeAll(orders));
return dbObject;
}
}
static class Address implements Convertible {
final String zipCode;
final String city;
final Set<AddressType> types;
public Address(String zipCode, String city) {
this(zipCode, city, new HashSet<AddressType>(pickRandomNumerOfItemsFrom(Arrays.asList(AddressType.values()))));
}
@PersistenceConstructor
public Address(String zipCode, String city, Set<AddressType> types) {
this.zipCode = zipCode;
this.city = city;
this.types = types;
}
public static Address from(DBObject source) {
String zipCode = (String) source.get("zipCode");
String city = (String) source.get("city");
BasicDBList types = (BasicDBList) source.get("types");
return new Address(zipCode, city, new HashSet<AddressType>(readFromBasicDBList(types, AddressType.class)));
}
public DBObject toDBObject() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("zipCode", zipCode);
dbObject.put("city", city);
dbObject.put("types", toBasicDBList(types));
return dbObject;
}
}
private static <T extends Enum<T>> List<T> readFromBasicDBList(BasicDBList source, Class<T> type) {
List<T> result = new ArrayList<T>(source.size());
for (Object object : source) {
result.add(Enum.valueOf(type, object.toString()));
}
return result;
}
private static <T extends Enum<T>> BasicDBList toBasicDBList(Collection<T> enums) {
BasicDBList result = new BasicDBList();
for (T element : enums) {
result.add(element.toString());
}
return result;
}
static class Order implements Convertible {
static enum Status {
ORDERED, PAYED, SHIPPED;
}
Date createdAt;
List<LineItem> lineItems;
Status status;
public Order(List<LineItem> lineItems, Date createdAt) {
this.lineItems = lineItems;
this.createdAt = createdAt;
this.status = Status.ORDERED;
}
@PersistenceConstructor
public Order(List<LineItem> lineItems, Date createdAt, Status status) {
this.lineItems = lineItems;
this.createdAt = createdAt;
this.status = status;
}
public static Order from(DBObject source) {
BasicDBList lineItemsSource = (BasicDBList) source.get("lineItems");
List<LineItem> lineItems = new ArrayList<PerformanceTests.LineItem>(lineItemsSource.size());
for (Object lineItemSource : lineItemsSource) {
lineItems.add(LineItem.from((DBObject) lineItemSource));
}
Date date = (Date) source.get("createdAt");
Status status = Status.valueOf((String) source.get("status"));
return new Order(lineItems, date, status);
}
public Order(List<LineItem> lineItems) {
this(lineItems, new Date());
}
public DBObject toDBObject() {
DBObject result = new BasicDBObject();
result.put("createdAt", createdAt);
result.put("lineItems", writeAll(lineItems));
result.put("status", status.toString());
return result;
}
}
static class LineItem implements Convertible {
String description;
double price;
int amount;
public LineItem(String description, int amount, double price) {
this.description = description;
this.amount = amount;
this.price = price;
}
public static List<LineItem> generate() {
LineItem iPad = new LineItem("iPad", 1, 649);
LineItem iPhone = new LineItem("iPhone", 1, 499);
LineItem macBook = new LineItem("MacBook", 2, 1299);
return pickRandomNumerOfItemsFrom(Arrays.asList(iPad, iPhone, macBook));
}
public static LineItem from(DBObject source) {
String description = (String) source.get("description");
double price = (Double) source.get("price");
int amount = (Integer) source.get("amount");
return new LineItem(description, amount, price);
}
public DBObject toDBObject() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("description", description);
dbObject.put("price", price);
dbObject.put("amount", amount);
return dbObject;
}
}
private static <T> List<T> pickRandomNumerOfItemsFrom(List<T> source) {
Assert.isTrue(!source.isEmpty());
Random random = new Random();
int numberOfItems = random.nextInt(source.size());
numberOfItems = numberOfItems == 0 ? 1 : numberOfItems;
List<T> result = new ArrayList<T>(numberOfItems);
while (result.size() < numberOfItems) {
int index = random.nextInt(source.size());
T candidate = source.get(index);
if (!result.contains(candidate)) {
result.add(candidate);
}
}
return result;
}
static enum AddressType {
SHIPPING, BILLING;
}
private interface WriteConcernCallback {
void doWithWriteConcern(String constantName, WriteConcern concern);
}
private interface WatchCallback<T> {
T doInWatch();
}
private interface PersonRepository extends MongoRepository<Person, ObjectId> {
List<Person> findByAddressesZipCodeContaining(String parameter);
}
private interface Convertible {
DBObject toDBObject();
}
private static List<DBObject> writeAll(Collection<? extends Convertible> convertibles) {
List<DBObject> result = new ArrayList<DBObject>();
for (Convertible convertible : convertibles) {
result.add(convertible.toDBObject());
}
return result;
}
}

View File

@@ -96,6 +96,14 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result.containsAll(all), is(true));
}
@Test
public void findsAllWithGivenIds() {
Iterable<Person> result = repository.findAll(Arrays.asList(dave.id, boyd.id));
assertThat(result, hasItems(dave, boyd));
assertThat(result, not(hasItems(oliver, carter, stefan, leroi, alicia)));
}
@Test
public void deletesPersonCorrectly() throws Exception {
@@ -310,16 +318,6 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(females.get(0), is(alicia));
}
/**
* @see DATAMONGO-446
*/
@Test
public void findsPeopleBySexPaginated() {
List<Person> males = repository.findBySex(Sex.MALE, new PageRequest(0, 2));
assertThat(males.size(), is(2));
}
@Test
public void findsPeopleByNamedQuery() {
List<Person> result = repository.findByNamedQuery("Dave");
@@ -413,14 +411,69 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result.get(6), is(stefan));
}
/**
* @see DATAMONGO-347
*/
@Test
public void executesQueryWithDBRefReferenceCorrectly() {
operations.remove(new org.springframework.data.mongodb.core.query.Query(), User.class);
User user = new User();
user.username = "Oliver";
operations.save(user);
dave.creator = user;
repository.save(dave);
List<Person> result = repository.findByCreator(user);
assertThat(result.size(), is(1));
assertThat(result, hasItem(dave));
}
/**
* @see DATAMONGO-425
*/
@Test
public void bindsDateParameterForDerivedQueryCorrectly() {
public void bindsDateParameterForLessThanPredicateCorrectly() {
List<Person> result = repository.findByCreatedAtLessThan(boyd.createdAt);
assertThat(result.isEmpty(), is(false));
assertThat(result.size(), is(3));
assertThat(result, hasItems(dave, oliver, carter));
}
/**
* @see DATAMONGO-425
*/
@Test
public void bindsDateParameterForGreaterThanPredicateCorrectly() {
List<Person> result = repository.findByCreatedAtGreaterThan(carter.createdAt);
assertThat(result.size(), is(4));
assertThat(result, hasItems(boyd, stefan, leroi, alicia));
}
/**
* @see DATAMONGO-427
*/
@Test
public void bindsDateParameterToBeforePredicateCorrectly() {
List<Person> result = repository.findByCreatedAtBefore(boyd.createdAt);
assertThat(result.size(), is(3));
assertThat(result, hasItems(dave, oliver, carter));
}
/**
* @see DATAMONGO-427
*/
@Test
public void bindsDateParameterForAfterPredicateCorrectly() {
List<Person> result = repository.findByCreatedAtAfter(carter.createdAt);
assertThat(result.size(), is(4));
assertThat(result, hasItems(boyd, stefan, leroi, alicia));
}
/**
@@ -432,21 +485,4 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
List<Person> result = repository.findByCreatedAtLessThanManually(boyd.createdAt);
assertThat(result.isEmpty(), is(false));
}
/**
* @see DATAMONGO-521
*/
@Test
public void executesAndQueryCorrectly() {
List<Person> result = repository.findByFirstnameAndLastname("Dave", "Matthews");
assertThat(result, hasSize(1));
assertThat(result, hasItem(dave));
result = repository.findByFirstnameAndLastname("Oliver August", "Matthews");
assertThat(result, hasSize(1));
assertThat(result, hasItem(oliver));
}
}
}

View File

@@ -21,6 +21,7 @@ import java.util.Set;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
/**
@@ -50,6 +51,9 @@ public class Person extends Contact {
private Address address;
private Set<Address> shippingAddresses;
@DBRef
User creator;
public Person() {
this(null, null);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2012 the original author or authors.
* Copyright 2002-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.
@@ -106,8 +106,6 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
*/
List<Person> findByFirstnameNotIn(Collection<String> firstnames);
List<Person> findByFirstnameAndLastname(String firstname, String lastname);
/**
* Returns all {@link Person}s with an age between the two given values.
*
@@ -149,22 +147,38 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
List<Person> findBySex(Sex sex);
List<Person> findBySex(Sex sex, Pageable pageable);
List<Person> findByNamedQuery(String firstname);
GeoResults<Person> findByLocationNear(Point point, Distance maxDistance);
GeoPage<Person> findByLocationNear(Point point, Distance maxDistance, Pageable pageable);
List<Person> findByCreator(User user);
/**
* @see DATAMONGO-425
*/
List<Person> findByCreatedAtLessThan(Date date);
/**
* @see DATAMONGO-425
*/
List<Person> findByCreatedAtGreaterThan(Date date);
/**
* @see DATAMONGO-425
*/
@Query("{ 'createdAt' : { '$lt' : ?0 }}")
List<Person> findByCreatedAtLessThanManually(Date date);
/**
* @see DATAMONGO-427
*/
List<Person> findByCreatedAtBefore(Date date);
/**
* @see DATAMONGO-427
*/
List<Person> findByCreatedAtAfter(Date date);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2012 the original author or authors.
* Copyright 2002-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.

View File

@@ -15,13 +15,11 @@
*/
package org.springframework.data.mongodb.repository;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.CollectionCallback;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -45,9 +44,11 @@ import com.mongodb.MongoException;
@ContextConfiguration
public class RepositoryIndexCreationIntegrationTests {
@Autowired MongoOperations operations;
@Autowired
MongoOperations operations;
@Autowired PersonRepository repository;
@Autowired
PersonRepository repository;
@After
public void tearDown() {
@@ -69,30 +70,21 @@ public class RepositoryIndexCreationIntegrationTests {
@Test
public void testname() {
operations.execute(Person.class, new CollectionCallback<Void>() {
@SuppressWarnings("unchecked")
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
List<DBObject> indexInfo = collection.getIndexInfo();
List<IndexInfo> indexInfo = operations.indexOps(Person.class).getIndexInfo();
assertThat(indexInfo.isEmpty(), is(false));
assertThat(indexInfo.size(), is(greaterThan(2)));
Matcher<String> lastnameIndex = startsWith("findByLastname");
Matcher<String> firstnameIndex = startsWith("findByFirstname");
Matcher<Iterable<String>> hasItems = hasItems(lastnameIndex, firstnameIndex);
assertThat(getIndexNamesFrom(indexInfo), hasItems);
return null;
}
});
assertHasIndexForField(indexInfo, "lastname");
assertHasIndexForField(indexInfo, "firstname");
}
private static List<String> getIndexNamesFrom(List<DBObject> indexes) {
List<String> result = new ArrayList<String>();
for (DBObject dbObject : indexes) {
result.add(dbObject.get("name").toString());
private static void assertHasIndexForField(List<IndexInfo> indexInfo, String... fields) {
for (IndexInfo info : indexInfo) {
if (info.isIndexForFields(Arrays.asList(fields))) {
return;
}
}
return result;
fail(String.format("Did not find index for field(s) %s in %s!", fields, indexInfo));
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.repository;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class User {
@Id
String id;
String username;
}

View File

@@ -0,0 +1,58 @@
/*
* 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.repository.cdi;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.apache.webbeans.cditest.CdiTestContainer;
import org.apache.webbeans.cditest.CdiTestContainerLoader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.data.mongodb.repository.Person;
/**
* Integration tests for {@link MongoRepositoryExtension}.
*
* @author Oliver Gierke
*/
public class CdiExtensionIntegrationTests {
static CdiTestContainer container;
@BeforeClass
public static void setUp() throws Exception {
container = CdiTestContainerLoader.getCdiContainer();
container.bootContainer();
}
@Test
public void bootstrapsRepositoryCorrectly() {
RepositoryClient client = container.getInstance(RepositoryClient.class);
PersonRepository repository = client.getRepository();
assertThat(repository, is(notNullValue()));
repository.deleteAll();
Person person = new Person("Dave", "Matthews");
Person result = repository.save(person);
assertThat(result, is(notNullValue()));
assertThat(repository.findOne(person.getId()).getId(), is(result.getId()));
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.repository.cdi;
import java.net.UnknownHostException;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
/**
* Simple component exposing a {@link MongoOperations} instance as CDI bean.
*
* @author Oliver Gierke
*/
class MongoTemplateProducer {
@Produces
@ApplicationScoped
public MongoOperations createMongoTemplate() throws UnknownHostException, MongoException {
MongoDbFactory factory = new SimpleMongoDbFactory(new Mongo(), "database");
return new MongoTemplate(factory);
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.repository.cdi;
import org.springframework.data.mongodb.repository.Person;
import org.springframework.data.repository.Repository;
public interface PersonRepository extends Repository<Person, String> {
void deleteAll();
Person save(Person person);
Person findOne(String id);
}

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