Compare commits

..

142 Commits

Author SHA1 Message Date
Mark Paluch
0ec0eaf937 DATAMONGO-2567 - Release version 2.2.9 (Moore SR9). 2020-07-22 09:55:55 +02:00
Mark Paluch
1babda2e69 DATAMONGO-2567 - Prepare 2.2.9 (Moore SR9). 2020-07-22 09:55:29 +02:00
Mark Paluch
e319ea82d0 DATAMONGO-2567 - Updated changelog. 2020-07-22 09:55:18 +02:00
Mark Paluch
63c8f63acf DATAMONGO-2566 - Updated changelog. 2020-07-22 09:44:31 +02:00
Mark Paluch
4f7d3131c5 DATAMONGO-2571 - Polishing.
Fix test method visibility.
2020-07-15 15:45:11 +02:00
Christoph Strobl
6a8609c7f8 DATAMONGO-2571 - Fix regular expression parameter binding for String-based queries.
Original pull request: #873.
2020-07-15 15:33:57 +02:00
Mark Paluch
d9477501ae DATAMONGO-2490 - Polishing.
Remove unnecessary code. Reuse session-associated collection when logging to avoid unqualified calls to MongoDbFactory.getMongoDatabase(). Create collection before transaction in test for compatibility with older MongoDB servers.

Original pull request: #875.
2020-07-15 15:17:26 +02:00
Christoph Strobl
be46540959 DATAMONGO-2490 - Fix dbref fetching during ongoing transaction.
Original pull request: #875.
2020-07-15 15:16:20 +02:00
Mark Paluch
773f20f861 DATAMONGO-2544 - Updated changelog. 2020-06-25 12:00:21 +02:00
Mark Paluch
171386baaf DATAMONGO-2570 - Polishing.
Add assertions. Use method references where possible.

Original pull request: #871.
2020-06-22 10:40:16 +02:00
Mehran Behnam
aa2716c2c0 DATAMONGO-2570 - Change type of count variable to primitive.
Avoiding unintentional unboxing.

Original pull request: #871.
2020-06-22 10:40:16 +02:00
Mark Paluch
1904a809a4 DATAMONGO-2572 - Remove usage of Oppressive Language.
Replaced blacklist with denylist and introduce meta keyword SECONDARY_READS as we no longer use MongoDB API with the initial replication concept.

Original Pull Request: #870
2020-06-17 13:49:04 +02:00
Mark Paluch
f8dddc366b DATAMONGO-2543 - Updated changelog. 2020-06-10 14:31:01 +02:00
Mark Paluch
f7bc5228ec DATAMONGO-2533 - After release cleanups. 2020-06-10 12:29:17 +02:00
Mark Paluch
4782900da5 DATAMONGO-2533 - Prepare next development iteration. 2020-06-10 12:29:14 +02:00
Mark Paluch
c011ff81b9 DATAMONGO-2533 - Release version 2.2.8 (Moore SR8). 2020-06-10 12:11:27 +02:00
Mark Paluch
cb768ce953 DATAMONGO-2533 - Prepare 2.2.8 (Moore SR8). 2020-06-10 12:11:00 +02:00
Mark Paluch
5d08c040b9 DATAMONGO-2533 - Updated changelog. 2020-06-10 12:10:46 +02:00
Mark Paluch
7a0f38d10e DATAMONGO-2532 - Updated changelog. 2020-06-10 11:22:53 +02:00
Mark Paluch
1e9ab02e3f DATAMONGO-2565 - Polishing.
Add unit test to verify behavior. Cleanup code.

Original pull request: #869.
2020-06-10 10:16:03 +02:00
BraveLeeLee
5e378bd21b DATAMONGO-2565 - Evaluate correct expression when obtaining collation from MongoPersistentEntity.
Original pull request: #869.
2020-06-10 10:15:29 +02:00
Mark Paluch
a2e5009f42 DATAMONGO-2542 - Polishing.
Fix nullable annotation.

Original pull request: #863.
2020-05-26 10:32:43 +02:00
Christoph Strobl
44cc0dad94 DATAMONGO-2542 - Shortcut PersistentPropertyPath resolution during query mapping.
By shortcutting the path resolution we avoid checking keywords like $in against a potential path expression.

Original pull request: #863.
2020-05-26 10:32:43 +02:00
Mark Paluch
68de8e57be DATAMONGO-2545 - Polishing.
Fix warnings and typos.

Original pull request: #864.
2020-05-26 10:24:31 +02:00
Christoph Strobl
fde59411da DATAMONGO-2545 - Fix full Query Document binding resulting from SpEL.
We reenabled annotated queries using a SpEL expression resulting in the actual query document.

Original pull request: #864.
2020-05-26 10:18:46 +02:00
Christoph Strobl
7d48dd0ce4 DATAMONGO-2545 - Fix regression in String query SpEL parameter binding.
We reenabled parameter binding within SpEL using query parameter placeholders ?0, ?1,... instead of their array index [0],[1],...

Original pull request: #864.
2020-05-26 10:18:44 +02:00
Christoph Strobl
699a7a00f5 DATAMONGO-2547 - Use target class ClassLoader instead of default CL when creating proxy instances.
Original pull request: #865.
2020-05-26 08:50:30 +02:00
Mark Paluch
16d55c8973 DATAMONGO-2509 - Add missing Query constructor in reference documentation.
Original pull request: #846.
2020-05-06 11:52:53 +02:00
Christoph Strobl
be0b9415b4 DATAMONGO-2509 - Update Javadoc.
Original pull request: #853.
2020-05-06 11:41:25 +02:00
Greg Turnquist
93df58a8d5 DATAMONGO-2535 - Remove Travis CI. 2020-05-04 15:09:18 -05:00
Mark Paluch
999e24902c DATAMONGO-2503 - Updated changelog. 2020-04-28 15:12:28 +02:00
Mark Paluch
d19208abec DATAMONGO-2500 - After release cleanups. 2020-04-28 14:45:30 +02:00
Mark Paluch
8f54676b97 DATAMONGO-2500 - Prepare next development iteration. 2020-04-28 14:45:29 +02:00
Mark Paluch
61bbc9ab7f DATAMONGO-2500 - Release version 2.2.7 (Moore SR7). 2020-04-28 14:35:55 +02:00
Mark Paluch
9e802a59c7 DATAMONGO-2500 - Prepare 2.2.7 (Moore SR7). 2020-04-28 14:35:24 +02:00
Mark Paluch
38e1e632a7 DATAMONGO-2500 - Updated changelog. 2020-04-28 14:35:16 +02:00
Mark Paluch
89cf78cc4a DATAMONGO-2484 - Updated changelog. 2020-04-28 11:59:03 +02:00
Mark Paluch
cecd47d679 DATAMONGO-2529 - Ensure that MappingMongoConverter.read(…) is never called with null.
Previously, various methods attempted to pass a null argument as source for the converter. The API is non-null and implementations relying on these constraints were easily breakable.

We now make sure that the source is never null.
2020-04-23 15:25:45 +02:00
Mark Paluch
ed35e577af DATAMONGO-2504 - Polishing.
Update equals/hashCode implementation to use the Spring Data form. Make fields final where possible. Use diamond syntax. Reorder methods. Reformat code. Extend tests.

Original pull request: #848.
2020-04-23 15:25:45 +02:00
ddebray
f54cf40eda DATAMONGO-2504 - Add hashCode and equals to TextCriteria and Term.
Original pull request: #848.
2020-04-23 12:29:07 +02:00
Christoph Strobl
5314e6f8bb DATAMONGO-2513 - Fix Eq aggregation operator comparing collection values.
Original pull request: #855.
2020-04-22 11:47:02 +02:00
Mark Paluch
b7b2709177 DATAMONGO-2523 - Polishing.
Reformat code.

Original pull request: #859.
2020-04-22 10:08:33 +02:00
Christoph Strobl
34c47e84c0 DATAMONGO-2523 - Fix Json binding of SpEL expressions in arrays.
The closing bracket must not have a leading whitespace.

Original pull request: #859.
2020-04-22 10:08:30 +02:00
Mark Paluch
f7d91184a0 DATAMONGO-2517 - Polishing.
Reformat code.

Original pull request: #857.
2020-04-21 16:02:36 +02:00
Christoph Strobl
eeddc860f7 DATAMONGO-2517 - Fix invalid entity creation for text queries.
Fix a glitch in the MappingMongoConverter that uses the single String argument constructor (since it matches in type and parameter count to the given input string) to falsely instantiate an Entity when it should not.

Original pull request: #857.
2020-04-21 16:02:24 +02:00
Christoph Strobl
bcefdd209b DATAMONGO-2506 - Provide meaningful error message when using unsupported return type in repository aggregation method.
We improved the error message for unsupported return types instead of running into an IllegalArgumentException for unique results.

Original pull request: #851.
2020-04-07 14:59:30 +02:00
Mark Paluch
3f1fea2d19 DATAMONGO-2502 - Polishing.
Extend tests. Fix generics. Consistently use compiled patterns for positional placeholder removal.

Original pull request: #847.
2020-04-07 14:47:42 +02:00
Christoph Strobl
665322a69a DATAMONGO-2502 - Fix nested array path mapping for updates.
Original pull request: #847.
2020-04-07 14:17:05 +02:00
Mark Paluch
3e59bc3b38 DATAMONGO-2492 - Updated changelog. 2020-03-31 15:08:52 +02:00
Mark Paluch
1752931dde DATAMONGO-2485 - After release cleanups. 2020-03-25 10:58:27 +01:00
Mark Paluch
4b9bae1656 DATAMONGO-2485 - Prepare next development iteration. 2020-03-25 10:58:26 +01:00
Mark Paluch
74c08fa8aa DATAMONGO-2485 - Release version 2.2.6 (Moore SR6). 2020-03-25 10:46:02 +01:00
Mark Paluch
628aad8f64 DATAMONGO-2485 - Prepare 2.2.6 (Moore SR6). 2020-03-25 10:45:35 +01:00
Mark Paluch
39c8672e6d DATAMONGO-2485 - Updated changelog. 2020-03-25 10:45:28 +01:00
Christoph Strobl
620991ddee DATAMONGO-2300 - Polishing.
Move null check to event publishing logic.

Original Pull Request: #763
2020-03-23 10:11:41 +01:00
Heesu Jung
ba8f28f623 DATAMONGO-2300 - Add check rawType is null in readMap.
Original Pull Request: #763
2020-03-23 10:01:50 +01:00
Mark Paluch
6389055d3a DATAMONGO-2497 - Update documentation regarding @Transient properties usage in the persistence constructor. 2020-03-19 15:37:37 +01:00
Christoph Strobl
4465ed9819 DATAMONGO-2445 - Deprecate ReactiveGridFsOperations using AsyncInputStream.
Methods using AsyncInputStream will be removed in 3.0. Please use the ones accepting a Publisher.

Original pull request: #843.
2020-03-19 09:42:49 +01:00
Mark Paluch
8dc97e5d01 DATAMONGO-2488 - Polishing.
Simplify conditional entity check.

Original pull request: #841.
2020-03-11 14:38:17 +01:00
Christoph Strobl
a037c50961 DATAMONGO-2488 - Fix nested array path field name mapping.
Original pull request: #841.
2020-03-11 14:38:16 +01:00
Jens Schauder
28d5f02e15 DATAMONGO-2473 - Updated changelog. 2020-03-11 09:59:36 +01:00
Mark Paluch
e65a353fc4 DATAMONGO-2453 - After release cleanups. 2020-02-26 11:54:09 +01:00
Mark Paluch
42400e7836 DATAMONGO-2453 - Prepare next development iteration. 2020-02-26 11:54:08 +01:00
Mark Paluch
cdd7a2008b DATAMONGO-2453 - Release version 2.2.5 (Moore SR5). 2020-02-26 11:38:17 +01:00
Mark Paluch
c2b80fddd8 DATAMONGO-2453 - Prepare 2.2.5 (Moore SR5). 2020-02-26 11:37:58 +01:00
Mark Paluch
8eaa8119e6 DATAMONGO-2453 - Updated changelog. 2020-02-26 11:37:53 +01:00
Mark Paluch
5fccadd41e DATAMONGO-2452 - Updated changelog. 2020-02-26 11:31:49 +01:00
Christoph Strobl
5d0ab340e3 DATAMONGO-2478 - Fix NPE in Query.of when given a proxied source.
Original pull request: #836.
2020-02-24 11:34:36 +01:00
Christoph Strobl
5d7e9199de DATAMONGO-2476 - Fix Json parsing for unquoted placeholders in arrays.
Original pull request: #835.
2020-02-24 11:07:56 +01:00
Mark Paluch
5e2c65a650 DATAMONGO-2456 - Updated changelog. 2020-02-12 15:05:04 +01:00
Mark Paluch
1f5553d2d8 DATAMONGO-2079 - MappingMongoConverter no longer implements ValueResolver.
MappingMongoConverter no longer implements a package-private interface so that converter instances can be proxied.

Original Pull Request: #832
2020-02-04 14:52:43 +01:00
Mark Paluch
40d5ab050f DATAMONGO-2464 - Polishing.
Apply fix also to reactive MongoDB repository documentation.

Original pull request: #816.
2020-02-03 11:33:42 +01:00
LiangYong
1629ba11b2 DATAMONGO-2464 - Fix code examples in reference documentation.
fixed repository miss "{" issue.

Original pull request: #816.
2020-02-03 11:31:33 +01:00
Mark Paluch
6e94f138d5 DATAMONGO-2460 - Polishing.
Reformat code. Use diamond syntax.

Original pull request: #830.
2020-02-03 11:25:36 +01:00
Christoph Strobl
1b7273db42 DATAMONGO-2460 - Fix target type computation for complex id properties with @Field annotation.
We now set the target type to org.bson.Document for id properties annotated with @Field having the implicit target type derived from the annotation. Along the lines we fixed warn message when an id property with explicit (unsupported) field name is detected.

Original pull request: #830.
2020-02-03 11:25:36 +01:00
Mark Paluch
8857903831 DATAMONGO-2406 - Polishing.
Add optimization for Mono.

Original pull request: #825.
2020-01-29 09:58:14 +01:00
Christoph Strobl
69cacb5fe3 DATAMONGO-2406 - Derived reactive deleteBy query execution should allow Mono<Void> result.
Mono<Void> is now a supported return type of derived reactive deleteBy queries like:

    Mono<Void> deleteByLastname(String lastname);

Original pull request: #825.
2020-01-29 09:58:14 +01:00
Mark Paluch
ba4b958114 DATAMONGO-2457 - Polishing.
Slightly tweak wording.

Original pull request: #829.
2020-01-29 09:52:02 +01:00
Christoph Strobl
6d971ef2c8 DATAMONGO-2459 - Add sample for passing on limit and offset using reactive repositories.
Original pull request: #829.
2020-01-29 09:46:43 +01:00
Christoph Strobl
03ff37db92 DATAMONGO-2457 - Fix id type explanation in repository documentation.
Original pull request: #829.
2020-01-29 09:46:41 +01:00
Mark Paluch
428126ef75 DATAMONGO-2454 - Updated changelog. 2020-01-17 09:58:36 +01:00
Mark Paluch
aee242c52a DATAMONGO-2383 - Updated changelog. 2020-01-16 16:12:40 +01:00
Mark Paluch
1299f78e80 DATAMONGO-2432 - After release cleanups. 2020-01-15 12:48:23 +01:00
Mark Paluch
fc3d13d5bc DATAMONGO-2432 - Prepare next development iteration. 2020-01-15 12:48:22 +01:00
Mark Paluch
6d2f7b0c9e DATAMONGO-2432 - Release version 2.2.4 (Moore SR4). 2020-01-15 12:37:44 +01:00
Mark Paluch
84128cab28 DATAMONGO-2432 - Prepare 2.2.4 (Moore SR4). 2020-01-15 12:37:18 +01:00
Mark Paluch
8f2d2784f8 DATAMONGO-2432 - Updated changelog. 2020-01-15 12:37:09 +01:00
Christoph Strobl
37ee677a67 DATAMONGO-2451 - Fix value conversion for id properties used in sort expression.
Previously we falsely converted the sort value (1/-1) into the id types target value when a Field annotation had been present.

Original pull request: #822.
2020-01-15 11:52:06 +01:00
Mark Paluch
1729744a9e DATAMONGO-2431 - Updated changelog. 2020-01-15 10:36:38 +01:00
Mark Paluch
b59903c890 DATAMONGO-2450 - Polishing.
Replace stream with for-loop.

Original pull request: #820.
2020-01-15 10:15:23 +01:00
Christoph Strobl
5063e68562 DATAMONGO-2450 - Apply array filters to bulk write.
Original pull request: #820.
2020-01-15 10:15:23 +01:00
Christoph Strobl
3cbb572c7c DATAMONGO-2442 - Fix thenValueOf in $cond builder.
We now create a field reference when calling the builder instead of using the value as is.

Original pull request: #818.
2020-01-08 15:18:47 +01:00
Mark Paluch
bb4f11a239 DATAMONGO-2440 - Polishing.
Iterate over List instead of using the Stream API.

Original pull request: #817.
2020-01-08 15:18:47 +01:00
Christoph Strobl
8a1750691b DATAMONGO-2440 - Fix field target type conversion for collection values.
We now preserve the collection nature of the source type when applying custom target type conversions. Prior to this change collection values had been changed to single element causing query errors in MongoDB when using $in queries.

Original pull request: #817.
2020-01-08 15:18:47 +01:00
Christoph Strobl
5cffaf9d73 DATAMONGO-2423 - Polishing.
Update nullability annotations and reduce visibility in tests.

Original pull request: #815.
2020-01-08 13:25:07 +01:00
Christoph Strobl
a9e50b28df DATAMONGO-2423 - Nullability refinements for Update.
Ease non null restrictions for operators that may use null values like $set.

Original pull request: #815.
2020-01-08 13:24:50 +01:00
Mark Paluch
24512639fb DATAMONGO-2444 - Update copyright years to 2020. 2020-01-07 08:59:35 +01:00
Jens Schauder
1355d043b6 DATAMONGO-2422 - After release cleanups. 2019-12-04 14:29:17 +01:00
Jens Schauder
c7600ef9ed DATAMONGO-2422 - Prepare next development iteration. 2019-12-04 14:29:14 +01:00
Jens Schauder
a4cb0d6432 DATAMONGO-2422 - Release version 2.2.3 (Moore SR3). 2019-12-04 14:12:24 +01:00
Jens Schauder
437368bedb DATAMONGO-2422 - Prepare 2.2.3 (Moore SR3). 2019-12-04 14:11:44 +01:00
Jens Schauder
4057b193d6 DATAMONGO-2422 - Updated changelog. 2019-12-04 14:11:36 +01:00
Jens Schauder
89b1b5c7f6 DATAMONGO-2421 - Updated changelog. 2019-12-04 12:09:48 +01:00
Mark Paluch
60d3438277 DATAMONGO-2430 - Upgrade to mongo-java-driver 3.11.2. 2019-12-04 11:42:04 +01:00
Mark Paluch
a578a10b5b DATAMONGO-2418 - Polishing.
Reformat code.

Original pull request: #814.
2019-12-04 10:31:33 +01:00
Christoph Strobl
5cf24af00b DATAMONGO-2418 - Obtain EvaluationContextExtensions lazily when parsing Bson queries.
An eager evaluation of the context extension can lead to errors when e.g. the security context was not present.

Original pull request: #814.
2019-12-04 10:31:33 +01:00
Mark Paluch
6c0e455146 DATAMONGO-2410 - Polishing.
Simplify cast. Extend test with DBObject interface.

Original pull request: #813.
2019-12-04 08:56:54 +01:00
Christoph Strobl
932a946868 DATAMONGO-2410 - Fix Document to BasicDBObject conversion.
Original pull request: #813.
2019-12-04 08:56:54 +01:00
Mark Paluch
99a1cfbff9 DATAMONGO-2402 - After release cleanups. 2019-11-18 12:42:05 +01:00
Mark Paluch
d7bbdde1e7 DATAMONGO-2402 - Prepare next development iteration. 2019-11-18 12:42:04 +01:00
Mark Paluch
7dba98dce8 DATAMONGO-2402 - Release version 2.2.2 (Moore SR2). 2019-11-18 12:32:12 +01:00
Mark Paluch
c1ae30bd82 DATAMONGO-2402 - Prepare 2.2.2 (Moore SR2). 2019-11-18 12:31:53 +01:00
Mark Paluch
6aa5aea424 DATAMONGO-2402 - Updated changelog. 2019-11-18 12:31:44 +01:00
Mark Paluch
bc1b00813c DATAMONGO-2401 - Updated changelog. 2019-11-18 12:16:28 +01:00
Mark Paluch
d1ad3ab301 DATAMONGO-2414 - Polishing.
Use longer timeout to cater for slower CI environments.

Original Pull Request: #807
2019-11-14 11:54:43 +01:00
Mark Paluch
923134bbdc DATAMONGO-2414 - Guard drain loop in AsyncInputStreamHandler with state switch.
We now use a non-blocking state switch to determine whether to invoke drainLoop(…) from Subscriber completion.

Previously, we relied on same thread identification assuming if the subscription thread and the completion thread were the same, that we're already running inside the drain loop.
It turns out that a I/O thread could also run in event-loop mode where subscription and completion happens on the same thread but in between there's some processing and so the the call to completion is a delayed signal and not being called on the same stack as drainLoop(…).
The same-thread assumption was in place to avoid StackOverflow caused by infinite recursions.

We now use a state lock to enter the drain loop. Any concurrent attempts to re-enter the drain loop in Subscriber completion is now prevented to make sure that we continue draining while not causing stack recursions.

Original Pull Request: #807
2019-11-14 11:54:30 +01:00
Mark Paluch
e211f69df5 DATAMONGO-2409 - Polishing.
Adapt also ExecutableFindOperation.DistinctWithProjection.asType() to return the appropriate TerminatingDistinct.

Original pull request: #805.
2019-11-11 10:22:37 +01:00
Christoph Strobl
fc35d706a0 DATAMONGO-2409 - Fix return type of Kotlin extension function for ReactiveFindOperation.DistinctWithProjection.asType().
Original pull request: #805.
2019-11-11 10:22:37 +01:00
Mark Paluch
82894e6aff DATAMONGO-2403 - Polishing.
Use handle(…) to skip values instead of flatMap(…) to reduce overhead.

Original pull request: #804.
2019-11-08 13:51:20 +01:00
Christoph Strobl
7356f157bb DATAMONGO-2403 - Fix aggregation simple type result retrieval from empty document.
Projections used within an aggregation pipeline can result in empty documents emitted by the driver. We now guarded those cases and skip those documents within a Flux or simply return an empty Mono depending on the methods signature.

Original pull request: #804.
2019-11-08 13:48:10 +01:00
Christoph Strobl
783fc6268a DATAMONGO-2382 - After release cleanups. 2019-11-04 15:34:27 +01:00
Christoph Strobl
360b17f299 DATAMONGO-2382 - Prepare next development iteration. 2019-11-04 15:34:26 +01:00
Christoph Strobl
2cfcdaff7c DATAMONGO-2382 - Release version 2.2.1 (Moore SR1). 2019-11-04 14:55:02 +01:00
Christoph Strobl
9d9cf46e47 DATAMONGO-2382 - Prepare 2.2.1 (Moore SR1). 2019-11-04 14:54:16 +01:00
Christoph Strobl
98661cf9a2 DATAMONGO-2382 - Updated changelog. 2019-11-04 14:54:10 +01:00
Christoph Strobl
cc50cd5e3a DATAMONGO-2381 - Updated changelog. 2019-11-04 10:34:52 +01:00
Christoph Strobl
d8399d2d23 DATAMONGO-2393 - Remove capturing lambdas and extract methods.
Original Pull Request: #799
2019-10-31 12:57:06 +01:00
Mark Paluch
f2134fb2f8 DATAMONGO-2393 - Support configurable chunk size.
We now allow consuming GridFS files using a configurable chunk size. The default chunk size is now 256kb.

Original Pull Request: #799
2019-10-31 12:56:59 +01:00
Mark Paluch
ec3ccc004e DATAMONGO-2393 - Polishing.
Extract read requests into inner class.

Original Pull Request: #799
2019-10-31 12:56:50 +01:00
Mark Paluch
6cb246c18a DATAMONGO-2393 - Fix BufferOverflow in GridFS upload.
AsyncInputStreamAdapter now properly splits and buffers incoming DataBuffers according the read requests of AsyncInputStream.read(…) calls.
Previously, the adapter used the input buffer size to be used as the output buffer size. A larger DataBuffer than the transfer buffer handed in through read(…) caused a BufferOverflow.

Original Pull Request: #799
2019-10-31 12:56:42 +01:00
Mark Paluch
e73cea0ecf DATAMONGO-2393 - Use drain loop for same-thread processing in GridFS download stream.
We now rely on an outer drain-loop when GridFS reads complete on the same thread instead of using recursive subscriptions to avoid StackOverflow. Previously, we recursively invoked subscriptions that lead to an increased stack size.

Original Pull Request: #799
2019-10-31 12:56:28 +01:00
Christoph Strobl
c69e185a2a DATAMONGO-2399 - Upgrade to MongoDB Java Driver 3.11.1 2019-10-30 10:49:43 +01:00
Mark Paluch
5789f59222 DATAMONGO-2388 - Polishing.
Use StringJoiner to create comma-delimited String. Add nullability annotations.

Original pull request: #797.
2019-10-28 10:55:58 +01:00
Christoph Strobl
5178eeb340 DATAMONGO-2388 - Fix CodecConfigurationException when reading index info that contains DbRef.
Provide the default CodecRegistry when converting partial index data to its String representation used in IndexInfo.

Original pull request: #797.
2019-10-28 10:47:57 +01:00
Mark Paluch
bc5e7fa4a2 DATAMONGO-2394 - Polishing.
Reformat code.

Original pull request: #798.
2019-10-28 09:32:14 +01:00
Christoph Strobl
c28ace6d40 DATAMONGO-2394 - Fix distance conversion for derived finder using near along with GeoJSON.
GeoJson requries the distance to be in meters instead of radians, so we now make sure to convert it correctly

Original pull request: #798.
2019-10-28 09:32:10 +01:00
Mark Paluch
de4fae37e1 DATAMONGO-2392 - Polishing.
Add author tags. Move integration tests to existing test class.
Apply more appropriate in existing tests assertions. Use diamond syntax.

Original pull request: #796.
2019-10-16 13:55:12 +02:00
Mark Paluch
2f1aff3ec3 DATAMONGO-2392 - Consistently use GridFS file Id instead of ObjectId.
We now consistently use GridFSFile.getId() to allow custom Id usage instead of enforcing the Id to be an ObjectId. Using the native Id allows interaction with files that use a custom Id type.

Original pull request: #796.
2019-10-16 13:55:12 +02:00
Nick Stolwijk
6970f934bd DATAMONGO-2392 - Fix handling in ReactiveGridFsTemplate of GridFS files with custom id type.
Original pull request: #796.
2019-10-16 13:55:12 +02:00
Greg Turnquist
6b5168e102 DATAMONGO-2334 - Create CI job. 2019-09-30 14:36:06 -05:00
Mark Paluch
4420edb4dc DATAMONGO-2334 - After release cleanups. 2019-09-30 16:17:54 +02:00
Mark Paluch
c2fae95fee DATAMONGO-2334 - Prepare next development iteration. 2019-09-30 16:17:53 +02:00
685 changed files with 10696 additions and 26910 deletions

27
CODE_OF_CONDUCT.adoc Normal file
View File

@@ -0,0 +1,27 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at spring-code-of-conduct@pivotal.io.
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
This Code of Conduct is adapted from the https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/].

115
Jenkinsfile vendored
View File

@@ -3,7 +3,7 @@ pipeline {
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/master", threshold: hudson.model.Result.SUCCESS)
upstream(upstreamProjects: "spring-data-commons/2.2.x", threshold: hudson.model.Result.SUCCESS)
}
options {
@@ -30,51 +30,51 @@ pipeline {
}
}
}
stage('Publish JDK 8 + MongoDB 4.1') {
when {
changeset "ci/openjdk8-mongodb-4.1/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-openjdk8-with-mongodb-4.1", "ci/openjdk8-mongodb-4.1/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
}
}
}
stage('Publish JDK 8 + MongoDB 4.2') {
when {
changeset "ci/openjdk8-mongodb-4.2/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
when {
changeset "ci/openjdk8-mongodb-4.2/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-openjdk8-with-mongodb-4.2.0", "ci/openjdk8-mongodb-4.2/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
}
}
}
stage('Publish JDK 14 + MongoDB 4.2') {
when {
changeset "ci/openjdk14-mongodb-4.2/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-openjdk14-with-mongodb-4.2.0", "ci/openjdk14-mongodb-4.2/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
}
}
}
steps {
script {
def image = docker.build("springci/spring-data-openjdk8-with-mongodb-4.2", "ci/openjdk8-mongodb-4.2/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
}
}
}
}
}
stage("test: baseline (jdk8)") {
stage("test: baseline") {
when {
anyOf {
branch 'master'
branch '2.2.x'
not { triggeredBy 'UpstreamCause' }
}
}
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.2.0:latest'
image 'springci/spring-data-openjdk8-with-mongodb-4.2:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
@@ -83,23 +83,23 @@ pipeline {
steps {
sh 'rm -rf ?'
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'mongod --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -U -B'
}
}
stage("Test other configurations") {
when {
allOf {
branch 'master'
anyOf {
branch '2.2.x'
not { triggeredBy 'UpstreamCause' }
}
}
parallel {
stage("test: mongodb 4.0 (jdk8)") {
stage("test: mongodb 4.0") {
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.0:latest'
@@ -111,18 +111,17 @@ pipeline {
steps {
sh 'rm -rf ?'
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'mongod --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -U -B'
}
}
stage("test: mongodb 4.2 (jdk8)") {
stage("test: mongodb 4.1") {
agent {
docker {
image 'springci/spring-data-openjdk8-with-mongodb-4.2.0:latest'
image 'springci/spring-data-openjdk8-with-mongodb-4.1:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
@@ -131,31 +130,11 @@ pipeline {
steps {
sh 'rm -rf ?'
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'mongod --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
}
}
stage("test: baseline (jdk14)") {
agent {
docker {
image 'springci/spring-data-openjdk14-with-mongodb-4.2.0:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
}
options { timeout(time: 30, unit: 'MINUTES') }
steps {
sh 'rm -rf ?'
sh 'mkdir -p /tmp/mongodb/db /tmp/mongodb/log'
sh 'mongod --setParameter transactionLifetimeLimitSeconds=90 --setParameter maxTransactionLockRequestTimeoutMillis=10000 --dbpath /tmp/mongodb/db --replSet rs0 --fork --logpath /tmp/mongodb/log/mongod.log &'
sh 'sleep 10'
sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"'
sh 'sleep 15'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Duser.name=jenkins -Dsort -U -B'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -U -B'
}
}
}
@@ -164,7 +143,7 @@ pipeline {
stage('Release to artifactory') {
when {
anyOf {
branch 'master'
branch '2.2.x'
not { triggeredBy 'UpstreamCause' }
}
}
@@ -196,7 +175,7 @@ pipeline {
stage('Publish documentation') {
when {
branch 'master'
branch '2.2.x'
}
agent {
docker {

View File

@@ -10,7 +10,7 @@ Key functional areas of Spring Data MongoDB are a POJO centric model for interac
== Code of Conduct
This project is governed by the https://github.com/spring-projects/.github/blob/e3cc2ff230d8f1dca06535aa6b5a4a23815861d4/CODE_OF_CONDUCT.md[Spring Code of Conduct]. By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
This project is governed by the link:CODE_OF_CONDUCT.adoc[Spring Code of Conduct]. By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
== Getting Started
@@ -50,7 +50,12 @@ public class MyService {
@Configuration
@EnableMongoRepositories
class ApplicationConfig extends AbstractMongoClientConfiguration {
class ApplicationConfig extends AbstractMongoConfiguration {
@Override
public MongoClient mongoClient() {
return new MongoClient();
}
@Override
protected String getDatabaseName() {
@@ -89,143 +94,6 @@ If you'd rather like the latest snapshots of the upcoming major version, use our
</repository>
----
== Upgrading from 2.x
The 4.0 MongoDB Java Driver does no longer support certain features that have already been deprecated in one of the last minor versions.
Some of the changes affect the initial setup configuration as well as compile/runtime features. We summarized the most typical changes one might encounter.
=== XML Namespace
.Changed XML Namespace Elements and Attributes:
|===
Element / Attribute | 2.x | 3.x
| `<mongo:mongo-client />`
| Used to create a `com.mongodb.MongoClient`
| Now exposes a `com.mongodb.client.MongoClient`
| `<mongo:mongo-client replica-set="..." />`
| Was a comma delimited list of replica set members (host/port)
| Now defines the replica set name. +
Use `<mongo:client-settings cluster-hosts="..." />` instead
| `<mongo:db-factory writeConcern="..." />`
| NONE, NORMAL, SAFE, FSYNC_SAFE, REPLICAS_SAFE, MAJORITY
| W1, W2, W3, UNAKNOWLEDGED, AKNOWLEDGED, JOURNALED, MAJORITY
|===
.Removed XML Namespace Elements and Attributes:
|===
Element / Attribute | Replacement in 3.x | Comment
| `<mongo:db-factory mongo-ref="..." />`
| `<mongo:db-factory mongo-client-ref="..." />`
| Referencing a `com.mongodb.client.MongoClient`.
| `<mongo:mongo-client credentials="..." />`
| `<mongo:mongo-client credential="..." />`
| Single authentication data instead of list.
| `<mongo:client-options />`
| `<mongo:client-settings />`
| See `com.mongodb.MongoClientSettings` for details.
|===
.New XML Namespace Elements and Attributes:
|===
Element | Comment
| `<mongo:db-factory mongo-client-ref="..." />`
| Replacement for `<mongo:db-factory mongo-ref="..." />`
| `<mongo:db-factory connection-string="..." />`
| Replacement for `uri` and `client-uri`.
| `<mongo:mongo-client connection-string="..." />`
| Replacement for `uri` and `client-uri`.
| `<mongo:client-settings />`
| Namespace element for `com.mongodb.MongoClientSettings`.
|===
=== Java Configuration
.Java API changes
|===
Type | Comment
| `MongoClientFactoryBean`
| Creates `com.mongodb.client.MongoClient` instead of `com.mongodb.MongoClient` +
Uses `MongoClientSettings` instead of `MongoClientOptions`.
| `MongoDataIntegrityViolationException`
| Uses `WriteConcernResult` instead of `WriteResult`.
| `BulkOperationException`
| Uses `MongoBulkWriteException` and `com.mongodb.bulk.BulkWriteError` instead of `BulkWriteException` and `com.mongodb.BulkWriteError`
| `ReactiveMongoClientFactoryBean`
| Uses `com.mongodb.MongoClientSettings` instead of `com.mongodb.async.client.MongoClientSettings`
| `ReactiveMongoClientSettingsFactoryBean`
| Now produces `com.mongodb.MongoClientSettings` instead of `com.mongodb.async.client.MongoClientSettings`
|===
.Removed Java API:
|===
2.x | Replacement in 3.x | Comment
| `MongoClientOptionsFactoryBean`
| `MongoClientSettingsFactoryBean`
| Creating a `com.mongodb.MongoClientSettings`.
| `AbstractMongoConfiguration`
| `AbstractMongoClientConfiguration` +
(Available since 2.1)
| Using `com.mongodb.client.MongoClient`.
| `MongoDbFactory#getLegacyDb()`
| -
| -
| `SimpleMongoDbFactory`
| `SimpleMongoClientDbFactory` +
(Available since 2.1)
|
| `MapReduceOptions#getOutputType()`
| `MapReduceOptions#getMapReduceAction()`
| Returns `MapReduceAction` instead of `MapReduceCommand.OutputType`.
| `Meta\|Query` maxScan & snapshot
|
|
|===
=== Other Changes
==== UUID Types
The MongoDB UUID representation can now be configured with different formats.
This has to be done via `MongoClientSettings` as shown in the snippet below.
.UUID Codec Configuration
====
[source,java]
----
static class Config extends AbstractMongoClientConfiguration {
@Override
public void configureClientSettings(MongoClientSettings.Builder builder) {
builder.uuidRepresentation(UuidRepresentation.STANDARD);
}
// ...
}
----
====
== Getting Help
Having trouble with Spring Data? Wed love to help!

View File

@@ -1,15 +0,0 @@
FROM adoptopenjdk/openjdk11:latest
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv e162f504a20cdf15827f718d4b7c549a058f8b6b ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.2.list; \
echo ${TZ} > /etc/timezone;
RUN apt-get update ; \
apt-get install -y mongodb-org=4.2.0 mongodb-org-server=4.2.0 mongodb-org-shell=4.2.0 mongodb-org-mongos=4.2.0 mongodb-org-tools=4.2.0 ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;

View File

@@ -1,15 +0,0 @@
FROM adoptopenjdk/openjdk14:latest
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv e162f504a20cdf15827f718d4b7c549a058f8b6b ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.2.list; \
echo ${TZ} > /etc/timezone;
RUN apt-get update ; \
apt-get install -y mongodb-org=4.2.0 mongodb-org-server=4.2.0 mongodb-org-shell=4.2.0 mongodb-org-mongos=4.2.0 mongodb-org-tools=4.2.0 ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;

View File

@@ -1,15 +1,14 @@
FROM adoptopenjdk/openjdk8:latest
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2
RUN RUN set -eux; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4 ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.0.list; \
echo ${TZ} > /etc/timezone;
RUN apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
RUN apt-get update ; \
apt-get install -y mongodb-org=4.0.14 mongodb-org-server=4.0.14 mongodb-org-shell=4.0.14 mongodb-org-mongos=4.0.14 mongodb-org-tools=4.0.14 ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;
RUN echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.0.list
RUN apt-get update
RUN apt-get install -y mongodb-org=4.0.9 mongodb-org-server=4.0.9 mongodb-org-shell=4.0.9 mongodb-org-mongos=4.0.9 mongodb-org-tools=4.0.9
RUN apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -0,0 +1,14 @@
FROM adoptopenjdk/openjdk8:latest
RUN apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2
RUN apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv 4B7C549A058F8B6B
RUN echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.1 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.1.list
RUN apt-get update
RUN apt-get install -y mongodb-org-unstable=4.1.13 mongodb-org-unstable-server=4.1.13 mongodb-org-unstable-shell=4.1.13 mongodb-org-unstable-mongos=4.1.13 mongodb-org-unstable-tools=4.1.13
RUN apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View File

@@ -1,15 +1,14 @@
FROM adoptopenjdk/openjdk8:latest
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2
RUN set -eux; \
apt-get update && apt-get install -y apt-transport-https apt-utils gnupg2 ; \
apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv e162f504a20cdf15827f718d4b7c549a058f8b6b ; \
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.2.list; \
echo ${TZ} > /etc/timezone;
RUN apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv e162f504a20cdf15827f718d4b7c549a058f8b6b
RUN apt-get update ; \
apt-get install -y mongodb-org=4.2.0 mongodb-org-server=4.2.0 mongodb-org-shell=4.2.0 mongodb-org-mongos=4.2.0 mongodb-org-tools=4.2.0 ; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*;
RUN echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.2.list
RUN apt-get update
RUN apt-get install -y mongodb-org=4.2.0 mongodb-org-server=4.2.0 mongodb-org-shell=4.2.0 mongodb-org-mongos=4.2.0 mongodb-org-tools=4.2.0
RUN apt-get clean \
&& rm -rf /var/lib/apt/lists/*

26
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-STATIC-METADATA-SNAPSHOT</version>
<version>2.2.9.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.4.0-SNAPSHOT</version>
<version>2.2.9.RELEASE</version>
</parent>
<modules>
@@ -26,9 +26,9 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.4.0-BUILD-TIME-DOMAIN-TYPE-METADATA-SNAPSHOT</springdata.commons>
<mongo>4.1.0</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<springdata.commons>2.2.9.RELEASE</springdata.commons>
<mongo>3.11.2</mongo>
<mongo.reactivestreams>1.12.0</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
</properties>
@@ -127,25 +127,15 @@
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
<artifactId>mongo-java-driver</artifactId>
<version>${mongo}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>sonatype-libs-snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-STATIC-METADATA-SNAPSHOT</version>
<version>2.2.9.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-STATIC-METADATA-SNAPSHOT</version>
<version>2.2.9.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.1.0-STATIC-METADATA-SNAPSHOT</version>
<version>2.2.9.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -65,12 +65,6 @@
<artifactId>querydsl-mongodb</artifactId>
<version>${querydsl}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -88,14 +82,6 @@
</dependency>
<!-- reactive -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>${mongo}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
@@ -103,6 +89,23 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-async</artifactId>
<version>${mongo}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
@@ -136,13 +139,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex.rxjava3</groupId>
<artifactId>rxjava</artifactId>
<version>${rxjava3}</version>
<optional>true</optional>
</dependency>
<!-- CDI -->
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 -->
<dependency>
@@ -199,14 +195,7 @@
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b11</version>
<version>5.2.4.Final</version>
<scope>test</scope>
</dependency>
@@ -264,13 +253,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>0.5.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
@@ -358,6 +340,12 @@
<java.util.logging.config.file>src/test/resources/logging.properties</java.util.logging.config.file>
<reactor.trace.cancel>true</reactor.trace.cancel>
</systemPropertyVariables>
<properties>
<property>
<name>listener</name>
<value>org.springframework.data.mongodb.test.util.CleanMongoDBJunitRunListener</value>
</property>
</properties>
</configuration>
</plugin>

View File

@@ -19,9 +19,9 @@ import java.util.List;
import org.springframework.dao.DataAccessException;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.bulk.BulkWriteError;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.BulkWriteError;
import com.mongodb.BulkWriteException;
import com.mongodb.BulkWriteResult;
/**
* Is thrown when errors occur during bulk operations.
@@ -38,12 +38,12 @@ public class BulkOperationException extends DataAccessException {
private final BulkWriteResult result;
/**
* Creates a new {@link BulkOperationException} with the given message and source {@link MongoBulkWriteException}.
* Creates a new {@link BulkOperationException} with the given message and source {@link BulkWriteException}.
*
* @param message must not be {@literal null}.
* @param source must not be {@literal null}.
*/
public BulkOperationException(String message, MongoBulkWriteException source) {
public BulkOperationException(String message, BulkWriteException source) {
super(message, source);

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2011-2019 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
*
* https://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;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
import com.mongodb.ClientSessionOptions;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoDatabase;
/**
* Interface for factories creating {@link MongoDatabase} instances.
*
* @author Mark Pollack
* @author Thomas Darimont
* @author Christoph Strobl
* @since 3.0
*/
public interface MongoDatabaseFactory extends CodecRegistryProvider, MongoSessionProvider {
/**
* Obtain a {@link MongoDatabase} from the underlying factory.
*
* @return never {@literal null}.
* @throws DataAccessException
*/
MongoDatabase getMongoDatabase() throws DataAccessException;
/**
* Obtain a {@link MongoDatabase} instance to access the database with the given name.
*
* @param dbName must not be {@literal null}.
* @return never {@literal null}.
* @throws DataAccessException
*/
MongoDatabase getMongoDatabase(String dbName) throws DataAccessException;
/**
* Exposes a shared {@link MongoExceptionTranslator}.
*
* @return will never be {@literal null}.
*/
PersistenceExceptionTranslator getExceptionTranslator();
/**
* Get the underlying {@link CodecRegistry} used by the MongoDB Java driver.
*
* @return never {@literal null}.
*/
@Override
default CodecRegistry getCodecRegistry() {
return getMongoDatabase().getCodecRegistry();
}
/**
* Obtain a {@link ClientSession} for given ClientSessionOptions.
*
* @param options must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
ClientSession getSession(ClientSessionOptions options);
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoDatabaseFactory} returning {@link MongoDatabase}
* instances that are aware and bound to a new session with given {@link ClientSessionOptions options}.
*
* @param options must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
default MongoDatabaseFactory withSession(ClientSessionOptions options) {
return withSession(getSession(options));
}
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoDatabaseFactory} returning {@link MongoDatabase}
* instances that are aware and bound to the given session.
*
* @param session must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
MongoDatabaseFactory withSession(ClientSession session);
/**
* Returns if the given {@link MongoDatabaseFactory} is bound to a {@link ClientSession} that has an
* {@link ClientSession#hasActiveTransaction() active transaction}.
*
* @return {@literal true} if there's an active transaction, {@literal false} otherwise.
* @since 2.1.3
*/
default boolean isTransactionActive() {
return false;
}
}

View File

@@ -27,7 +27,7 @@ import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoDatabase;
/**
* Helper class for managing a {@link MongoDatabase} instances via {@link MongoDatabaseFactory}. Used for obtaining
* Helper class for managing a {@link MongoDatabase} instances via {@link MongoDbFactory}. Used for obtaining
* {@link ClientSession session bound} resources, such as {@link MongoDatabase} and
* {@link com.mongodb.client.MongoCollection} suitable for transactional usage.
* <p />
@@ -41,94 +41,93 @@ import com.mongodb.client.MongoDatabase;
public class MongoDatabaseUtils {
/**
* Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory} using
* Obtain the default {@link MongoDatabase database} form the given {@link MongoDbFactory factory} using
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param factory the {@link MongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(MongoDatabaseFactory factory) {
public static MongoDatabase getDatabase(MongoDbFactory factory) {
return doGetMongoDatabase(null, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION);
}
/**
* Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory}.
* Obtain the default {@link MongoDatabase database} form the given {@link MongoDbFactory factory}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param factory the {@link MongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @param sessionSynchronization the synchronization to use. Must not be {@literal null}.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(MongoDatabaseFactory factory, SessionSynchronization sessionSynchronization) {
public static MongoDatabase getDatabase(MongoDbFactory factory, SessionSynchronization sessionSynchronization) {
return doGetMongoDatabase(null, factory, sessionSynchronization);
}
/**
* Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDatabaseFactory factory} using
* Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDbFactory factory} using
* {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param dbName the name of the {@link MongoDatabase} to get.
* @param factory the {@link MongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(@Nullable String dbName, MongoDatabaseFactory factory) {
public static MongoDatabase getDatabase(@Nullable String dbName, MongoDbFactory factory) {
return doGetMongoDatabase(dbName, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION);
}
/**
* Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDatabaseFactory factory}.
* Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDbFactory factory}.
* <p />
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
* {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
*
* @param dbName the name of the {@link MongoDatabase} to get.
* @param factory the {@link MongoDatabaseFactory} to get the {@link MongoDatabase} from.
* @param factory the {@link MongoDbFactory} to get the {@link MongoDatabase} from.
* @param sessionSynchronization the synchronization to use. Must not be {@literal null}.
* @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
*/
public static MongoDatabase getDatabase(@Nullable String dbName, MongoDatabaseFactory factory,
public static MongoDatabase getDatabase(@Nullable String dbName, MongoDbFactory factory,
SessionSynchronization sessionSynchronization) {
return doGetMongoDatabase(dbName, factory, sessionSynchronization);
}
private static MongoDatabase doGetMongoDatabase(@Nullable String dbName, MongoDatabaseFactory factory,
private static MongoDatabase doGetMongoDatabase(@Nullable String dbName, MongoDbFactory factory,
SessionSynchronization sessionSynchronization) {
Assert.notNull(factory, "Factory must not be null!");
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
return StringUtils.hasText(dbName) ? factory.getMongoDatabase(dbName) : factory.getMongoDatabase();
return StringUtils.hasText(dbName) ? factory.getDb(dbName) : factory.getDb();
}
ClientSession session = doGetSession(factory, sessionSynchronization);
if (session == null) {
return StringUtils.hasText(dbName) ? factory.getMongoDatabase(dbName) : factory.getMongoDatabase();
return StringUtils.hasText(dbName) ? factory.getDb(dbName) : factory.getDb();
}
MongoDatabaseFactory factoryToUse = factory.withSession(session);
return StringUtils.hasText(dbName) ? factoryToUse.getMongoDatabase(dbName) : factoryToUse.getMongoDatabase();
MongoDbFactory factoryToUse = factory.withSession(session);
return StringUtils.hasText(dbName) ? factoryToUse.getDb(dbName) : factoryToUse.getDb();
}
/**
* Check if the {@link MongoDatabaseFactory} is actually bound to a {@link ClientSession} that has an active
* transaction, or if a {@link TransactionSynchronization} has been registered for the {@link MongoDatabaseFactory
* resource} and if the associated {@link ClientSession} has an {@link ClientSession#hasActiveTransaction() active
* transaction}.
* Check if the {@link MongoDbFactory} is actually bound to a {@link ClientSession} that has an active transaction, or
* if a {@link TransactionSynchronization} has been registered for the {@link MongoDbFactory resource} and if the
* associated {@link ClientSession} has an {@link ClientSession#hasActiveTransaction() active transaction}.
*
* @param dbFactory the resource to check transactions for. Must not be {@literal null}.
* @return {@literal true} if the factory has an ongoing transaction.
* @since 2.1.3
*/
public static boolean isTransactionActive(MongoDatabaseFactory dbFactory) {
public static boolean isTransactionActive(MongoDbFactory dbFactory) {
if (dbFactory.isTransactionActive()) {
return true;
@@ -139,8 +138,7 @@ public class MongoDatabaseUtils {
}
@Nullable
private static ClientSession doGetSession(MongoDatabaseFactory dbFactory,
SessionSynchronization sessionSynchronization) {
private static ClientSession doGetSession(MongoDbFactory dbFactory, SessionSynchronization sessionSynchronization) {
MongoResourceHolder resourceHolder = (MongoResourceHolder) TransactionSynchronizationManager.getResource(dbFactory);
@@ -171,7 +169,7 @@ public class MongoDatabaseUtils {
return resourceHolder.getSession();
}
private static ClientSession createClientSession(MongoDatabaseFactory dbFactory) {
private static ClientSession createClientSession(MongoDbFactory dbFactory) {
return dbFactory.getSession(ClientSessionOptions.builder().causallyConsistent(true).build());
}
@@ -186,7 +184,7 @@ public class MongoDatabaseUtils {
private final MongoResourceHolder resourceHolder;
MongoSessionSynchronization(MongoResourceHolder resourceHolder, MongoDatabaseFactory dbFactory) {
MongoSessionSynchronization(MongoResourceHolder resourceHolder, MongoDbFactory dbFactory) {
super(resourceHolder, dbFactory);
this.resourceHolder = resourceHolder;

View File

@@ -15,8 +15,14 @@
*/
package org.springframework.data.mongodb;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
import com.mongodb.ClientSessionOptions;
import com.mongodb.DB;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoDatabase;
/**
@@ -25,33 +31,92 @@ import com.mongodb.client.MongoDatabase;
* @author Mark Pollack
* @author Thomas Darimont
* @author Christoph Strobl
* @deprecated since 3.0, use {@link MongoDatabaseFactory} instead.
*/
@Deprecated
public interface MongoDbFactory extends MongoDatabaseFactory {
public interface MongoDbFactory extends CodecRegistryProvider, MongoSessionProvider {
/**
* Creates a default {@link MongoDatabase} instance.
*
* @return never {@literal null}.
* @throws DataAccessException
* @deprecated since 3.0. Use {@link #getMongoDatabase()} instead.
*/
@Deprecated
default MongoDatabase getDb() throws DataAccessException {
return getMongoDatabase();
}
MongoDatabase getDb() throws DataAccessException;
/**
* Obtain a {@link MongoDatabase} instance to access the database with the given name.
* Creates a {@link DB} instance to access the database with the given name.
*
* @param dbName must not be {@literal null} or empty.
* @return never {@literal null}.
* @throws DataAccessException
* @deprecated since 3.0. Use {@link #getMongoDatabase(String)} instead.
*/
MongoDatabase getDb(String dbName) throws DataAccessException;
/**
* Exposes a shared {@link MongoExceptionTranslator}.
*
* @return will never be {@literal null}.
*/
PersistenceExceptionTranslator getExceptionTranslator();
/**
* Get the legacy database entry point. Please consider {@link #getDb()} instead.
*
* @return
* @deprecated since 2.1, use {@link #getDb()}. This method will be removed with a future version as it works only
* with the legacy MongoDB driver.
*/
@Deprecated
default MongoDatabase getDb(String dbName) throws DataAccessException {
return getMongoDatabase(dbName);
DB getLegacyDb();
/**
* Get the underlying {@link CodecRegistry} used by the MongoDB Java driver.
*
* @return never {@literal null}.
*/
@Override
default CodecRegistry getCodecRegistry() {
return getDb().getCodecRegistry();
}
/**
* Obtain a {@link ClientSession} for given ClientSessionOptions.
*
* @param options must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
ClientSession getSession(ClientSessionOptions options);
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoDbFactory} returning {@link MongoDatabase} instances
* that are aware and bound to a new session with given {@link ClientSessionOptions options}.
*
* @param options must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
default MongoDbFactory withSession(ClientSessionOptions options) {
return withSession(getSession(options));
}
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoDbFactory} returning {@link MongoDatabase} instances
* that are aware and bound to the given session.
*
* @param session must not be {@literal null}.
* @return never {@literal null}.
* @since 2.1
*/
MongoDbFactory withSession(ClientSession session);
/**
* Returns if the given {@link MongoDbFactory} is bound to a {@link ClientSession} that has an
* {@link ClientSession#hasActiveTransaction() active transaction}.
*
* @return {@literal true} if there's an active transaction, {@literal false} otherwise.
* @since 2.1.3
*/
default boolean isTransactionActive() {
return false;
}
}

View File

@@ -36,15 +36,15 @@ import com.mongodb.client.ClientSession;
class MongoResourceHolder extends ResourceHolderSupport {
private @Nullable ClientSession session;
private MongoDatabaseFactory dbFactory;
private MongoDbFactory dbFactory;
/**
* Create a new {@link MongoResourceHolder} for a given {@link ClientSession session}.
*
* @param session the associated {@link ClientSession}. Can be {@literal null}.
* @param dbFactory the associated {@link MongoDatabaseFactory}. must not be {@literal null}.
* @param dbFactory the associated {@link MongoDbFactory}. must not be {@literal null}.
*/
MongoResourceHolder(@Nullable ClientSession session, MongoDatabaseFactory dbFactory) {
MongoResourceHolder(@Nullable ClientSession session, MongoDbFactory dbFactory) {
this.session = session;
this.dbFactory = dbFactory;
@@ -75,9 +75,9 @@ class MongoResourceHolder extends ResourceHolderSupport {
}
/**
* @return the associated {@link MongoDatabaseFactory}.
* @return the associated {@link MongoDbFactory}.
*/
public MongoDatabaseFactory getDbFactory() {
public MongoDbFactory getDbFactory() {
return dbFactory;
}

View File

@@ -36,18 +36,17 @@ import com.mongodb.client.ClientSession;
/**
* A {@link org.springframework.transaction.PlatformTransactionManager} implementation that manages
* {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}.
* {@link ClientSession} based transactions for a single {@link MongoDbFactory}.
* <p />
* Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread.
* Binds a {@link ClientSession} from the specified {@link MongoDbFactory} to the thread.
* <p />
* {@link TransactionDefinition#isReadOnly() Readonly} transactions operate on a {@link ClientSession} and enable causal
* consistency, and also {@link ClientSession#startTransaction() start}, {@link ClientSession#commitTransaction()
* commit} or {@link ClientSession#abortTransaction() abort} a transaction.
* <p />
* Application code is required to retrieve the {@link com.mongodb.client.MongoDatabase} via
* {@link MongoDatabaseUtils#getDatabase(MongoDatabaseFactory)} instead of a standard
* {@link MongoDatabaseFactory#getMongoDatabase()} call. Spring classes such as
* {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly.
* {@link MongoDatabaseUtils#getDatabase(MongoDbFactory)} instead of a standard {@link MongoDbFactory#getDb()} call.
* Spring classes such as {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly.
* <p />
* By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. One may override
* {@link #doCommit(MongoTransactionObject)} to implement the
@@ -59,46 +58,46 @@ import com.mongodb.client.ClientSession;
* @currentRead Shadow's Edge - Brent Weeks
* @since 2.1
* @see <a href="https://www.mongodb.com/transactions">MongoDB Transaction Documentation</a>
* @see MongoDatabaseUtils#getDatabase(MongoDatabaseFactory, SessionSynchronization)
* @see MongoDatabaseUtils#getDatabase(MongoDbFactory, SessionSynchronization)
*/
public class MongoTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
private @Nullable MongoDatabaseFactory dbFactory;
private @Nullable MongoDbFactory dbFactory;
private @Nullable TransactionOptions options;
/**
* Create a new {@link MongoTransactionManager} for bean-style usage.
* <p />
* <strong>Note:</strong>The {@link MongoDatabaseFactory db factory} has to be
* {@link #setDbFactory(MongoDatabaseFactory) set} before using the instance. Use this constructor to prepare a
* {@link MongoTransactionManager} via a {@link org.springframework.beans.factory.BeanFactory}.
* <strong>Note:</strong>The {@link MongoDbFactory db factory} has to be {@link #setDbFactory(MongoDbFactory) set}
* before using the instance. Use this constructor to prepare a {@link MongoTransactionManager} via a
* {@link org.springframework.beans.factory.BeanFactory}.
* <p />
* Optionally it is possible to set default {@link TransactionOptions transaction options} defining
* {@link com.mongodb.ReadConcern} and {@link com.mongodb.WriteConcern}.
*
* @see #setDbFactory(MongoDatabaseFactory)
* @see #setDbFactory(MongoDbFactory)
* @see #setTransactionSynchronization(int)
*/
public MongoTransactionManager() {}
/**
* Create a new {@link MongoTransactionManager} obtaining sessions from the given {@link MongoDatabaseFactory}.
* Create a new {@link MongoTransactionManager} obtaining sessions from the given {@link MongoDbFactory}.
*
* @param dbFactory must not be {@literal null}.
*/
public MongoTransactionManager(MongoDatabaseFactory dbFactory) {
public MongoTransactionManager(MongoDbFactory dbFactory) {
this(dbFactory, null);
}
/**
* Create a new {@link MongoTransactionManager} obtaining sessions from the given {@link MongoDatabaseFactory}
* applying the given {@link TransactionOptions options}, if present, when starting a new transaction.
* Create a new {@link MongoTransactionManager} obtaining sessions from the given {@link MongoDbFactory} applying the
* given {@link TransactionOptions options}, if present, when starting a new transaction.
*
* @param dbFactory must not be {@literal null}.
* @param options can be {@literal null}.
*/
public MongoTransactionManager(MongoDatabaseFactory dbFactory, @Nullable TransactionOptions options) {
public MongoTransactionManager(MongoDbFactory dbFactory, @Nullable TransactionOptions options) {
Assert.notNull(dbFactory, "DbFactory must not be null!");
@@ -296,11 +295,11 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
}
/**
* Set the {@link MongoDatabaseFactory} that this instance should manage transactions for.
* Set the {@link MongoDbFactory} that this instance should manage transactions for.
*
* @param dbFactory must not be {@literal null}.
*/
public void setDbFactory(MongoDatabaseFactory dbFactory) {
public void setDbFactory(MongoDbFactory dbFactory) {
Assert.notNull(dbFactory, "DbFactory must not be null!");
this.dbFactory = dbFactory;
@@ -316,12 +315,12 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
}
/**
* Get the {@link MongoDatabaseFactory} that this instance manages transactions for.
* Get the {@link MongoDbFactory} that this instance manages transactions for.
*
* @return can be {@literal null}.
*/
@Nullable
public MongoDatabaseFactory getDbFactory() {
public MongoDbFactory getDbFactory() {
return dbFactory;
}
@@ -330,7 +329,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
* @see org.springframework.transaction.support.ResourceTransactionManager#getResourceFactory()
*/
@Override
public MongoDatabaseFactory getResourceFactory() {
public MongoDbFactory getResourceFactory() {
return getRequiredDbFactory();
}
@@ -345,7 +344,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
private MongoResourceHolder newResourceHolder(TransactionDefinition definition, ClientSessionOptions options) {
MongoDatabaseFactory dbFactory = getResourceFactory();
MongoDbFactory dbFactory = getResourceFactory();
MongoResourceHolder resourceHolder = new MongoResourceHolder(dbFactory.getSession(options), dbFactory);
resourceHolder.setTimeoutIfNotDefaulted(determineTimeout(definition));
@@ -356,7 +355,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
/**
* @throws IllegalStateException if {@link #dbFactory} is {@literal null}.
*/
private MongoDatabaseFactory getRequiredDbFactory() {
private MongoDbFactory getRequiredDbFactory() {
Assert.state(dbFactory != null,
"MongoTransactionManager operates upon a MongoDbFactory. Did you forget to provide one? It's required.");

View File

@@ -31,7 +31,6 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mathieu Ouellet
* @since 2.0
*/
public interface ReactiveMongoDatabaseFactory extends CodecRegistryProvider {
@@ -42,16 +41,16 @@ public interface ReactiveMongoDatabaseFactory extends CodecRegistryProvider {
* @return never {@literal null}.
* @throws DataAccessException
*/
Mono<MongoDatabase> getMongoDatabase() throws DataAccessException;
MongoDatabase getMongoDatabase() throws DataAccessException;
/**
* Obtain a {@link MongoDatabase} instance to access the database with the given name.
* Creates a {@link MongoDatabase} instance to access the database with the given name.
*
* @param dbName must not be {@literal null} or empty.
* @return never {@literal null}.
* @throws DataAccessException
*/
Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException;
MongoDatabase getMongoDatabase(String dbName) throws DataAccessException;
/**
* Exposes a shared {@link MongoExceptionTranslator}.
@@ -65,7 +64,10 @@ public interface ReactiveMongoDatabaseFactory extends CodecRegistryProvider {
*
* @return never {@literal null}.
*/
CodecRegistry getCodecRegistry();
@Override
default CodecRegistry getCodecRegistry() {
return getMongoDatabase().getCodecRegistry();
}
/**
* Obtain a {@link Mono} emitting a {@link ClientSession} for given {@link ClientSessionOptions options}.

View File

@@ -41,7 +41,6 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mathieu Ouellet
* @since 2.2
*/
public class ReactiveMongoDatabaseUtils {
@@ -143,13 +142,14 @@ public class ReactiveMongoDatabaseUtils {
.flatMap(synchronizationManager -> {
return doGetSession(synchronizationManager, factory, sessionSynchronization) //
.flatMap(it -> getMongoDatabaseOrDefault(dbName, factory.withSession(it)));
}) //
.onErrorResume(NoTransactionException.class, e -> getMongoDatabaseOrDefault(dbName, factory))
.switchIfEmpty(getMongoDatabaseOrDefault(dbName, factory));
.map(it -> getMongoDatabaseOrDefault(dbName, factory.withSession(it)));
})
.onErrorResume(NoTransactionException.class,
e -> Mono.fromSupplier(() -> getMongoDatabaseOrDefault(dbName, factory)))
.defaultIfEmpty(getMongoDatabaseOrDefault(dbName, factory));
}
private static Mono<MongoDatabase> getMongoDatabaseOrDefault(@Nullable String dbName,
private static MongoDatabase getMongoDatabaseOrDefault(@Nullable String dbName,
ReactiveMongoDatabaseFactory factory) {
return StringUtils.hasText(dbName) ? factory.getMongoDatabase(dbName) : factory.getMongoDatabase();
}

View File

@@ -42,7 +42,7 @@ class ReactiveMongoResourceHolder extends ResourceHolderSupport {
* Create a new {@link ReactiveMongoResourceHolder} for a given {@link ClientSession session}.
*
* @param session the associated {@link ClientSession}. Can be {@literal null}.
* @param databaseFactory the associated {@link MongoDatabaseFactory}. must not be {@literal null}.
* @param databaseFactory the associated {@link MongoDbFactory}. must not be {@literal null}.
*/
ReactiveMongoResourceHolder(@Nullable ClientSession session, ReactiveMongoDatabaseFactory databaseFactory) {
@@ -99,7 +99,7 @@ class ReactiveMongoResourceHolder extends ResourceHolderSupport {
* If the {@link ReactiveMongoResourceHolder} is {@link #hasSession() not already associated} with a
* {@link ClientSession} the given value is {@link #setSession(ClientSession) set} and returned, otherwise the current
* bound session is returned.
*
*
* @param session
* @return
*/

View File

@@ -1,77 +0,0 @@
/*
* Copyright 2020 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
*
* https://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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.util.Version;
import org.springframework.util.StringUtils;
import com.mongodb.MongoDriverInformation;
/**
* Class that exposes the SpringData MongoDB specific information like the current {@link Version} or
* {@link MongoDriverInformation driver information}.
*
* @author Christoph Strobl
* @since 3.0
*/
public class SpringDataMongoDB {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringDataMongoDB.class);
private static final Version FALLBACK_VERSION = new Version(3);
private static final MongoDriverInformation DRIVER_INFORMATION = MongoDriverInformation
.builder(MongoDriverInformation.builder().build()).driverName("spring-data").build();
/**
* Obtain the SpringData MongoDB specific driver information.
*
* @return never {@literal null}.
*/
public static MongoDriverInformation driverInformation() {
return DRIVER_INFORMATION;
}
/**
* Fetches the "Implementation-Version" manifest attribute from the jar file.
* <p />
* Note that some ClassLoaders do not expose the package metadata, hence this class might not be able to determine the
* version in all environments. In this case the current Major version is returned as a fallback.
*
* @return never {@literal null}.
*/
public static Version version() {
Package pkg = SpringDataMongoDB.class.getPackage();
String versionString = (pkg != null ? pkg.getImplementationVersion() : null);
if (!StringUtils.hasText(versionString)) {
LOGGER.debug("Unable to find Spring Data MongoDB version.");
return FALLBACK_VERSION;
}
try {
return Version.parse(versionString);
} catch (Exception e) {
LOGGER.debug("Cannot read Spring Data MongoDB version '{}'.", versionString);
}
return FALLBACK_VERSION;
}
}

View File

@@ -17,22 +17,17 @@ package org.springframework.data.mongodb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.SpringDataMongoDB;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.lang.Nullable;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
/**
* Base class for Spring Data MongoDB configuration using JavaConfig with {@link com.mongodb.client.MongoClient}.
@@ -40,44 +35,40 @@ import com.mongodb.client.MongoClients;
* @author Christoph Strobl
* @since 2.1
* @see MongoConfigurationSupport
* @see AbstractMongoConfiguration
*/
@Configuration(proxyBeanMethods = false)
@Configuration
public abstract class AbstractMongoClientConfiguration extends MongoConfigurationSupport {
/**
* Return the {@link MongoClient} instance to connect to. Annotate with {@link Bean} in case you want to expose a
* {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}. <br />
* Override {@link #mongoClientSettings()} to configure connection details.
* {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}.
*
* @return never {@literal null}.
* @see #mongoClientSettings()
* @see #configureClientSettings(Builder)
* @return
*/
public MongoClient mongoClient() {
return createMongoClient(mongoClientSettings());
}
public abstract MongoClient mongoClient();
/**
* Creates a {@link MongoTemplate}.
*
* @see #mongoDbFactory()
* @see #mappingMongoConverter(MongoDatabaseFactory, MongoCustomConversions, MongoMappingContext)
* @return
*/
@Bean
public MongoTemplate mongoTemplate(MongoDatabaseFactory databaseFactory, MappingMongoConverter converter) {
return new MongoTemplate(databaseFactory, converter);
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
}
/**
* Creates a {@link org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory} to be used by the
* {@link MongoTemplate}. Will use the {@link MongoClient} instance configured in {@link #mongoClient()}.
* Creates a {@link SimpleMongoDbFactory} to be used by the {@link MongoTemplate}. Will use the {@link MongoClient}
* instance configured in {@link #mongoClient()}.
*
* @see #mongoClient()
* @see #mongoTemplate(MongoDatabaseFactory, MappingMongoConverter)
* @see #mongoTemplate()
* @return
*/
@Bean
public MongoDatabaseFactory mongoDbFactory() {
return new SimpleMongoClientDatabaseFactory(mongoClient(), getDatabaseName());
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoClientDbFactory(mongoClient(), getDatabaseName());
}
/**
@@ -100,32 +91,22 @@ public abstract class AbstractMongoClientConfiguration extends MongoConfiguratio
/**
* Creates a {@link MappingMongoConverter} using the configured {@link #mongoDbFactory()} and
* {@link #mongoMappingContext(MongoCustomConversions)}. Will get {@link #customConversions()} applied.
* {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #mongoMappingContext(MongoCustomConversions)
* @see #mongoMappingContext()
* @see #mongoDbFactory()
* @return
* @throws Exception
*/
@Bean
public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory databaseFactory,
MongoCustomConversions customConversions, MongoMappingContext mappingContext) {
public MappingMongoConverter mappingMongoConverter() throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(databaseFactory);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
converter.setCustomConversions(customConversions);
converter.setCodecRegistryProvider(databaseFactory);
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(mongoDbFactory());
return converter;
}
/**
* Create the Reactive Streams {@link com.mongodb.reactivestreams.client.MongoClient} instance with given
* {@link MongoClientSettings}.
*
* @return never {@literal null}.
* @since 3.0
*/
protected MongoClient createMongoClient(MongoClientSettings settings) {
return MongoClients.create(settings, SpringDataMongoDB.driverInformation());
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2011-2020 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.lang.Nullable;
import com.mongodb.MongoClient;
/**
* Base class for Spring Data MongoDB configuration using JavaConfig with {@link com.mongodb.MongoClient}.
* <p />
* <strong>INFO:</strong>In case you want to use {@link com.mongodb.client.MongoClients} for configuration please refer
* to {@link AbstractMongoClientConfiguration}.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
* @author Ryan Tenney
* @author Christoph Strobl
* @author Mark Paluch
* @see MongoConfigurationSupport
* @see AbstractMongoClientConfiguration
* @deprecated since 2.2 in favor of {@link AbstractMongoClientConfiguration}.
*/
@Configuration
@Deprecated
public abstract class AbstractMongoConfiguration extends MongoConfigurationSupport {
/**
* Return the {@link MongoClient} instance to connect to. Annotate with {@link Bean} in case you want to expose a
* {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}.
*
* @return
*/
public abstract MongoClient mongoClient();
/**
* Creates a {@link MongoTemplate}.
*
* @return
*/
@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 MongoClient}
* instance configured in {@link #mongoClient()}.
*
* @see #mongoClient()
* @see #mongoTemplate()
* @return
*/
@Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(mongoClient(), getDatabaseName());
}
/**
* Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration
* class' (the concrete class, not this one here) by default. So if you have a {@code com.acme.AppConfig} extending
* {@link AbstractMongoConfiguration} the base package will be considered {@code com.acme} unless the method is
* overridden to implement alternate behavior.
*
* @return the base package to scan for mapped {@link Document} classes or {@literal null} to not enable scanning for
* entities.
* @deprecated use {@link #getMappingBasePackages()} instead.
*/
@Deprecated
@Nullable
protected String getMappingBasePackage() {
Package mappingBasePackage = getClass().getPackage();
return mappingBasePackage == null ? null : mappingBasePackage.getName();
}
/**
* 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 {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(mongoDbFactory());
return converter;
}
}

View File

@@ -18,19 +18,13 @@ package org.springframework.data.mongodb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.SpringDataMongoDB;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
/**
* Base class for reactive Spring Data MongoDB configuration using JavaConfig.
@@ -40,33 +34,25 @@ import com.mongodb.reactivestreams.client.MongoClients;
* @since 2.0
* @see MongoConfigurationSupport
*/
@Configuration(proxyBeanMethods = false)
@Configuration
public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurationSupport {
/**
* Return the Reactive Streams {@link MongoClient} instance to connect to. Annotate with {@link Bean} in case you want
* to expose a {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}. <br />
* Override {@link #mongoClientSettings()} to configure connection details.
* to expose a {@link MongoClient} instance to the {@link org.springframework.context.ApplicationContext}.
*
* @return never {@literal null}.
* @see #mongoClientSettings()
* @see #configureClientSettings(Builder)
*/
public MongoClient reactiveMongoClient() {
return createReactiveMongoClient(mongoClientSettings());
}
public abstract MongoClient reactiveMongoClient();
/**
* Creates {@link ReactiveMongoOperations}.
*
* @see #reactiveMongoDbFactory()
* @see #mappingMongoConverter(ReactiveMongoDatabaseFactory, MongoCustomConversions, MongoMappingContext)
* @return never {@literal null}.
*/
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoDatabaseFactory databaseFactory,
MappingMongoConverter mongoConverter) {
return new ReactiveMongoTemplate(databaseFactory, mongoConverter);
public ReactiveMongoOperations reactiveMongoTemplate() throws Exception {
return new ReactiveMongoTemplate(reactiveMongoDbFactory(), mappingMongoConverter());
}
/**
@@ -74,7 +60,7 @@ public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurat
* {@link MongoClient} instance configured in {@link #reactiveMongoClient()}.
*
* @see #reactiveMongoClient()
* @see #reactiveMongoTemplate(ReactiveMongoDatabaseFactory, MappingMongoConverter)
* @see #reactiveMongoTemplate()
* @return never {@literal null}.
*/
@Bean
@@ -84,31 +70,21 @@ public abstract class AbstractReactiveMongoConfiguration extends MongoConfigurat
/**
* Creates a {@link MappingMongoConverter} using the configured {@link #reactiveMongoDbFactory()} and
* {@link #mongoMappingContext(MongoCustomConversions)}. Will get {@link #customConversions()} applied.
* {@link #mongoMappingContext()}. Will get {@link #customConversions()} applied.
*
* @see #customConversions()
* @see #mongoMappingContext(MongoCustomConversions)
* @see #mongoMappingContext()
* @see #reactiveMongoDbFactory()
* @return never {@literal null}.
* @throws Exception
*/
@Bean
public MappingMongoConverter mappingMongoConverter(ReactiveMongoDatabaseFactory databaseFactory,
MongoCustomConversions customConversions, MongoMappingContext mappingContext) {
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
converter.setCustomConversions(customConversions);
converter.setCodecRegistryProvider(databaseFactory);
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mongoMappingContext());
converter.setCustomConversions(customConversions());
converter.setCodecRegistryProvider(reactiveMongoDbFactory());
return converter;
}
/**
* Create the Reactive Streams {@link MongoClient} instance with given {@link MongoClientSettings}.
*
* @return never {@literal null}.
* @since 3.0
*/
protected MongoClient createReactiveMongoClient(MongoClientSettings settings) {
return MongoClients.create(settings, SpringDataMongoDB.driverInformation());
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2019 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import com.mongodb.ConnectionString;
/**
* Parse a {@link String} to a {@link com.mongodb.ConnectionString}.
*
* @author Christoph Strobl
* @since 3.0
*/
public class ConnectionStringPropertyEditor extends PropertyEditorSupport {
/*
* (non-Javadoc)
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
@Override
public void setAsText(@Nullable String connectionString) {
if (!StringUtils.hasText(connectionString)) {
return;
}
setValue(new ConnectionString(connectionString));
}
}

View File

@@ -61,8 +61,8 @@ public @interface EnableMongoAuditing {
boolean modifyOnCreate() default true;
/**
* Configures a {@link DateTimeProvider} bean name that allows customizing the timestamp to be used for setting
* creation and modification dates.
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link org.joda.time.DateTime} to be
* used for setting creation and modification dates.
*
* @return empty {@link String} by default.
*/

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2020 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
*
* https://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.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.domain.ReactiveAuditorAware;
/**
* Annotation to enable auditing in MongoDB using reactive infrastructure via annotation configuration.
*
* @author Mark Paluch
* @since 3.1
*/
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ReactiveMongoAuditingRegistrar.class)
public @interface EnableReactiveMongoAuditing {
/**
* Configures the {@link ReactiveAuditorAware} bean to be used to lookup the current principal.
*
* @return empty {@link String} by default.
*/
String auditorAwareRef() default "";
/**
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
*
* @return {@literal true} by default.
*/
boolean setDates() default true;
/**
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
*
* @return {@literal true} by default.
*/
boolean modifyOnCreate() default true;
/**
* Configures a {@link DateTimeProvider} bean name that allows customizing the timestamp to be used for setting
* creation and modification dates.
*
* @return empty {@link String} by default.
*/
String dateTimeProviderRef() default "";
}

View File

@@ -96,9 +96,6 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
String id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);
id = StringUtils.hasText(id) ? id : DEFAULT_CONVERTER_BEAN_NAME;
String autoIndexCreation = element.getAttribute("auto-index-creation");
boolean autoIndexCreationEnabled = StringUtils.hasText(autoIndexCreation) && Boolean.valueOf(autoIndexCreation);
parserContext.pushContainingComponent(new CompositeComponentDefinition("Mapping Mongo Converter", element));
BeanDefinition conversionsDefinition = getCustomConversions(element, parserContext);
@@ -202,11 +199,6 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
public static String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
@Nullable BeanDefinition conversionsDefinition, @Nullable String converterId) {
return potentiallyCreateMappingContext(element, parserContext, conversionsDefinition, converterId, false);
}
public static String potentiallyCreateMappingContext(Element element, ParserContext parserContext,
@Nullable BeanDefinition conversionsDefinition, @Nullable String converterId, boolean autoIndexCreation) {
String ctxRef = element.getAttribute("mapping-context-ref");
@@ -234,8 +226,6 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition);
}
mappingContextBuilder.addPropertyValue("autoIndexCreation", autoIndexCreation);
parseFieldNamingStrategy(element, parserContext.getReaderContext(), mappingContextBuilder);
ctxRef = converterId == null || DEFAULT_CONVERTER_BEAN_NAME.equals(converterId) ? MAPPING_CONTEXT_BEAN_NAME

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.config;
import java.lang.annotation.Annotation;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -27,8 +28,14 @@ import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback;
import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableMongoAuditing} annotation.
@@ -39,6 +46,9 @@ import org.springframework.util.Assert;
*/
class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
private static boolean PROJECT_REACTOR_AVAILABLE = ClassUtils.isPresent("reactor.core.publisher.Mono",
MongoAuditingRegistrar.class.getClassLoader());
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
@@ -81,7 +91,7 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContextLookup.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
builder.addConstructorArgValue(definition.getBeanDefinition());
@@ -106,6 +116,68 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
AuditingEntityCallback.class.getName(), registry);
if (PROJECT_REACTOR_AVAILABLE) {
registerReactiveAuditingEntityCallback(registry, auditingHandlerDefinition.getSource());
}
}
private void registerReactiveAuditingEntityCallback(BeanDefinitionRegistry registry, Object source) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
builder.getRawBeanDefinition().setSource(source);
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingEntityCallback.class.getName(),
registry);
}
/**
* Simple helper to be able to wire the {@link MappingContext} from a {@link MappingMongoConverter} bean available in
* the application context.
*
* @author Oliver Gierke
*/
static class MongoMappingContextLookup
implements FactoryBean<MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty>> {
private final MappingMongoConverter converter;
/**
* Creates a new {@link MongoMappingContextLookup} for the given {@link MappingMongoConverter}.
*
* @param converter must not be {@literal null}.
*/
public MongoMappingContextLookup(MappingMongoConverter converter) {
this.converter = converter;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getObject() throws Exception {
return converter.getMappingContext();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return MappingContext.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
}
}

View File

@@ -50,11 +50,10 @@ public class MongoClientParser implements BeanDefinitionParser {
ParsingUtils.setPropertyValue(builder, element, "port", "port");
ParsingUtils.setPropertyValue(builder, element, "host", "host");
ParsingUtils.setPropertyValue(builder, element, "credential", "credential");
ParsingUtils.setPropertyValue(builder, element, "replica-set", "replicaSet");
ParsingUtils.setPropertyValue(builder, element, "connection-string", "connectionString");
ParsingUtils.setPropertyValue(builder, element, "credentials", "credentials");
MongoParsingUtils.parseMongoClientSettings(element, builder);
MongoParsingUtils.parseMongoClientOptions(element, builder);
MongoParsingUtils.parseReplicaSet(element, builder);
String defaultedId = StringUtils.hasText(id) ? id : BeanNames.MONGO_BEAN_NAME;
@@ -63,34 +62,22 @@ public class MongoClientParser implements BeanDefinitionParser {
BeanComponentDefinition mongoComponent = helper.getComponent(builder, defaultedId);
parserContext.registerBeanComponent(mongoComponent);
BeanComponentDefinition connectionStringPropertyEditor = helper
.getComponent(MongoParsingUtils.getConnectionStringPropertyEditorBuilder());
parserContext.registerBeanComponent(connectionStringPropertyEditor);
BeanComponentDefinition serverAddressPropertyEditor = helper
.getComponent(MongoParsingUtils.getServerAddressPropertyEditorBuilder());
BeanComponentDefinition serverAddressPropertyEditor = helper.getComponent(MongoParsingUtils
.getServerAddressPropertyEditorBuilder());
parserContext.registerBeanComponent(serverAddressPropertyEditor);
BeanComponentDefinition writeConcernEditor = helper
.getComponent(MongoParsingUtils.getWriteConcernPropertyEditorBuilder());
BeanComponentDefinition writeConcernEditor = helper.getComponent(MongoParsingUtils
.getWriteConcernPropertyEditorBuilder());
parserContext.registerBeanComponent(writeConcernEditor);
BeanComponentDefinition readConcernEditor = helper
.getComponent(MongoParsingUtils.getReadConcernPropertyEditorBuilder());
parserContext.registerBeanComponent(readConcernEditor);
BeanComponentDefinition readPreferenceEditor = helper
.getComponent(MongoParsingUtils.getReadPreferencePropertyEditorBuilder());
BeanComponentDefinition readPreferenceEditor = helper.getComponent(MongoParsingUtils
.getReadPreferencePropertyEditorBuilder());
parserContext.registerBeanComponent(readPreferenceEditor);
BeanComponentDefinition credentialsEditor = helper
.getComponent(MongoParsingUtils.getMongoCredentialPropertyEditor());
BeanComponentDefinition credentialsEditor = helper.getComponent(MongoParsingUtils
.getMongoCredentialPropertyEditor());
parserContext.registerBeanComponent(credentialsEditor);
BeanComponentDefinition uuidRepresentationEditor = helper
.getComponent(MongoParsingUtils.getUUidRepresentationEditorBuilder());
parserContext.registerBeanComponent(uuidRepresentationEditor);
parserContext.popAndRegisterContainingComponent();
return mongoComponent.getBeanDefinition();

View File

@@ -20,7 +20,6 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.bson.UuidRepresentation;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -32,15 +31,11 @@ import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingSt
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
/**
* Base class for Spring Data MongoDB to be extended for JavaConfiguration usage.
*
@@ -80,12 +75,11 @@ public abstract class MongoConfigurationSupport {
* @throws ClassNotFoundException
*/
@Bean
public MongoMappingContext mongoMappingContext(MongoCustomConversions customConversions)
throws ClassNotFoundException {
public MongoMappingContext mongoMappingContext() throws ClassNotFoundException {
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
mappingContext.setAutoIndexCreation(autoIndexCreation());
@@ -94,30 +88,14 @@ public abstract class MongoConfigurationSupport {
/**
* Register custom {@link Converter}s in a {@link CustomConversions} object if required. These
* {@link CustomConversions} will be registered with the
* {@link org.springframework.data.mongodb.core.convert.MappingMongoConverter} and {@link MongoMappingContext}.
* Returns an empty {@link MongoCustomConversions} instance by default.
* <p>
* <strong>NOTE:</strong> Use {@link #configureConverters(MongoConverterConfigurationAdapter)} to configure MongoDB
* native simple types and register custom {@link Converter converters}.
* {@link CustomConversions} will be registered with the {@link #mappingMongoConverter()} and
* {@link MongoMappingContext}. Returns an empty {@link MongoCustomConversions} instance by default.
*
* @return must not be {@literal null}.
*/
@Bean
public MongoCustomConversions customConversions() {
return MongoCustomConversions.create(this::configureConverters);
}
/**
* Configuration hook for {@link MongoCustomConversions} creation.
*
* @param converterConfigurationAdapter never {@literal null}.
* @since 2.3
* @see MongoConverterConfigurationAdapter#useNativeDriverJavaTimeCodecs()
* @see MongoConverterConfigurationAdapter#useSpringDataJavaTimeCodecs()
*/
protected void configureConverters(MongoConverterConfigurationAdapter converterConfigurationAdapter) {
public CustomConversions customConversions() {
return new MongoCustomConversions(Collections.emptyList());
}
/**
@@ -199,36 +177,11 @@ public abstract class MongoConfigurationSupport {
* Configure whether to automatically create indices for domain types by deriving the
* {@link org.springframework.data.mongodb.core.index.IndexDefinition} from the entity or not.
*
* @return {@literal false} by default. <br />
* <strong>INFO</strong>: As of 3.x the default is set to {@literal false}; In 2.x it was {@literal true}.
* @return {@literal true} by default. <br />
* <strong>INFO</strong>: As of 3.x the default will be set to {@literal false}.
* @since 2.2
*/
protected boolean autoIndexCreation() {
return false;
}
/**
* Return the {@link MongoClientSettings} used to create the actual {@literal MongoClient}. <br />
* Override either this method, or use {@link #configureClientSettings(Builder)} to alter the setup.
*
* @return never {@literal null}.
* @since 3.0
*/
protected MongoClientSettings mongoClientSettings() {
MongoClientSettings.Builder builder = MongoClientSettings.builder();
builder.uuidRepresentation(UuidRepresentation.JAVA_LEGACY);
configureClientSettings(builder);
return builder.build();
}
/**
* Configure {@link MongoClientSettings} via its {@link Builder} API.
*
* @param builder never {@literal null}.
* @since 3.0
*/
protected void configureClientSettings(MongoClientSettings.Builder builder) {
// customization hook
return true;
}
}

View File

@@ -32,12 +32,14 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.springframework.data.mongodb.core.MongoClientFactoryBean;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import com.mongodb.ConnectionString;
import com.mongodb.Mongo;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoURI;
/**
* {@link BeanDefinitionParser} to parse {@code db-factory} elements into {@link BeanDefinition}s.
@@ -82,11 +84,10 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
// Common setup
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder
.genericBeanDefinition(SimpleMongoClientDatabaseFactory.class);
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class);
setPropertyValue(dbFactoryBuilder, element, "write-concern", "writeConcern");
BeanDefinition mongoUri = getConnectionString(element, parserContext);
BeanDefinition mongoUri = getMongoUri(element, parserContext);
if (mongoUri != null) {
@@ -96,8 +97,7 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
BeanComponentDefinitionBuilder helper = new BeanComponentDefinitionBuilder(element, parserContext);
String mongoRef = element.getAttribute("mongo-client-ref");
String mongoRef = element.getAttribute("mongo-ref");
String dbname = element.getAttribute("dbname");
// Defaulting
@@ -119,8 +119,8 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
}
/**
* Registers a default {@link BeanDefinition} of a {@link com.mongodb.client.MongoClient} instance and returns the
* name under which the {@link com.mongodb.client.MongoClient} instance was registered under.
* Registers a default {@link BeanDefinition} of a {@link Mongo} instance and returns the name under which the
* {@link Mongo} instance was registered under.
*
* @param element must not be {@literal null}.
* @param parserContext must not be {@literal null}.
@@ -136,7 +136,8 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
}
/**
* Creates a {@link BeanDefinition} for a {@link ConnectionString} depending on configured attributes. <br />
* Creates a {@link BeanDefinition} for a {@link MongoURI} or {@link MongoClientURI} depending on configured
* attributes. <br />
* Errors when configured element contains {@literal uri} or {@literal client-uri} along with other attributes except
* {@literal write-concern} and/or {@literal id}.
*
@@ -145,19 +146,11 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
* @return {@literal null} in case no client-/uri defined.
*/
@Nullable
private BeanDefinition getConnectionString(Element element, ParserContext parserContext) {
private BeanDefinition getMongoUri(Element element, ParserContext parserContext) {
String type = null;
boolean hasClientUri = element.hasAttribute("client-uri");
if (element.hasAttribute("client-uri")) {
type = "client-uri";
} else if (element.hasAttribute("connection-string")) {
type = "connection-string";
} else if (element.hasAttribute("uri")) {
type = "uri";
}
if (!StringUtils.hasText(type)) {
if (!hasClientUri && !element.hasAttribute("uri")) {
return null;
}
@@ -171,12 +164,16 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
if (element.getAttributes().getLength() > allowedAttributesCount) {
parserContext.getReaderContext().error("Configure either MongoDB " + type + " or details individually!",
parserContext.getReaderContext().error(
"Configure either " + (hasClientUri ? "Mongo Client URI" : "Mongo URI") + " or details individually!",
parserContext.extractSource(element));
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConnectionString.class);
builder.addConstructorArgValue(element.getAttribute(type));
Class<?> type = MongoClientURI.class;
String uri = hasClientUri ? element.getAttribute("client-uri") : element.getAttribute("uri");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(type);
builder.addConstructorArgValue(uri);
return builder.getBeanDefinition();
}

View File

@@ -24,7 +24,7 @@ import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.data.mongodb.core.MongoClientSettingsFactoryBean;
import org.springframework.data.mongodb.core.MongoClientOptionsFactoryBean;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@@ -43,78 +43,60 @@ abstract class MongoParsingUtils {
private MongoParsingUtils() {}
/**
* Parses the {@code mongo:client-settings} sub-element. Populates the given attribute factory with the proper
* attributes.
*
* @param element
* @param mongoClientBuilder
* Parses the mongo replica-set element.
*
* @param parserContext the parser context
* @param element the mongo element
* @param mongoBuilder the bean definition builder to populate
* @return
* @since 3.0
*/
public static boolean parseMongoClientSettings(Element element, BeanDefinitionBuilder mongoClientBuilder) {
static void parseReplicaSet(Element element, BeanDefinitionBuilder mongoBuilder) {
setPropertyValue(mongoBuilder, element, "replica-set", "replicaSetSeeds");
}
Element settingsElement = DomUtils.getChildElementByTagName(element, "client-settings");
if (settingsElement == null) {
/**
* Parses the {@code mongo:client-options} sub-element. Populates the given attribute factory with the proper
* attributes.
*
* @param element must not be {@literal null}.
* @param mongoClientBuilder must not be {@literal null}.
* @return
* @since 1.7
*/
public static boolean parseMongoClientOptions(Element element, BeanDefinitionBuilder mongoClientBuilder) {
Element optionsElement = DomUtils.getChildElementByTagName(element, "client-options");
if (optionsElement == null) {
return false;
}
BeanDefinitionBuilder clientOptionsDefBuilder = BeanDefinitionBuilder
.genericBeanDefinition(MongoClientSettingsFactoryBean.class);
.genericBeanDefinition(MongoClientOptionsFactoryBean.class);
setPropertyValue(clientOptionsDefBuilder, settingsElement, "application-name", "applicationName");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "read-preference", "readPreference");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "read-concern", "readConcern");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "write-concern", "writeConcern");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "retry-reads", "retryReads");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "retry-writes", "retryWrites");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "uuid-representation", "uUidRepresentation");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "description", "description");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "min-connections-per-host", "minConnectionsPerHost");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "connections-per-host", "connectionsPerHost");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "threads-allowed-to-block-for-connection-multiplier",
"threadsAllowedToBlockForConnectionMultiplier");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-wait-time", "maxWaitTime");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-connection-idle-time", "maxConnectionIdleTime");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "max-connection-life-time", "maxConnectionLifeTime");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "connect-timeout", "connectTimeout");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "socket-timeout", "socketTimeout");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "socket-keep-alive", "socketKeepAlive");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "read-preference", "readPreference");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "write-concern", "writeConcern");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-frequency", "heartbeatFrequency");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "min-heartbeat-frequency", "minHeartbeatFrequency");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-connect-timeout", "heartbeatConnectTimeout");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "heartbeat-socket-timeout", "heartbeatSocketTimeout");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "ssl", "ssl");
setPropertyReference(clientOptionsDefBuilder, optionsElement, "ssl-socket-factory-ref", "sslSocketFactory");
setPropertyReference(clientOptionsDefBuilder, optionsElement, "encryption-settings-ref", "autoEncryptionSettings");
setPropertyValue(clientOptionsDefBuilder, optionsElement, "server-selection-timeout", "serverSelectionTimeout");
// SocketSettings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "socket-connect-timeout", "socketConnectTimeoutMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "socket-read-timeout", "socketReadTimeoutMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "socket-receive-buffer-size", "socketReceiveBufferSize");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "socket-send-buffer-size", "socketSendBufferSize");
// Server Settings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "server-heartbeat-frequency",
"serverHeartbeatFrequencyMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "server-min-heartbeat-frequency",
"serverMinHeartbeatFrequencyMS");
// Cluster Settings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-srv-host", "clusterSrvHost");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-hosts", "clusterHosts");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-connection-mode", "clusterConnectionMode");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-type", "custerRequiredClusterType");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-local-threshold", "clusterLocalThresholdMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "cluster-server-selection-timeout",
"clusterServerSelectionTimeoutMS");
// Connection Pool Settings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-max-size", "poolMaxSize");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-min-size", "poolMinSize");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-max-wait-time", "poolMaxWaitTimeMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-max-connection-life-time",
"poolMaxConnectionLifeTimeMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-max-connection-idle-time",
"poolMaxConnectionIdleTimeMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-maintenance-initial-delay",
"poolMaintenanceInitialDelayMS");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "connection-pool-maintenance-frequency",
"poolMaintenanceFrequencyMS");
// SSL Settings
setPropertyValue(clientOptionsDefBuilder, settingsElement, "ssl-enabled", "sslEnabled");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "ssl-invalid-host-name-allowed",
"sslInvalidHostNameAllowed");
setPropertyValue(clientOptionsDefBuilder, settingsElement, "ssl-provider", "sslProvider");
// Field level encryption
setPropertyReference(clientOptionsDefBuilder, settingsElement, "encryption-settings-ref", "autoEncryptionSettings");
// and the rest
mongoClientBuilder.addPropertyValue("mongoClientSettings", clientOptionsDefBuilder.getBeanDefinition());
mongoClientBuilder.addPropertyValue("mongoClientOptions", clientOptionsDefBuilder.getBeanDefinition());
return true;
}
@@ -136,24 +118,6 @@ abstract class MongoParsingUtils {
return builder;
}
/**
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
* {@link ReadConcernPropertyEditor}.
*
* @return
* @since 3.0
*/
static BeanDefinitionBuilder getReadConcernPropertyEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<>();
customEditors.put("com.mongodb.ReadConcern", ReadConcernPropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
builder.addPropertyValue("customEditors", customEditors);
return builder;
}
/**
* One should only register one bean definition but want to have the convenience of using
* AbstractSingleBeanDefinitionParser but have the side effect of registering a 'default' property editor with the
@@ -161,7 +125,7 @@ abstract class MongoParsingUtils {
*/
static BeanDefinitionBuilder getServerAddressPropertyEditorBuilder() {
Map<String, String> customEditors = new ManagedMap<>();
Map<String, String> customEditors = new ManagedMap<String, String>();
customEditors.put("com.mongodb.ServerAddress[]",
"org.springframework.data.mongodb.config.ServerAddressPropertyEditor");
@@ -179,7 +143,7 @@ abstract class MongoParsingUtils {
*/
static BeanDefinitionBuilder getReadPreferencePropertyEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<>();
Map<String, Class<?>> customEditors = new ManagedMap<String, Class<?>>();
customEditors.put("com.mongodb.ReadPreference", ReadPreferencePropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
@@ -205,41 +169,4 @@ abstract class MongoParsingUtils {
return builder;
}
/**
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
* {@link ConnectionStringPropertyEditor}.
*
* @return
* @since 3.0
*/
static BeanDefinitionBuilder getConnectionStringPropertyEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<>();
customEditors.put("com.mongodb.ConnectionString", ConnectionStringPropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
builder.addPropertyValue("customEditors", customEditors);
return builder;
}
/**
* Returns the {@link BeanDefinitionBuilder} to build a {@link BeanDefinition} for a
* {@link ConnectionStringPropertyEditor}.
*
* @return
* @since 3.0
*/
static BeanDefinitionBuilder getUUidRepresentationEditorBuilder() {
Map<String, Class<?>> customEditors = new ManagedMap<>();
customEditors.put("org.bson.UuidRepresentation", UUidRepresentationPropertyEditor.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomEditorConfigurer.class);
builder.addPropertyValue("customEditors", customEditors);
return builder;
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2020 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
/**
* Simple helper to be able to wire the {@link PersistentEntities} from a {@link MappingMongoConverter} bean available
* in the application context.
*
* @author Oliver Gierke
* @author Mark Paluch
* @author Christoph Strobl
* @since 3.1
*/
class PersistentEntitiesFactoryBean implements FactoryBean<PersistentEntities> {
private final MappingMongoConverter converter;
/**
* Creates a new {@link PersistentEntitiesFactoryBean} for the given {@link MappingMongoConverter}.
*
* @param converter must not be {@literal null}.
*/
public PersistentEntitiesFactoryBean(MappingMongoConverter converter) {
this.converter = converter;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public PersistentEntities getObject() {
return PersistentEntities.of(converter.getMappingContext());
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return PersistentEntities.class;
}
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright 2020 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
*
* https://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.annotation.Annotation;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback;
import org.springframework.util.Assert;
/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableReactiveMongoAuditing} annotation.
*
* @author Mark Paluch
* @since 3.1
*/
class ReactiveMongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
*/
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableReactiveMongoAuditing.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
*/
@Override
protected String getAuditingHandlerBeanName() {
return "reactiveMongoAuditingHandler";
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration)
*/
@Override
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveIsNewAwareAuditingHandler.class);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
builder.addConstructorArgValue(definition.getBeanDefinition());
return configureDefaultAuditHandlerAttributes(configuration, builder);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@Override
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
BeanDefinitionRegistry registry) {
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ReactiveAuditingEntityCallback.class);
builder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
builder.getRawBeanDefinition().setSource(auditingHandlerDefinition.getSource());
registerInfrastructureBeanWithId(builder.getBeanDefinition(), ReactiveAuditingEntityCallback.class.getName(),
registry);
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright 2019 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import com.mongodb.ReadConcern;
import com.mongodb.ReadConcernLevel;
/**
* Parse a {@link String} to a {@link ReadConcern}. If it is a well know {@link String} as identified by the
* {@link ReadConcernLevel#fromString(String)}.
*
* @author Christoph Strobl
* @since 3.0
*/
public class ReadConcernPropertyEditor extends PropertyEditorSupport {
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
public void setAsText(@Nullable String readConcernString) {
if (!StringUtils.hasText(readConcernString)) {
return;
}
setValue(new ReadConcern(ReadConcernLevel.fromString(readConcernString)));
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2020 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import java.beans.PropertyEditorSupport;
import org.bson.UuidRepresentation;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Parse a {@link String} to a {@link UuidRepresentation}.
*
* @author Christoph Strobl
* @since 3.0
*/
public class UUidRepresentationPropertyEditor extends PropertyEditorSupport {
/*
* (non-Javadoc)
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
@Override
public void setAsText(@Nullable String value) {
if (!StringUtils.hasText(value)) {
return;
}
setValue(UuidRepresentation.valueOf(value));
}
}

View File

@@ -34,7 +34,7 @@ import com.mongodb.WriteConcern;
public class WriteConcernPropertyEditor extends PropertyEditorSupport {
/**
* Parse a string to a {@link WriteConcern}.
* Parse a string to a List<ServerAddress>
*/
@Override
public void setAsText(@Nullable String writeConcernString) {
@@ -51,5 +51,6 @@ public class WriteConcernPropertyEditor extends PropertyEditorSupport {
// pass on the string to the constructor
setValue(new WriteConcern(writeConcernString));
}
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AllArgsConstructor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -28,7 +30,6 @@ import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.CountOperation;
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.QueryMapper;
@@ -48,18 +49,12 @@ import org.springframework.util.ObjectUtils;
* @author Mark Paluch
* @since 2.1
*/
@AllArgsConstructor
class AggregationUtil {
QueryMapper queryMapper;
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
AggregationUtil(QueryMapper queryMapper,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
this.queryMapper = queryMapper;
this.mappingContext = mappingContext;
}
/**
* Prepare the {@link AggregationOperationContext} for a given aggregation by either returning the context itself it
* is not {@literal null}, create a {@link TypeBasedAggregationOperationContext} if the aggregation contains type
@@ -76,17 +71,12 @@ class AggregationUtil {
return context;
}
if (!(aggregation instanceof TypedAggregation)) {
return Aggregation.DEFAULT_CONTEXT;
if (aggregation instanceof TypedAggregation) {
return new TypeBasedAggregationOperationContext(((TypedAggregation) aggregation).getInputType(), mappingContext,
queryMapper);
}
Class<?> inputType = ((TypedAggregation) aggregation).getInputType();
if (aggregation.getPipeline().containsUnionWith()) {
return new RelaxedTypeBasedAggregationOperationContext(inputType, mappingContext, queryMapper);
}
return new TypeBasedAggregationOperationContext(inputType, mappingContext, queryMapper);
return Aggregation.DEFAULT_CONTEXT;
}
/**

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.EqualsAndHashCode;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
@@ -25,7 +27,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.messaging.Message;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import com.mongodb.client.model.changestream.ChangeStreamDocument;
import com.mongodb.client.model.changestream.OperationType;
@@ -38,6 +39,7 @@ import com.mongodb.client.model.changestream.OperationType;
* @author Mark Paluch
* @since 2.1
*/
@EqualsAndHashCode
public class ChangeStreamEvent<T> {
@SuppressWarnings("rawtypes") //
@@ -185,8 +187,8 @@ public class ChangeStreamEvent<T> {
return CONVERTED_UPDATER.compareAndSet(this, null, result) ? result : CONVERTED_UPDATER.get(this);
}
throw new IllegalArgumentException(
String.format("No converter found capable of converting %s to %s", fullDocument.getClass(), targetType));
throw new IllegalArgumentException(String.format("No converter found capable of converting %s to %s",
fullDocument.getClass(), targetType));
}
/*
@@ -197,27 +199,4 @@ public class ChangeStreamEvent<T> {
public String toString() {
return "ChangeStreamEvent {" + "raw=" + raw + ", targetType=" + targetType + '}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ChangeStreamEvent<?> that = (ChangeStreamEvent<?>) o;
if (!ObjectUtils.nullSafeEquals(this.raw, that.raw)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.targetType, that.targetType);
}
@Override
public int hashCode() {
int result = raw != null ? raw.hashCode() : 0;
result = 31 * result + ObjectUtils.nullSafeHashCode(targetType);
return result;
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.EqualsAndHashCode;
import java.time.Instant;
import java.util.Arrays;
import java.util.Optional;
@@ -23,6 +25,7 @@ import org.bson.BsonDocument;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.lang.Nullable;
@@ -42,6 +45,7 @@ import com.mongodb.client.model.changestream.FullDocument;
* @author Mark Paluch
* @since 2.1
*/
@EqualsAndHashCode
public class ChangeStreamOptions {
private @Nullable Object filter;
@@ -152,44 +156,6 @@ public class ChangeStreamOptions {
+ ObjectUtils.nullSafeClassName(timestamp));
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ChangeStreamOptions that = (ChangeStreamOptions) o;
if (!ObjectUtils.nullSafeEquals(this.filter, that.filter)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.resumeToken, that.resumeToken)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.fullDocumentLookup, that.fullDocumentLookup)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.collation, that.collation)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.resumeTimestamp, that.resumeTimestamp)) {
return false;
}
return resume == that.resume;
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(filter);
result = 31 * result + ObjectUtils.nullSafeHashCode(resumeToken);
result = 31 * result + ObjectUtils.nullSafeHashCode(fullDocumentLookup);
result = 31 * result + ObjectUtils.nullSafeHashCode(collation);
result = 31 * result + ObjectUtils.nullSafeHashCode(resumeTimestamp);
result = 31 * result + ObjectUtils.nullSafeHashCode(resume);
return result;
}
/**
* @author Christoph Strobl
* @since 2.2

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.RequiredArgsConstructor;
import java.util.Optional;
import org.springframework.data.mongodb.core.query.Collation;
@@ -310,6 +312,7 @@ public class CollectionOptions {
* @author Andreas Zink
* @since 2.1
*/
@RequiredArgsConstructor
public static class ValidationOptions {
private static final ValidationOptions NONE = new ValidationOptions(null, null, null);
@@ -318,13 +321,6 @@ public class CollectionOptions {
private final @Nullable ValidationLevel validationLevel;
private final @Nullable ValidationAction validationAction;
public ValidationOptions(Validator validator, ValidationLevel validationLevel, ValidationAction validationAction) {
this.validator = validator;
this.validationLevel = validationLevel;
this.validationAction = validationAction;
}
/**
* Create an empty {@link ValidationOptions}.
*

View File

@@ -1,227 +0,0 @@
/*
* Copyright 2019-2020 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.springframework.data.geo.Point;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* Value object representing a count query. Count queries using {@code $near} or {@code $nearSphere} require a rewrite
* to {@code $geoWithin}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0
*/
class CountQuery {
private Document source;
private CountQuery(Document source) {
this.source = source;
}
public static CountQuery of(Document source) {
return new CountQuery(source);
}
/**
* Returns the query {@link Document} that can be used with {@code countDocuments()}. Potentially rewrites the query
* to be usable with {@code countDocuments()}.
*
* @return the query {@link Document} that can be used with {@code countDocuments()}.
*/
public Document toQueryDocument() {
if (!requiresRewrite(source)) {
return source;
}
Document target = new Document();
for (Map.Entry<String, Object> entry : source.entrySet()) {
if (entry.getValue() instanceof Document && requiresRewrite(entry.getValue())) {
Document theValue = (Document) entry.getValue();
target.putAll(createGeoWithin(entry.getKey(), theValue, source.get("$and")));
continue;
}
if (entry.getValue() instanceof Collection && requiresRewrite(entry.getValue())) {
Collection<?> source = (Collection<?>) entry.getValue();
target.put(entry.getKey(), rewriteCollection(source));
continue;
}
if ("$and".equals(entry.getKey()) && target.containsKey("$and")) {
// Expect $and to be processed with Document and createGeoWithin.
continue;
}
target.put(entry.getKey(), entry.getValue());
}
return target;
}
/**
* @param valueToInspect
* @return {@code true} if the enclosing element needs to be rewritten.
*/
private boolean requiresRewrite(Object valueToInspect) {
if (valueToInspect instanceof Document) {
return requiresRewrite((Document) valueToInspect);
}
if (valueToInspect instanceof Collection) {
return requiresRewrite((Collection) valueToInspect);
}
return false;
}
private boolean requiresRewrite(Collection<?> collection) {
for (Object o : collection) {
if (o instanceof Document && requiresRewrite((Document) o)) {
return true;
}
}
return false;
}
private boolean requiresRewrite(Document document) {
if (containsNear(document)) {
return true;
}
for (Object entry : document.values()) {
if (requiresRewrite(entry)) {
return true;
}
}
return false;
}
private Collection<Object> rewriteCollection(Collection<?> source) {
Collection<Object> rewrittenCollection = new ArrayList<>(source.size());
for (Object item : source) {
if (item instanceof Document && requiresRewrite(item)) {
rewrittenCollection.add(CountQuery.of((Document) item).toQueryDocument());
} else {
rewrittenCollection.add(item);
}
}
return rewrittenCollection;
}
/**
* Rewrite the near query for field {@code key} to {@code $geoWithin}.
*
* @param key the queried field.
* @param source source {@link Document}.
* @param $and potentially existing {@code $and} condition.
* @return the rewritten query {@link Document}.
*/
private static Document createGeoWithin(String key, Document source, @Nullable Object $and) {
boolean spheric = source.containsKey("$nearSphere");
Object $near = spheric ? source.get("$nearSphere") : source.get("$near");
Number maxDistance = source.containsKey("$maxDistance") ? (Number) source.get("$maxDistance") : Double.MAX_VALUE;
List<Object> $centerMax = Arrays.asList(toCenterCoordinates($near), maxDistance);
Document $geoWithinMax = new Document("$geoWithin",
new Document(spheric ? "$centerSphere" : "$center", $centerMax));
if (!containsNearWithMinDistance(source)) {
return new Document(key, $geoWithinMax);
}
Number minDistance = (Number) source.get("$minDistance");
List<Object> $centerMin = Arrays.asList(toCenterCoordinates($near), minDistance);
Document $geoWithinMin = new Document("$geoWithin",
new Document(spheric ? "$centerSphere" : "$center", $centerMin));
List<Document> criteria = new ArrayList<>();
if ($and != null) {
if ($and instanceof Collection) {
criteria.addAll((Collection) $and);
} else {
throw new IllegalArgumentException(
"Cannot rewrite query as it contains an '$and' element that is not a Collection!: Offending element: "
+ $and);
}
}
criteria.add(new Document("$nor", Collections.singletonList(new Document(key, $geoWithinMin))));
criteria.add(new Document(key, $geoWithinMax));
return new Document("$and", criteria);
}
private static boolean containsNear(Document source) {
return source.containsKey("$near") || source.containsKey("$nearSphere");
}
private static boolean containsNearWithMinDistance(Document source) {
if (!containsNear(source)) {
return false;
}
return source.containsKey("$minDistance");
}
private static Object toCenterCoordinates(Object value) {
if (ObjectUtils.isArray(value)) {
return value;
}
if (value instanceof Point) {
return Arrays.asList(((Point) value).getX(), ((Point) value).getY());
}
if (value instanceof Document && ((Document) value).containsKey("x")) {
Document point = (Document) value;
return Arrays.asList(point.get("x"), point.get("y"));
}
return value;
}
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.NonNull;
import lombok.Value;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -24,13 +27,11 @@ import java.util.stream.Collectors;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
@@ -45,9 +46,7 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.util.Pair;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.WriteConcern;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.MongoCollection;
@@ -63,8 +62,6 @@ import com.mongodb.client.model.*;
* @author Minsu Kim
* @author Jens Schauder
* @author Michail Nikolaev
* @author Roman Puchkovskiy
* @author Jacob Botuck
* @since 1.9
*/
class DefaultBulkOperations implements BulkOperations {
@@ -74,6 +71,7 @@ class DefaultBulkOperations implements BulkOperations {
private final BulkOperationContext bulkOperationContext;
private final List<SourceAwareWriteModelHolder> models = new ArrayList<>();
private PersistenceExceptionTranslator exceptionTranslator;
private @Nullable WriteConcern defaultWriteConcern;
private BulkWriteOptions bulkOptions;
@@ -97,9 +95,19 @@ class DefaultBulkOperations implements BulkOperations {
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
this.bulkOperationContext = bulkOperationContext;
this.exceptionTranslator = new MongoExceptionTranslator();
this.bulkOptions = getBulkWriteOptions(bulkOperationContext.getBulkMode());
}
/**
* Configures the {@link PersistenceExceptionTranslator} to be used. Defaults to {@link MongoExceptionTranslator}.
*
* @param exceptionTranslator can be {@literal null}.
*/
public void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator == null ? new MongoExceptionTranslator() : exceptionTranslator;
}
/**
* Configures the default {@link WriteConcern} to be used. Defaults to {@literal null}.
*
@@ -292,7 +300,6 @@ class DefaultBulkOperations implements BulkOperations {
Assert.state(result != null, "Result must not be null.");
models.forEach(this::maybeEmitAfterSaveEvent);
models.forEach(this::maybeInvokeAfterSaveCallback);
return result;
} finally {
@@ -306,26 +313,11 @@ class DefaultBulkOperations implements BulkOperations {
collection = collection.withWriteConcern(defaultWriteConcern);
}
try {
return collection.bulkWrite( //
models.stream() //
.map(this::extractAndMapWriteModel) //
.collect(Collectors.toList()), //
bulkOptions);
} catch (RuntimeException ex) {
if (ex instanceof MongoBulkWriteException) {
MongoBulkWriteException mongoBulkWriteException = (MongoBulkWriteException) ex;
if (mongoBulkWriteException.getWriteConcernError() != null) {
throw new DataIntegrityViolationException(ex.getMessage(), ex);
}
throw new BulkOperationException(ex.getMessage(), mongoBulkWriteException);
}
throw ex;
}
return collection.bulkWrite( //
models.stream() //
.map(this::extractAndMapWriteModel) //
.collect(Collectors.toList()), //
bulkOptions);
}
private WriteModel<Document> extractAndMapWriteModel(SourceAwareWriteModelHolder it) {
@@ -429,52 +421,38 @@ class DefaultBulkOperations implements BulkOperations {
models.add(new SourceAwareWriteModelHolder(source, model));
}
private void maybeEmitBeforeSaveEvent(SourceAwareWriteModelHolder holder) {
private void maybeEmitBeforeSaveEvent(SourceAwareWriteModelHolder it) {
if (holder.getModel() instanceof InsertOneModel) {
if (it.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeEmitEvent(new BeforeSaveEvent<>(holder.getSource(), target, collectionName));
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = ((InsertOneModel<Document>) it.getModel()).getDocument();
maybeEmitEvent(new BeforeSaveEvent<>(it.getSource(), target, collectionName));
} else if (it.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeEmitEvent(new BeforeSaveEvent<>(holder.getSource(), target, collectionName));
Document target = ((ReplaceOneModel<Document>) it.getModel()).getReplacement();
maybeEmitEvent(new BeforeSaveEvent<>(it.getSource(), target, collectionName));
}
}
private void maybeEmitAfterSaveEvent(SourceAwareWriteModelHolder holder) {
private void maybeEmitAfterSaveEvent(SourceAwareWriteModelHolder it) {
if (holder.getModel() instanceof InsertOneModel) {
if (it.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeEmitEvent(new AfterSaveEvent<>(holder.getSource(), target, collectionName));
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = ((InsertOneModel<Document>) it.getModel()).getDocument();
maybeEmitEvent(new AfterSaveEvent<>(it.getSource(), target, collectionName));
} else if (it.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeEmitEvent(new AfterSaveEvent<>(holder.getSource(), target, collectionName));
}
}
private void maybeInvokeAfterSaveCallback(SourceAwareWriteModelHolder holder) {
if (holder.getModel() instanceof InsertOneModel) {
Document target = ((InsertOneModel<Document>) holder.getModel()).getDocument();
maybeInvokeAfterSaveCallback(holder.getSource(), target);
} else if (holder.getModel() instanceof ReplaceOneModel) {
Document target = ((ReplaceOneModel<Document>) holder.getModel()).getReplacement();
maybeInvokeAfterSaveCallback(holder.getSource(), target);
Document target = ((ReplaceOneModel<Document>) it.getModel()).getReplacement();
maybeEmitEvent(new AfterSaveEvent<>(it.getSource(), target, collectionName));
}
}
private <E extends MongoMappingEvent<T>, T> E maybeEmitEvent(E event) {
if (bulkOperationContext.getEventPublisher() == null) {
return event;
if (null != bulkOperationContext.getEventPublisher()) {
bulkOperationContext.getEventPublisher().publishEvent(event);
}
bulkOperationContext.getEventPublisher().publishEvent(event);
return event;
}
@@ -497,16 +475,6 @@ class DefaultBulkOperations implements BulkOperations {
collectionName);
}
private Object maybeInvokeAfterSaveCallback(Object value, Document mappedDocument) {
if (bulkOperationContext.getEntityCallbacks() == null) {
return value;
}
return bulkOperationContext.getEntityCallbacks().callback(AfterSaveCallback.class, value, mappedDocument,
collectionName);
}
private static BulkWriteOptions getBulkWriteOptions(BulkMode bulkMode) {
BulkWriteOptions options = new BulkWriteOptions();
@@ -552,93 +520,15 @@ class DefaultBulkOperations implements BulkOperations {
* @author Christoph Strobl
* @since 2.0
*/
static final class BulkOperationContext {
@Value
static class BulkOperationContext {
private final BulkMode bulkMode;
private final Optional<? extends MongoPersistentEntity<?>> entity;
private final QueryMapper queryMapper;
private final UpdateMapper updateMapper;
private final ApplicationEventPublisher eventPublisher;
private final EntityCallbacks entityCallbacks;
BulkOperationContext(BulkOperations.BulkMode bulkMode, Optional<? extends MongoPersistentEntity<?>> entity,
QueryMapper queryMapper, UpdateMapper updateMapper, ApplicationEventPublisher eventPublisher,
EntityCallbacks entityCallbacks) {
this.bulkMode = bulkMode;
this.entity = entity;
this.queryMapper = queryMapper;
this.updateMapper = updateMapper;
this.eventPublisher = eventPublisher;
this.entityCallbacks = entityCallbacks;
}
public BulkMode getBulkMode() {
return this.bulkMode;
}
public Optional<? extends MongoPersistentEntity<?>> getEntity() {
return this.entity;
}
public QueryMapper getQueryMapper() {
return this.queryMapper;
}
public UpdateMapper getUpdateMapper() {
return this.updateMapper;
}
public ApplicationEventPublisher getEventPublisher() {
return this.eventPublisher;
}
public EntityCallbacks getEntityCallbacks() {
return this.entityCallbacks;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
BulkOperationContext that = (BulkOperationContext) o;
if (bulkMode != that.bulkMode)
return false;
if (!ObjectUtils.nullSafeEquals(this.entity, that.entity)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.queryMapper, that.queryMapper)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.updateMapper, that.updateMapper)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.eventPublisher, that.eventPublisher)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.entityCallbacks, that.entityCallbacks);
}
@Override
public int hashCode() {
int result = bulkMode != null ? bulkMode.hashCode() : 0;
result = 31 * result + ObjectUtils.nullSafeHashCode(entity);
result = 31 * result + ObjectUtils.nullSafeHashCode(queryMapper);
result = 31 * result + ObjectUtils.nullSafeHashCode(updateMapper);
result = 31 * result + ObjectUtils.nullSafeHashCode(eventPublisher);
result = 31 * result + ObjectUtils.nullSafeHashCode(entityCallbacks);
return result;
}
public String toString() {
return "DefaultBulkOperations.BulkOperationContext(bulkMode=" + this.getBulkMode() + ", entity="
+ this.getEntity() + ", queryMapper=" + this.getQueryMapper() + ", updateMapper=" + this.getUpdateMapper()
+ ", eventPublisher=" + this.getEventPublisher() + ", entityCallbacks=" + this.getEntityCallbacks() + ")";
}
@NonNull BulkMode bulkMode;
@NonNull Optional<? extends MongoPersistentEntity<?>> entity;
@NonNull QueryMapper queryMapper;
@NonNull UpdateMapper updateMapper;
ApplicationEventPublisher eventPublisher;
EntityCallbacks entityCallbacks;
}
/**
@@ -647,50 +537,10 @@ class DefaultBulkOperations implements BulkOperations {
* @since 2.2
* @author Christoph Strobl
*/
private static final class SourceAwareWriteModelHolder {
@Value
private static class SourceAwareWriteModelHolder {
private final Object source;
private final WriteModel<Document> model;
SourceAwareWriteModelHolder(Object source, WriteModel<Document> model) {
this.source = source;
this.model = model;
}
public Object getSource() {
return this.source;
}
public WriteModel<Document> getModel() {
return this.model;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SourceAwareWriteModelHolder that = (SourceAwareWriteModelHolder) o;
if (!ObjectUtils.nullSafeEquals(this.source, that.source)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.model, that.model);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(model);
result = 31 * result + ObjectUtils.nullSafeHashCode(source);
return result;
}
public String toString() {
return "DefaultBulkOperations.SourceAwareWriteModelHolder(source=" + this.getSource() + ", model="
+ this.getModel() + ")";
}
Object source;
WriteModel<Document> model;
}
}

View File

@@ -21,7 +21,7 @@ import java.util.List;
import org.bson.Document;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
@@ -64,7 +64,7 @@ public class DefaultIndexOperations implements IndexOperations {
* {@link DefaultIndexOperations#DefaultIndexOperations(MongoOperations, String, Class)}.
*/
@Deprecated
public DefaultIndexOperations(MongoDatabaseFactory mongoDbFactory, String collectionName, QueryMapper queryMapper) {
public DefaultIndexOperations(MongoDbFactory mongoDbFactory, String collectionName, QueryMapper queryMapper) {
this(mongoDbFactory, collectionName, queryMapper, null);
}
@@ -80,7 +80,7 @@ public class DefaultIndexOperations implements IndexOperations {
* {@link DefaultIndexOperations#DefaultIndexOperations(MongoOperations, String, Class)}.
*/
@Deprecated
public DefaultIndexOperations(MongoDatabaseFactory mongoDbFactory, String collectionName, QueryMapper queryMapper,
public DefaultIndexOperations(MongoDbFactory mongoDbFactory, String collectionName, QueryMapper queryMapper,
@Nullable Class<?> type) {
Assert.notNull(mongoDbFactory, "MongoDbFactory must not be null!");

View File

@@ -15,13 +15,13 @@
*/
package org.springframework.data.mongodb.core;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
/**
* {@link IndexOperationsProvider} to obtain {@link IndexOperations} from a given {@link MongoDatabaseFactory}.
* {@link IndexOperationsProvider} to obtain {@link IndexOperations} from a given {@link MongoDbFactory}.
*
* @author Mark Paluch
* @author Christoph Strobl
@@ -29,14 +29,14 @@ import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
*/
class DefaultIndexOperationsProvider implements IndexOperationsProvider {
private final MongoDatabaseFactory mongoDbFactory;
private final MongoDbFactory mongoDbFactory;
private final QueryMapper mapper;
/**
* @param mongoDbFactory must not be {@literal null}.
* @param mapper must not be {@literal null}.
*/
DefaultIndexOperationsProvider(MongoDatabaseFactory mongoDbFactory, QueryMapper mapper) {
DefaultIndexOperationsProvider(MongoDbFactory mongoDbFactory, QueryMapper mapper) {
this.mongoDbFactory = mongoDbFactory;
this.mapper = mapper;

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
@@ -51,15 +55,12 @@ import org.springframework.util.MultiValueMap;
* @see MongoTemplate
* @see ReactiveMongoTemplate
*/
@RequiredArgsConstructor
class EntityOperations {
private static final String ID_FIELD = "_id";
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context;
EntityOperations(MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context) {
this.context = context;
}
private final @NonNull MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context;
/**
* Creates a new {@link Entity} for the given bean.
@@ -68,7 +69,7 @@ class EntityOperations {
* @return new instance of {@link Entity}.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
<T> Entity<T> forEntity(T entity) {
public <T> Entity<T> forEntity(T entity) {
Assert.notNull(entity, "Bean must not be null!");
@@ -91,7 +92,7 @@ class EntityOperations {
* @return new instance of {@link AdaptibleEntity}.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
<T> AdaptibleEntity<T> forEntity(T entity, ConversionService conversionService) {
public <T> AdaptibleEntity<T> forEntity(T entity, ConversionService conversionService) {
Assert.notNull(entity, "Bean must not be null!");
Assert.notNull(conversionService, "ConversionService must not be null!");
@@ -345,14 +346,11 @@ class EntityOperations {
Number getVersion();
}
@RequiredArgsConstructor
private static class UnmappedEntity<T extends Map<String, Object>> implements AdaptibleEntity<T> {
private final T map;
protected UnmappedEntity(T map) {
this.map = map;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.EntityOperations.PersistableSource#getIdPropertyName()
@@ -462,7 +460,7 @@ class EntityOperations {
private static class SimpleMappedEntity<T extends Map<String, Object>> extends UnmappedEntity<T> {
protected SimpleMappedEntity(T map) {
SimpleMappedEntity(T map) {
super(map);
}
@@ -485,19 +483,12 @@ class EntityOperations {
}
}
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
private static class MappedEntity<T> implements Entity<T> {
private final MongoPersistentEntity<?> entity;
private final IdentifierAccessor idAccessor;
private final PersistentPropertyAccessor<T> propertyAccessor;
protected MappedEntity(MongoPersistentEntity<?> entity, IdentifierAccessor idAccessor,
PersistentPropertyAccessor<T> propertyAccessor) {
this.entity = entity;
this.idAccessor = idAccessor;
this.propertyAccessor = propertyAccessor;
}
private final @NonNull MongoPersistentEntity<?> entity;
private final @NonNull IdentifierAccessor idAccessor;
private final @NonNull PersistentPropertyAccessor<T> propertyAccessor;
private static <T> MappedEntity<T> of(T bean,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context) {
@@ -768,12 +759,11 @@ class EntityOperations {
* {@link TypedOperations} for generic entities that are not represented with {@link PersistentEntity} (e.g. custom
* conversions).
*/
@RequiredArgsConstructor
enum UntypedOperations implements TypedOperations<Object> {
INSTANCE;
UntypedOperations() {}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T> TypedOperations<T> instance() {
return (TypedOperations) INSTANCE;
@@ -808,13 +798,10 @@ class EntityOperations {
*
* @param <T>
*/
@RequiredArgsConstructor
static class TypedEntityOperations<T> implements TypedOperations<T> {
private final MongoPersistentEntity<T> entity;
protected TypedEntityOperations(MongoPersistentEntity<T> entity) {
this.entity = entity;
}
private final @NonNull MongoPersistentEntity<T> entity;
/*
* (non-Javadoc)

View File

@@ -90,8 +90,8 @@ public interface ExecutableAggregationOperation {
* Apply pipeline operations as specified and stream all matching elements. <br />
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable}
*
* @return a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to be closed.
* Never {@literal null}.
* @return a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs
* to be closed. Never {@literal null}.
*/
CloseableIterator<T> stream();
}

View File

@@ -15,10 +15,16 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -29,13 +35,10 @@ import org.springframework.util.StringUtils;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableAggregationOperationSupport implements ExecutableAggregationOperation {
private final MongoTemplate template;
ExecutableAggregationOperationSupport(MongoTemplate template) {
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javadoc)
@@ -53,21 +56,15 @@ class ExecutableAggregationOperationSupport implements ExecutableAggregationOper
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableAggregationSupport<T>
implements AggregationWithAggregation<T>, ExecutableAggregation<T>, TerminatingAggregation<T> {
private final MongoTemplate template;
private final Class<T> domainType;
private final Aggregation aggregation;
private final String collection;
public ExecutableAggregationSupport(MongoTemplate template, Class<T> domainType, Aggregation aggregation,
String collection) {
this.template = template;
this.domainType = domainType;
this.aggregation = aggregation;
this.collection = collection;
}
@NonNull MongoTemplate template;
@NonNull Class<T> domainType;
@Nullable Aggregation aggregation;
@Nullable String collection;
/*
* (non-Javadoc)

View File

@@ -21,7 +21,6 @@ import java.util.stream.Stream;
import org.springframework.dao.DataAccessException;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.lang.Nullable;
@@ -44,7 +43,7 @@ import com.mongodb.client.MongoCollection;
* query(Human.class)
* .inCollection("star-wars")
* .as(Jedi.class)
* .matching(where("firstname").is("luke"))
* .matching(query(where("firstname").is("luke")))
* .all();
* </code>
* </pre>
@@ -118,8 +117,8 @@ public interface ExecutableFindOperation {
/**
* Stream all matching elements.
*
* @return a {@link Stream} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to be closed. Never
* {@literal null}.
* @return a {@link Stream} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to be
* closed. Never {@literal null}.
*/
Stream<T> stream();
@@ -171,18 +170,6 @@ public interface ExecutableFindOperation {
*/
TerminatingFind<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingFind}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingFind<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
/**
* Set the filter query for the geoNear execution.
*
@@ -304,21 +291,9 @@ public interface ExecutableFindOperation {
*
* @param query must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if query is {@literal null}.
* @throws IllegalArgumentException if resultType is {@literal null}.
*/
TerminatingDistinct<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingDistinct<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -15,11 +15,17 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.bson.Document;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
@@ -41,15 +47,12 @@ import com.mongodb.client.FindIterable;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableFindOperationSupport implements ExecutableFindOperation {
private static final Query ALL_QUERY = new Query();
private final MongoTemplate template;
ExecutableFindOperationSupport(MongoTemplate template) {
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javadoc)
@@ -68,23 +71,16 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableFindSupport<T>
implements ExecutableFind<T>, FindWithCollection<T>, FindWithProjection<T>, FindWithQuery<T> {
private final MongoTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
@Nullable private final String collection;
private final Query query;
ExecutableFindSupport(MongoTemplate template, Class<?> domainType, Class<T> returnType,
String collection, Query query) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.collection = collection;
this.query = query;
}
@NonNull MongoTemplate template;
@NonNull Class<?> domainType;
Class<T> returnType;
@Nullable String collection;
Query query;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,11 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.ArrayList;
import java.util.Collection;
@@ -32,13 +37,10 @@ import com.mongodb.bulk.BulkWriteResult;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
private final MongoTemplate template;
ExecutableInsertOperationSupport(MongoTemplate template) {
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javadoc)
@@ -56,20 +58,14 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableInsertSupport<T> implements ExecutableInsert<T> {
private final MongoTemplate template;
private final Class<T> domainType;
@Nullable private final String collection;
@Nullable private final BulkMode bulkMode;
ExecutableInsertSupport(MongoTemplate template, Class<T> domainType, String collection, BulkMode bulkMode) {
this.template = template;
this.domainType = domainType;
this.collection = collection;
this.bulkMode = bulkMode;
}
@NonNull MongoTemplate template;
@NonNull Class<T> domainType;
@Nullable String collection;
@Nullable BulkMode bulkMode;
/*
* (non-Javadoc)

View File

@@ -19,7 +19,6 @@ import java.util.List;
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
/**
@@ -31,7 +30,7 @@ import org.springframework.data.mongodb.core.query.Query;
* The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
* via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
* collection name for the execution.
*
*
* <pre>
* <code>
* mapReduce(Human.class)
@@ -45,7 +44,6 @@ import org.springframework.data.mongodb.core.query.Query;
* </pre>
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.1
*/
public interface ExecutableMapReduceOperation {
@@ -148,18 +146,6 @@ public interface ExecutableMapReduceOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingMapReduce<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingMapReduce}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingMapReduce<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
@@ -29,17 +32,12 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @since 2.1
*/
@RequiredArgsConstructor
class ExecutableMapReduceOperationSupport implements ExecutableMapReduceOperation {
private static final Query ALL_QUERY = new Query();
private final MongoTemplate template;
ExecutableMapReduceOperationSupport(MongoTemplate template) {
Assert.notNull(template, "Template must not be null!");
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javascript)

View File

@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.core;
import java.util.List;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.client.result.DeleteResult;
@@ -120,18 +119,6 @@ public interface ExecutableRemoveOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingRemove<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingRemove}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingRemove<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -15,6 +15,11 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.util.List;
import org.springframework.data.mongodb.core.query.Query;
@@ -31,15 +36,12 @@ import com.mongodb.client.result.DeleteResult;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation {
private static final Query ALL_QUERY = new Query();
private final MongoTemplate tempate;
public ExecutableRemoveOperationSupport(MongoTemplate tempate) {
this.tempate = tempate;
}
private final @NonNull MongoTemplate tempate;
/*
* (non-Javadoc)
@@ -57,19 +59,14 @@ class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableRemoveSupport<T> implements ExecutableRemove<T>, RemoveWithCollection<T> {
private final MongoTemplate template;
private final Class<T> domainType;
private final Query query;
@Nullable private final String collection;
public ExecutableRemoveSupport(MongoTemplate template, Class<T> domainType, Query query, String collection) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.collection = collection;
}
@NonNull MongoTemplate template;
@NonNull Class<T> domainType;
Query query;
@Nullable String collection;
/*
* (non-Javadoc)

View File

@@ -17,11 +17,8 @@ package org.springframework.data.mongodb.core;
import java.util.Optional;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
import com.mongodb.client.result.UpdateResult;
@@ -154,16 +151,13 @@ public interface ExecutableUpdateOperation {
interface UpdateWithUpdate<T> {
/**
* Set the {@link UpdateDefinition} to be applied.
* Set the {@link Update} to be applied.
*
* @param update must not be {@literal null}.
* @return new instance of {@link TerminatingUpdate}.
* @throws IllegalArgumentException if update is {@literal null}.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
TerminatingUpdate<T> apply(UpdateDefinition update);
TerminatingUpdate<T> apply(Update update);
/**
* Specify {@code replacement} object.
@@ -211,18 +205,6 @@ public interface ExecutableUpdateOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
UpdateWithUpdate<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link UpdateWithUpdate}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default UpdateWithUpdate<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -15,8 +15,13 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -30,15 +35,12 @@ import com.mongodb.client.result.UpdateResult;
* @author Mark Paluch
* @since 2.0
*/
@RequiredArgsConstructor
class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
private static final Query ALL_QUERY = new Query();
private final MongoTemplate template;
ExecutableUpdateOperationSupport(MongoTemplate template) {
this.template = template;
}
private final @NonNull MongoTemplate template;
/*
* (non-Javadoc)
@@ -56,41 +58,28 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ExecutableUpdateSupport<T>
implements ExecutableUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T>,
FindAndReplaceWithOptions<T>, TerminatingFindAndReplace<T>, FindAndReplaceWithProjection<T> {
private final MongoTemplate template;
private final Class domainType;
private final Query query;
@Nullable private final UpdateDefinition update;
@Nullable private final String collection;
@Nullable private final FindAndModifyOptions findAndModifyOptions;
@Nullable private final FindAndReplaceOptions findAndReplaceOptions;
@Nullable private final Object replacement;
private final Class<T> targetType;
ExecutableUpdateSupport(MongoTemplate template, Class domainType, Query query, UpdateDefinition update,
String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions,
Object replacement, Class<T> targetType) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.update = update;
this.collection = collection;
this.findAndModifyOptions = findAndModifyOptions;
this.findAndReplaceOptions = findAndReplaceOptions;
this.replacement = replacement;
this.targetType = targetType;
}
@NonNull MongoTemplate template;
@NonNull Class domainType;
Query query;
@Nullable Update update;
@Nullable String collection;
@Nullable FindAndModifyOptions findAndModifyOptions;
@Nullable FindAndReplaceOptions findAndReplaceOptions;
@Nullable Object replacement;
@NonNull Class<T> targetType;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.UpdateWithUpdate#apply(org.springframework.data.mongodb.core.query.UpdateDefinition)
* @see org.springframework.data.mongodb.core.ExecutableUpdateOperation.UpdateWithUpdate#apply(Update)
*/
@Override
public TerminatingUpdate<T> apply(UpdateDefinition update) {
public TerminatingUpdate<T> apply(Update update) {
Assert.notNull(update, "Update must not be null!");

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.List;
@@ -24,6 +27,8 @@ import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.util.StreamUtils;
import com.mongodb.client.model.Filters;
/**
* A MongoDB document in its mapped state. I.e. after a source document has been mapped using mapping information of the
* entity the source document was supposed to represent.
@@ -31,20 +36,13 @@ import org.springframework.data.util.StreamUtils;
* @author Oliver Gierke
* @since 2.1
*/
@RequiredArgsConstructor(staticName = "of")
public class MappedDocument {
private static final String ID_FIELD = "_id";
private static final Document ID_ONLY_PROJECTION = new Document(ID_FIELD, 1);
private final Document document;
private MappedDocument(Document document) {
this.document = document;
}
public static MappedDocument of(Document document) {
return new MappedDocument(document);
}
private final @Getter Document document;
public static Document getIdOnlyProjection() {
return ID_ONLY_PROJECTION;
@@ -82,21 +80,13 @@ public class MappedDocument {
}
public Bson getIdFilter() {
return new Document(ID_FIELD, document.get(ID_FIELD));
}
public Object get(String key) {
return document.get(key);
return Filters.eq(ID_FIELD, document.get(ID_FIELD));
}
public UpdateDefinition updateWithoutId() {
return new MappedUpdate(Update.fromDocument(document, ID_FIELD));
}
public Document getDocument() {
return this.document;
}
/**
* An {@link UpdateDefinition} that indicates that the {@link #getUpdateObject() update object} has already been
* mapped to the specific domain type.

View File

@@ -20,7 +20,7 @@ import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.Assert;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
/**
@@ -34,13 +34,24 @@ import com.mongodb.client.MongoDatabase;
@ManagedResource(description = "Mongo Admin Operations")
public class MongoAdmin implements MongoAdminOperations {
private final MongoClient mongoClient;
private final Object mongoClient;
/**
* @param mongoClient
* @deprecated since 2.2 in favor of {@link MongoAdmin#MongoAdmin(com.mongodb.client.MongoClient)}.
*/
@Deprecated
public MongoAdmin(MongoClient mongoClient) {
Assert.notNull(mongoClient, "MongoClient must not be null!");
this.mongoClient = mongoClient;
}
/**
* @param client the underlying {@link com.mongodb.client.MongoClient} used for data access.
* @since 2.2
*/
public MongoAdmin(MongoClient client) {
public MongoAdmin(com.mongodb.client.MongoClient client) {
Assert.notNull(client, "Client must not be null!");
this.mongoClient = client;
@@ -77,6 +88,11 @@ public class MongoAdmin implements MongoAdminOperations {
}
MongoDatabase getDB(String databaseName) {
return mongoClient.getDatabase(databaseName);
if (mongoClient instanceof MongoClient) {
return ((MongoClient) mongoClient).getDatabase(databaseName);
}
return ((com.mongodb.client.MongoClient) mongoClient).getDatabase(databaseName);
}
}

View File

@@ -16,72 +16,70 @@
package org.springframework.data.mongodb.core;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.bson.UuidRepresentation;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.SpringDataMongoDB;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.connection.ClusterSettings;
import com.mongodb.connection.ConnectionPoolSettings;
import com.mongodb.connection.ServerSettings;
import com.mongodb.connection.SocketSettings;
import com.mongodb.connection.SslSettings;
import com.mongodb.event.ClusterListener;
/**
* Convenient factory for configuring MongoDB.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.7
* @deprecated since 2.2 - There is no replacement for this {@link org.springframework.beans.factory.FactoryBean} at
* this time. However moving forward the {@link org.springframework.beans.factory.FactoryBean} will be
* suitable to provide instances of {@link com.mongodb.client.MongoClient}.
*/
@Deprecated
public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> implements PersistenceExceptionTranslator {
private static final PersistenceExceptionTranslator DEFAULT_EXCEPTION_TRANSLATOR = new MongoExceptionTranslator();
private @Nullable MongoClientSettings mongoClientSettings;
private @Nullable MongoClientOptions mongoClientOptions;
private @Nullable String host;
private @Nullable Integer port;
private @Nullable List<MongoCredential> credential = null;
private @Nullable ConnectionString connectionString;
private @Nullable String replicaSet = null;
private List<ServerAddress> replicaSetSeeds = Collections.emptyList();
private List<MongoCredential> credentials = Collections.emptyList();
private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_EXCEPTION_TRANSLATOR;
/**
* Set the {@link MongoClientSettings} to be used when creating {@link MongoClient}.
* Set the {@link MongoClientOptions} to be used when creating {@link MongoClient}.
*
* @param mongoClientOptions
*/
public void setMongoClientSettings(@Nullable MongoClientSettings mongoClientOptions) {
this.mongoClientSettings = mongoClientOptions;
public void setMongoClientOptions(@Nullable MongoClientOptions mongoClientOptions) {
this.mongoClientOptions = mongoClientOptions;
}
/**
* Set the list of credentials to be used when creating {@link MongoClient}.
*
* @param credential can be {@literal null}.
* @param credentials can be {@literal null}.
*/
public void setCredential(@Nullable MongoCredential[] credential) {
this.credential = Arrays.asList(credential);
public void setCredentials(@Nullable MongoCredential[] credentials) {
this.credentials = filterNonNullElementsAsList(credentials);
}
/**
* Set the list of {@link ServerAddress} to build up a replica set for.
*
* @param replicaSetSeeds can be {@literal null}.
*/
public void setReplicaSetSeeds(@Nullable ServerAddress[] replicaSetSeeds) {
this.replicaSetSeeds = filterNonNullElementsAsList(replicaSetSeeds);
}
/**
@@ -102,14 +100,6 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> imp
this.port = port;
}
public void setConnectionString(@Nullable ConnectionString connectionString) {
this.connectionString = connectionString;
}
public void setReplicaSet(@Nullable String replicaSet) {
this.replicaSet = replicaSet;
}
/**
* Configures the {@link PersistenceExceptionTranslator} to use.
*
@@ -142,198 +132,12 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> imp
*/
@Override
protected MongoClient createInstance() throws Exception {
return createMongoClient(computeClientSetting());
}
/**
* Create {@link MongoClientSettings} based on configuration and priority (lower is better).
* <ol>
* <li>{@link MongoClientFactoryBean#mongoClientSettings}</li>
* <li>{@link MongoClientFactoryBean#connectionString}</li>
* <li>default {@link MongoClientSettings}</li>
* </ol>
*
* @since 3.0
*/
protected MongoClientSettings computeClientSetting() {
if (connectionString != null && (StringUtils.hasText(host) || port != null)) {
throw new IllegalStateException("ConnectionString and host/port configuration exclude one another!");
if (mongoClientOptions == null) {
mongoClientOptions = MongoClientOptions.builder().build();
}
ConnectionString connectionString = this.connectionString != null ? this.connectionString
: new ConnectionString(String.format("mongodb://%s:%s", getOrDefault(host, ServerAddress.defaultHost()),
getOrDefault(port, "" + ServerAddress.defaultPort())));
Builder builder = MongoClientSettings.builder().applyConnectionString(connectionString);
builder.uuidRepresentation(UuidRepresentation.JAVA_LEGACY);
if (mongoClientSettings != null) {
MongoClientSettings defaultSettings = MongoClientSettings.builder().build();
SslSettings sslSettings = mongoClientSettings.getSslSettings();
ClusterSettings clusterSettings = mongoClientSettings.getClusterSettings();
ConnectionPoolSettings connectionPoolSettings = mongoClientSettings.getConnectionPoolSettings();
SocketSettings socketSettings = mongoClientSettings.getSocketSettings();
ServerSettings serverSettings = mongoClientSettings.getServerSettings();
builder = builder //
.applicationName(computeSettingsValue(defaultSettings.getApplicationName(),
mongoClientSettings.getApplicationName(), connectionString.getApplicationName())) //
.applyToSslSettings(settings -> {
applySettings(settings::enabled, computeSettingsValue(SslSettings::isEnabled,
defaultSettings.getSslSettings(), sslSettings, connectionString.getSslEnabled()));
applySettings(settings::invalidHostNameAllowed, (computeSettingsValue(SslSettings::isInvalidHostNameAllowed,
defaultSettings.getSslSettings(), sslSettings, connectionString.getSslInvalidHostnameAllowed())));
settings.context(sslSettings.getContext());
}).applyToClusterSettings(settings -> {
applySettings(settings::hosts,
computeSettingsValue(ClusterSettings::getHosts, defaultSettings.getClusterSettings(), clusterSettings,
connectionString.getHosts().stream().map(ServerAddress::new).collect(Collectors.toList())));
applySettings(settings::requiredReplicaSetName,
computeSettingsValue(ClusterSettings::getRequiredReplicaSetName, defaultSettings.getClusterSettings(),
clusterSettings, connectionString.getRequiredReplicaSetName()));
applySettings(settings::srvHost, computeSettingsValue(ClusterSettings::getSrvHost,
defaultSettings.getClusterSettings(), clusterSettings, null));
applySettings(settings::mode, computeSettingsValue(ClusterSettings::getMode,
defaultSettings.getClusterSettings(), clusterSettings, null));
applySettings(it -> settings.localThreshold(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ClusterSettings it) -> it.getLocalThreshold(TimeUnit.MILLISECONDS),
defaultSettings.getClusterSettings(), clusterSettings, connectionString.getLocalThreshold()));
applySettings(settings::requiredClusterType, computeSettingsValue(ClusterSettings::getRequiredClusterType,
defaultSettings.getClusterSettings(), clusterSettings, null));
applySettings(it -> settings.serverSelectionTimeout(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ClusterSettings it) -> it.getServerSelectionTimeout(TimeUnit.MILLISECONDS),
defaultSettings.getClusterSettings(), clusterSettings,
connectionString.getServerSelectionTimeout()));
applySettings(settings::serverSelector, computeSettingsValue(ClusterSettings::getServerSelector,
defaultSettings.getClusterSettings(), clusterSettings, null));
List<ClusterListener> clusterListeners = computeSettingsValue(ClusterSettings::getClusterListeners,
defaultSettings.getClusterSettings(), clusterSettings, null);
if (clusterListeners != null) {
clusterListeners.forEach(settings::addClusterListener);
}
}) //
.applyToConnectionPoolSettings(settings -> {
applySettings(it -> settings.maintenanceFrequency(it, TimeUnit.MILLISECONDS),
computeSettingsValue((ConnectionPoolSettings it) -> it.getMaintenanceFrequency(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings, null));
applySettings(it -> settings.maxConnectionIdleTime(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ConnectionPoolSettings it) -> it.getMaxConnectionIdleTime(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings,
connectionString.getMaxConnectionIdleTime()));
applySettings(it -> settings.maxConnectionLifeTime(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ConnectionPoolSettings it) -> it.getMaxConnectionLifeTime(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings,
connectionString.getMaxConnectionLifeTime()));
applySettings(it -> settings.maxWaitTime(it.longValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ConnectionPoolSettings it) -> it.getMaxWaitTime(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings,
connectionString.getMaxWaitTime()));
applySettings(it -> settings.maintenanceInitialDelay(it, TimeUnit.MILLISECONDS),
computeSettingsValue(
(ConnectionPoolSettings it) -> it.getMaintenanceInitialDelay(TimeUnit.MILLISECONDS),
defaultSettings.getConnectionPoolSettings(), connectionPoolSettings, null));
applySettings(settings::minSize,
computeSettingsValue(ConnectionPoolSettings::getMinSize, defaultSettings.getConnectionPoolSettings(),
connectionPoolSettings, connectionString.getMinConnectionPoolSize()));
applySettings(settings::maxSize,
computeSettingsValue(ConnectionPoolSettings::getMaxSize, defaultSettings.getConnectionPoolSettings(),
connectionPoolSettings, connectionString.getMaxConnectionPoolSize()));
}) //
.applyToSocketSettings(settings -> {
applySettings(it -> settings.connectTimeout(it, TimeUnit.MILLISECONDS),
computeSettingsValue((SocketSettings it) -> it.getConnectTimeout(TimeUnit.MILLISECONDS),
defaultSettings.getSocketSettings(), socketSettings, connectionString.getConnectTimeout()));
applySettings(it -> settings.readTimeout(it, TimeUnit.MILLISECONDS),
computeSettingsValue((SocketSettings it) -> it.getReadTimeout(TimeUnit.MILLISECONDS),
defaultSettings.getSocketSettings(), socketSettings, connectionString.getSocketTimeout()));
applySettings(settings::receiveBufferSize, computeSettingsValue(SocketSettings::getReceiveBufferSize,
defaultSettings.getSocketSettings(), socketSettings, null));
applySettings(settings::sendBufferSize, computeSettingsValue(SocketSettings::getSendBufferSize,
defaultSettings.getSocketSettings(), socketSettings, null));
}) //
.applyToServerSettings(settings -> {
applySettings(it -> settings.minHeartbeatFrequency(it.intValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ServerSettings it) -> it.getMinHeartbeatFrequency(TimeUnit.MILLISECONDS),
defaultSettings.getServerSettings(), serverSettings, null));
applySettings(it -> settings.heartbeatFrequency(it.intValue(), TimeUnit.MILLISECONDS),
computeSettingsValue((ServerSettings it) -> it.getHeartbeatFrequency(TimeUnit.MILLISECONDS),
defaultSettings.getServerSettings(), serverSettings, connectionString.getHeartbeatFrequency()));
settings.applySettings(serverSettings);
}) //
.autoEncryptionSettings(mongoClientSettings.getAutoEncryptionSettings()) //
.codecRegistry(mongoClientSettings.getCodecRegistry()); //
applySettings(builder::readConcern, computeSettingsValue(defaultSettings.getReadConcern(),
mongoClientSettings.getReadConcern(), connectionString.getReadConcern()));
applySettings(builder::writeConcern, computeSettingsValue(defaultSettings.getWriteConcern(),
mongoClientSettings.getWriteConcern(), connectionString.getWriteConcern()));
applySettings(builder::readPreference, computeSettingsValue(defaultSettings.getReadPreference(),
mongoClientSettings.getReadPreference(), connectionString.getReadPreference()));
applySettings(builder::retryReads, computeSettingsValue(defaultSettings.getRetryReads(),
mongoClientSettings.getRetryReads(), connectionString.getRetryReads()));
applySettings(builder::retryWrites, computeSettingsValue(defaultSettings.getRetryWrites(),
mongoClientSettings.getRetryWrites(), connectionString.getRetryWritesValue()));
applySettings(builder::uuidRepresentation,
computeSettingsValue(null, mongoClientSettings.getUuidRepresentation(), UuidRepresentation.JAVA_LEGACY));
}
if (!CollectionUtils.isEmpty(credential)) {
builder = builder.credential(credential.iterator().next());
}
if (StringUtils.hasText(replicaSet)) {
builder.applyToClusterSettings((settings) -> {
settings.requiredReplicaSetName(replicaSet);
});
}
return builder.build();
}
private <T> void applySettings(Consumer<T> settingsBuilder, @Nullable T value) {
if (ObjectUtils.isEmpty(value)) {
return;
}
settingsBuilder.accept(value);
}
private <S, T> T computeSettingsValue(Function<S, T> function, S defaultValueHolder, S settingsValueHolder,
@Nullable T connectionStringValue) {
return computeSettingsValue(function.apply(defaultValueHolder), function.apply(settingsValueHolder),
connectionStringValue);
}
private <T> T computeSettingsValue(T defaultValue, T fromSettings, T fromConnectionString) {
boolean fromSettingsIsDefault = ObjectUtils.nullSafeEquals(defaultValue, fromSettings);
boolean fromConnectionStringIsDefault = ObjectUtils.nullSafeEquals(defaultValue, fromConnectionString);
if (!fromSettingsIsDefault) {
return fromSettings;
}
return !fromConnectionStringIsDefault ? fromConnectionString : defaultValue;
return createMongoClient();
}
/*
@@ -348,11 +152,43 @@ public class MongoClientFactoryBean extends AbstractFactoryBean<MongoClient> imp
}
}
private MongoClient createMongoClient(MongoClientSettings settings) throws UnknownHostException {
return MongoClients.create(settings, SpringDataMongoDB.driverInformation());
private MongoClient createMongoClient() throws UnknownHostException {
if (!CollectionUtils.isEmpty(replicaSetSeeds)) {
return new MongoClient(replicaSetSeeds, credentials, mongoClientOptions);
}
return new MongoClient(createConfiguredOrDefaultServerAddress(), credentials, mongoClientOptions);
}
private String getOrDefault(Object value, String defaultValue) {
return !StringUtils.isEmpty(value) ? value.toString() : defaultValue;
private ServerAddress createConfiguredOrDefaultServerAddress() throws UnknownHostException {
ServerAddress defaultAddress = new ServerAddress();
return new ServerAddress(StringUtils.hasText(host) ? host : defaultAddress.getHost(),
port != null ? port.intValue() : defaultAddress.getPort());
}
/**
* Returns the given array as {@link List} with all {@literal null} elements removed.
*
* @param elements the elements to filter <T>, can be {@literal null}.
* @return a new unmodifiable {@link List#} from the given elements without {@literal null}s.
*/
private static <T> List<T> filterNonNullElementsAsList(@Nullable T[] elements) {
if (elements == null) {
return Collections.emptyList();
}
List<T> candidateElements = new ArrayList<T>();
for (T element : elements) {
if (element != null) {
candidateElements.add(element);
}
}
return Collections.unmodifiableList(candidateElements);
}
}

View File

@@ -0,0 +1,338 @@
/*
* Copyright 2015-2020 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
*
* https://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 javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.lang.Nullable;
import com.mongodb.AutoEncryptionSettings;
import com.mongodb.DBDecoderFactory;
import com.mongodb.DBEncoderFactory;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
/**
* A factory bean for construction of a {@link MongoClientOptions} instance.
*
* @author Christoph Strobl
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.7
* @deprecated since 2.2 - There is no replacement for this {@link org.springframework.beans.factory.FactoryBean}.
* However moving forward there will be a dedicated factory bean for {@link com.mongodb.MongoClientSettings}
* replacing {@link MongoClientOptions}.
*/
@Deprecated
public class MongoClientOptionsFactoryBean extends AbstractFactoryBean<MongoClientOptions> {
private static final MongoClientOptions DEFAULT_MONGO_OPTIONS = MongoClientOptions.builder().build();
// TODO: Mongo Driver 4 - use application name insetad of description if not available
private @Nullable String description = DEFAULT_MONGO_OPTIONS.getApplicationName();
private int minConnectionsPerHost = DEFAULT_MONGO_OPTIONS.getMinConnectionsPerHost();
private int connectionsPerHost = DEFAULT_MONGO_OPTIONS.getConnectionsPerHost();
private int threadsAllowedToBlockForConnectionMultiplier = DEFAULT_MONGO_OPTIONS
.getThreadsAllowedToBlockForConnectionMultiplier();
private int maxWaitTime = DEFAULT_MONGO_OPTIONS.getMaxWaitTime();
private int maxConnectionIdleTime = DEFAULT_MONGO_OPTIONS.getMaxConnectionIdleTime();
private int maxConnectionLifeTime = DEFAULT_MONGO_OPTIONS.getMaxConnectionLifeTime();
private int connectTimeout = DEFAULT_MONGO_OPTIONS.getConnectTimeout();
private int socketTimeout = DEFAULT_MONGO_OPTIONS.getSocketTimeout();
// TODO: Mongo Driver 4 - check if available
private boolean socketKeepAlive = DEFAULT_MONGO_OPTIONS.isSocketKeepAlive();
private @Nullable ReadPreference readPreference = DEFAULT_MONGO_OPTIONS.getReadPreference();
private DBDecoderFactory dbDecoderFactory = DEFAULT_MONGO_OPTIONS.getDbDecoderFactory();
private DBEncoderFactory dbEncoderFactory = DEFAULT_MONGO_OPTIONS.getDbEncoderFactory();
private @Nullable WriteConcern writeConcern = DEFAULT_MONGO_OPTIONS.getWriteConcern();
private @Nullable SocketFactory socketFactory = DEFAULT_MONGO_OPTIONS.getSocketFactory();
private boolean cursorFinalizerEnabled = DEFAULT_MONGO_OPTIONS.isCursorFinalizerEnabled();
// TODO: Mongo Driver 4 - remove this option
private boolean alwaysUseMBeans = DEFAULT_MONGO_OPTIONS.isAlwaysUseMBeans();
private int heartbeatFrequency = DEFAULT_MONGO_OPTIONS.getHeartbeatFrequency();
private int minHeartbeatFrequency = DEFAULT_MONGO_OPTIONS.getMinHeartbeatFrequency();
private int heartbeatConnectTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatConnectTimeout();
private int heartbeatSocketTimeout = DEFAULT_MONGO_OPTIONS.getHeartbeatSocketTimeout();
private String requiredReplicaSetName = DEFAULT_MONGO_OPTIONS.getRequiredReplicaSetName();
private int serverSelectionTimeout = DEFAULT_MONGO_OPTIONS.getServerSelectionTimeout();
private boolean ssl;
private @Nullable SSLSocketFactory sslSocketFactory;
private @Nullable AutoEncryptionSettings autoEncryptionSettings;
/**
* Set the {@link MongoClient} description.
*
* @param description
*/
// TODO: Mongo Driver 4 - deprecate that one and add application name
public void setDescription(@Nullable String description) {
this.description = description;
}
/**
* Set the minimum number of connections per host.
*
* @param minConnectionsPerHost
*/
public void setMinConnectionsPerHost(int minConnectionsPerHost) {
this.minConnectionsPerHost = minConnectionsPerHost;
}
/**
* Set the number of connections allowed per host. Will block if run out. Default is 10. System property
* {@code MONGO.POOLSIZE} can override
*
* @param connectionsPerHost
*/
public void setConnectionsPerHost(int connectionsPerHost) {
this.connectionsPerHost = connectionsPerHost;
}
/**
* Set 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.
*
* @param threadsAllowedToBlockForConnectionMultiplier
*/
public void setThreadsAllowedToBlockForConnectionMultiplier(int threadsAllowedToBlockForConnectionMultiplier) {
this.threadsAllowedToBlockForConnectionMultiplier = threadsAllowedToBlockForConnectionMultiplier;
}
/**
* Set the max wait time of a blocking thread for a connection. Default is 12000 ms (2 minutes)
*
* @param maxWaitTime
*/
public void setMaxWaitTime(int maxWaitTime) {
this.maxWaitTime = maxWaitTime;
}
/**
* The maximum idle time for a pooled connection.
*
* @param maxConnectionIdleTime
*/
public void setMaxConnectionIdleTime(int maxConnectionIdleTime) {
this.maxConnectionIdleTime = maxConnectionIdleTime;
}
/**
* Set the maximum life time for a pooled connection.
*
* @param maxConnectionLifeTime
*/
public void setMaxConnectionLifeTime(int maxConnectionLifeTime) {
this.maxConnectionLifeTime = maxConnectionLifeTime;
}
/**
* Set the connect timeout in milliseconds. 0 is default and infinite.
*
* @param connectTimeout
*/
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
/**
* Set the socket timeout. 0 is default and infinite.
*
* @param socketTimeout
*/
public void setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
}
/**
* Set the keep alive flag, controls whether or not to have socket keep alive timeout. Defaults to false.
*
* @param socketKeepAlive
*/
public void setSocketKeepAlive(boolean socketKeepAlive) {
this.socketKeepAlive = socketKeepAlive;
}
/**
* Set the {@link ReadPreference}.
*
* @param readPreference
*/
public void setReadPreference(@Nullable ReadPreference readPreference) {
this.readPreference = readPreference;
}
/**
* Set the {@link WriteConcern} that will be the default value used when asking the {@link MongoDbFactory} for a DB
* object.
*
* @param writeConcern
*/
public void setWriteConcern(@Nullable WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/**
* @param socketFactory
*/
public void setSocketFactory(@Nullable SocketFactory socketFactory) {
this.socketFactory = socketFactory;
}
/**
* Set the frequency that the driver will attempt to determine the current state of each server in the cluster.
*
* @param heartbeatFrequency
*/
public void setHeartbeatFrequency(int heartbeatFrequency) {
this.heartbeatFrequency = heartbeatFrequency;
}
/**
* In the event that the driver has to frequently re-check a server's availability, it will wait at least this long
* since the previous check to avoid wasted effort.
*
* @param minHeartbeatFrequency
*/
public void setMinHeartbeatFrequency(int minHeartbeatFrequency) {
this.minHeartbeatFrequency = minHeartbeatFrequency;
}
/**
* Set the connect timeout for connections used for the cluster heartbeat.
*
* @param heartbeatConnectTimeout
*/
public void setHeartbeatConnectTimeout(int heartbeatConnectTimeout) {
this.heartbeatConnectTimeout = heartbeatConnectTimeout;
}
/**
* Set the socket timeout for connections used for the cluster heartbeat.
*
* @param heartbeatSocketTimeout
*/
public void setHeartbeatSocketTimeout(int heartbeatSocketTimeout) {
this.heartbeatSocketTimeout = heartbeatSocketTimeout;
}
/**
* Configures the name of the replica set.
*
* @param requiredReplicaSetName
*/
public void setRequiredReplicaSetName(String requiredReplicaSetName) {
this.requiredReplicaSetName = requiredReplicaSetName;
}
/**
* This controls if the driver should us an SSL connection. Defaults to {@literal false}.
*
* @param ssl
*/
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
/**
* Set the {@link SSLSocketFactory} to use for the {@literal SSL} connection. If none is configured here,
* {@link SSLSocketFactory#getDefault()} will be used.
*
* @param sslSocketFactory
*/
public void setSslSocketFactory(@Nullable SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
this.ssl = sslSocketFactory != null;
}
/**
* Set the {@literal server selection timeout} in msec for a 3.x MongoDB Java driver. If not set the default value of
* 30 sec will be used. A value of 0 means that it will timeout immediately if no server is available. A negative
* value means to wait indefinitely.
*
* @param serverSelectionTimeout in msec.
*/
public void setServerSelectionTimeout(int serverSelectionTimeout) {
this.serverSelectionTimeout = serverSelectionTimeout;
}
/**
* Set the {@link AutoEncryptionSettings} to be used.
*
* @param autoEncryptionSettings can be {@literal null}.
* @since 2.2
*/
public void setAutoEncryptionSettings(@Nullable AutoEncryptionSettings autoEncryptionSettings) {
this.autoEncryptionSettings = autoEncryptionSettings;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
*/
@SuppressWarnings("ConstantConditions")
@Override
protected MongoClientOptions createInstance() throws Exception {
SocketFactory socketFactoryToUse = ssl
? (sslSocketFactory != null ? sslSocketFactory : SSLSocketFactory.getDefault())
: this.socketFactory;
return MongoClientOptions.builder() //
.alwaysUseMBeans(this.alwaysUseMBeans) //
.connectionsPerHost(this.connectionsPerHost) //
.connectTimeout(connectTimeout) //
.cursorFinalizerEnabled(cursorFinalizerEnabled) //
.dbDecoderFactory(dbDecoderFactory) //
.dbEncoderFactory(dbEncoderFactory) //
.applicationName(description) // TODO: Mongo Driver 4 - use application name if description not available
.heartbeatConnectTimeout(heartbeatConnectTimeout) //
.heartbeatFrequency(heartbeatFrequency) //
.heartbeatSocketTimeout(heartbeatSocketTimeout) //
.maxConnectionIdleTime(maxConnectionIdleTime) //
.maxConnectionLifeTime(maxConnectionLifeTime) //
.maxWaitTime(maxWaitTime) //
.minConnectionsPerHost(minConnectionsPerHost) //
.minHeartbeatFrequency(minHeartbeatFrequency) //
.readPreference(readPreference) //
.requiredReplicaSetName(requiredReplicaSetName) //
.serverSelectionTimeout(serverSelectionTimeout) //
.sslEnabled(ssl) //
.autoEncryptionSettings(autoEncryptionSettings) //
.socketFactory(socketFactoryToUse) // TODO: Mongo Driver 4 -
.socketKeepAlive(socketKeepAlive) // TODO: Mongo Driver 4 - remove if not available
.socketTimeout(socketTimeout) //
.threadsAllowedToBlockForConnectionMultiplier(threadsAllowedToBlockForConnectionMultiplier) //
.writeConcern(writeConcern).build();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<?> getObjectType() {
return MongoClientOptions.class;
}
}

View File

@@ -1,486 +0,0 @@
/*
* Copyright 2019 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import org.bson.UuidRepresentation;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.mongodb.AutoEncryptionSettings;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoClientSettings.Builder;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ClusterType;
import com.mongodb.connection.StreamFactoryFactory;
/**
* A factory bean for construction of a {@link MongoClientSettings} instance to be used with a MongoDB driver.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0
*/
public class MongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoClientSettings> {
private static final MongoClientSettings DEFAULT_MONGO_SETTINGS = MongoClientSettings.builder().build();
private CodecRegistry codecRegistry = DEFAULT_MONGO_SETTINGS.getCodecRegistry();
private StreamFactoryFactory streamFactoryFactory = DEFAULT_MONGO_SETTINGS.getStreamFactoryFactory();
private ReadPreference readPreference = DEFAULT_MONGO_SETTINGS.getReadPreference();
private ReadConcern readConcern = DEFAULT_MONGO_SETTINGS.getReadConcern();
private @Nullable Boolean retryReads = null;
private WriteConcern writeConcern = DEFAULT_MONGO_SETTINGS.getWriteConcern();
private @Nullable Boolean retryWrites = null;
private @Nullable String applicationName = null;
private @Nullable UuidRepresentation uUidRepresentation = null;
// --> Socket Settings
private int socketConnectTimeoutMS = DEFAULT_MONGO_SETTINGS.getSocketSettings()
.getConnectTimeout(TimeUnit.MILLISECONDS);
private int socketReadTimeoutMS = DEFAULT_MONGO_SETTINGS.getSocketSettings().getReadTimeout(TimeUnit.MILLISECONDS);
private int socketReceiveBufferSize = DEFAULT_MONGO_SETTINGS.getSocketSettings().getReceiveBufferSize();
private int socketSendBufferSize = DEFAULT_MONGO_SETTINGS.getSocketSettings().getSendBufferSize();
// --> Cluster Settings
private @Nullable String clusterSrvHost = DEFAULT_MONGO_SETTINGS.getClusterSettings().getSrvHost();
private List<ServerAddress> clusterHosts = Collections.emptyList();
private @Nullable ClusterConnectionMode clusterConnectionMode = null;
private ClusterType custerRequiredClusterType = DEFAULT_MONGO_SETTINGS.getClusterSettings().getRequiredClusterType();
private String clusterRequiredReplicaSetName = DEFAULT_MONGO_SETTINGS.getClusterSettings()
.getRequiredReplicaSetName();
private long clusterLocalThresholdMS = DEFAULT_MONGO_SETTINGS.getClusterSettings()
.getLocalThreshold(TimeUnit.MILLISECONDS);
private long clusterServerSelectionTimeoutMS = DEFAULT_MONGO_SETTINGS.getClusterSettings()
.getServerSelectionTimeout(TimeUnit.MILLISECONDS);
// --> ConnectionPoolSettings
private int poolMaxSize = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings().getMaxSize();
private int poolMinSize = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings().getMinSize();
private long poolMaxWaitTimeMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaxWaitTime(TimeUnit.MILLISECONDS);
private long poolMaxConnectionLifeTimeMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaxConnectionLifeTime(TimeUnit.MILLISECONDS);
private long poolMaxConnectionIdleTimeMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaxConnectionIdleTime(TimeUnit.MILLISECONDS);
private long poolMaintenanceInitialDelayMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaintenanceInitialDelay(TimeUnit.MILLISECONDS);
private long poolMaintenanceFrequencyMS = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings()
.getMaintenanceFrequency(TimeUnit.MILLISECONDS);
// --> SSL Settings
private boolean sslEnabled = DEFAULT_MONGO_SETTINGS.getSslSettings().isEnabled();
private boolean sslInvalidHostNameAllowed = DEFAULT_MONGO_SETTINGS.getSslSettings().isInvalidHostNameAllowed();
private String sslProvider = DEFAULT_MONGO_SETTINGS.getSslSettings().isEnabled()
? DEFAULT_MONGO_SETTINGS.getSslSettings().getContext().getProvider().getName()
: "";
// encryption and retry
private @Nullable AutoEncryptionSettings autoEncryptionSettings;
/**
* @param socketConnectTimeoutMS in msec
* @see com.mongodb.connection.SocketSettings.Builder#connectTimeout(int, TimeUnit)
*/
public void setSocketConnectTimeoutMS(int socketConnectTimeoutMS) {
this.socketConnectTimeoutMS = socketConnectTimeoutMS;
}
/**
* @param socketReadTimeoutMS in msec
* @see com.mongodb.connection.SocketSettings.Builder#readTimeout(int, TimeUnit)
*/
public void setSocketReadTimeoutMS(int socketReadTimeoutMS) {
this.socketReadTimeoutMS = socketReadTimeoutMS;
}
/**
* @param socketReceiveBufferSize
* @see com.mongodb.connection.SocketSettings.Builder#receiveBufferSize(int)
*/
public void setSocketReceiveBufferSize(int socketReceiveBufferSize) {
this.socketReceiveBufferSize = socketReceiveBufferSize;
}
/**
* @param socketSendBufferSize
* @see com.mongodb.connection.SocketSettings.Builder#sendBufferSize(int)
*/
public void setSocketSendBufferSize(int socketSendBufferSize) {
this.socketSendBufferSize = socketSendBufferSize;
}
// --> Server Settings
private long serverHeartbeatFrequencyMS = DEFAULT_MONGO_SETTINGS.getServerSettings()
.getHeartbeatFrequency(TimeUnit.MILLISECONDS);
private long serverMinHeartbeatFrequencyMS = DEFAULT_MONGO_SETTINGS.getServerSettings()
.getMinHeartbeatFrequency(TimeUnit.MILLISECONDS);
/**
* @param serverHeartbeatFrequencyMS in msec
* @see com.mongodb.connection.ServerSettings.Builder#heartbeatFrequency(long, TimeUnit)
*/
public void setServerHeartbeatFrequencyMS(long serverHeartbeatFrequencyMS) {
this.serverHeartbeatFrequencyMS = serverHeartbeatFrequencyMS;
}
/**
* @param serverMinHeartbeatFrequencyMS in msec
* @see com.mongodb.connection.ServerSettings.Builder#minHeartbeatFrequency(long, TimeUnit)
*/
public void setServerMinHeartbeatFrequencyMS(long serverMinHeartbeatFrequencyMS) {
this.serverMinHeartbeatFrequencyMS = serverMinHeartbeatFrequencyMS;
}
// --> Cluster Settings
/**
* @param clusterSrvHost
* @see com.mongodb.connection.ClusterSettings.Builder#srvHost(String)
*/
public void setClusterSrvHost(String clusterSrvHost) {
this.clusterSrvHost = clusterSrvHost;
}
/**
* @param clusterHosts
* @see com.mongodb.connection.ClusterSettings.Builder#hosts(List)
*/
public void setClusterHosts(ServerAddress[] clusterHosts) {
this.clusterHosts = Arrays.asList(clusterHosts);
}
/**
* ????
*
* @param clusterConnectionMode
* @see com.mongodb.connection.ClusterSettings.Builder#mode(ClusterConnectionMode)
*/
public void setClusterConnectionMode(ClusterConnectionMode clusterConnectionMode) {
this.clusterConnectionMode = clusterConnectionMode;
}
/**
* @param custerRequiredClusterType
* @see com.mongodb.connection.ClusterSettings.Builder#requiredClusterType(ClusterType)
*/
public void setCusterRequiredClusterType(ClusterType custerRequiredClusterType) {
this.custerRequiredClusterType = custerRequiredClusterType;
}
/**
* @param clusterRequiredReplicaSetName
* @see com.mongodb.connection.ClusterSettings.Builder#requiredReplicaSetName(String)
*/
public void setClusterRequiredReplicaSetName(String clusterRequiredReplicaSetName) {
this.clusterRequiredReplicaSetName = clusterRequiredReplicaSetName;
}
/**
* @param clusterLocalThresholdMS in msec
* @see com.mongodb.connection.ClusterSettings.Builder#localThreshold(long, TimeUnit)
*/
public void setClusterLocalThresholdMS(long clusterLocalThresholdMS) {
this.clusterLocalThresholdMS = clusterLocalThresholdMS;
}
/**
* @param clusterServerSelectionTimeoutMS in msec
* @see com.mongodb.connection.ClusterSettings.Builder#serverSelectionTimeout(long, TimeUnit)
*/
public void setClusterServerSelectionTimeoutMS(long clusterServerSelectionTimeoutMS) {
this.clusterServerSelectionTimeoutMS = clusterServerSelectionTimeoutMS;
}
// --> ConnectionPoolSettings
/**
* @param poolMaxSize
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maxSize(int)
*/
public void setPoolMaxSize(int poolMaxSize) {
this.poolMaxSize = poolMaxSize;
}
/**
* @param poolMinSize
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#minSize(int)
*/
public void setPoolMinSize(int poolMinSize) {
this.poolMinSize = poolMinSize;
}
/**
* @param poolMaxWaitTimeMS in mesec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maxWaitTime(long, TimeUnit)
*/
public void setPoolMaxWaitTimeMS(long poolMaxWaitTimeMS) {
this.poolMaxWaitTimeMS = poolMaxWaitTimeMS;
}
/**
* @param poolMaxConnectionLifeTimeMS in msec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maxConnectionLifeTime(long, TimeUnit)
*/
public void setPoolMaxConnectionLifeTimeMS(long poolMaxConnectionLifeTimeMS) {
this.poolMaxConnectionLifeTimeMS = poolMaxConnectionLifeTimeMS;
}
/**
* @param poolMaxConnectionIdleTimeMS in msec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maxConnectionIdleTime(long, TimeUnit)
*/
public void setPoolMaxConnectionIdleTimeMS(long poolMaxConnectionIdleTimeMS) {
this.poolMaxConnectionIdleTimeMS = poolMaxConnectionIdleTimeMS;
}
/**
* @param poolMaintenanceInitialDelayMS in msec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maintenanceInitialDelay(long, TimeUnit)
*/
public void setPoolMaintenanceInitialDelayMS(long poolMaintenanceInitialDelayMS) {
this.poolMaintenanceInitialDelayMS = poolMaintenanceInitialDelayMS;
}
/**
* @param poolMaintenanceFrequencyMS in msec
* @see com.mongodb.connection.ConnectionPoolSettings.Builder#maintenanceFrequency(long, TimeUnit)
*/
public void setPoolMaintenanceFrequencyMS(long poolMaintenanceFrequencyMS) {
this.poolMaintenanceFrequencyMS = poolMaintenanceFrequencyMS;
}
// --> SSL Settings
/**
* @param sslEnabled
* @see com.mongodb.connection.SslSettings.Builder#enabled(boolean)
*/
public void setSslEnabled(Boolean sslEnabled) {
this.sslEnabled = sslEnabled;
}
/**
* @param sslInvalidHostNameAllowed
* @see com.mongodb.connection.SslSettings.Builder#invalidHostNameAllowed(boolean)
*/
public void setSslInvalidHostNameAllowed(Boolean sslInvalidHostNameAllowed) {
this.sslInvalidHostNameAllowed = sslInvalidHostNameAllowed;
}
/**
* @param sslProvider
* @see com.mongodb.connection.SslSettings.Builder#context(SSLContext)
* @see SSLContext#getInstance(String)
*/
public void setSslProvider(String sslProvider) {
this.sslProvider = sslProvider;
}
// encryption and retry
/**
* @param applicationName
* @see MongoClientSettings.Builder#applicationName(String)
*/
public void setApplicationName(@Nullable String applicationName) {
this.applicationName = applicationName;
}
/**
* @param retryReads
* @see MongoClientSettings.Builder#retryReads(boolean)
*/
public void setRetryReads(@Nullable Boolean retryReads) {
this.retryReads = retryReads;
}
/**
* @param readConcern
* @see MongoClientSettings.Builder#readConcern(ReadConcern)
*/
public void setReadConcern(ReadConcern readConcern) {
this.readConcern = readConcern;
}
/**
* @param writeConcern
* @see MongoClientSettings.Builder#writeConcern(WriteConcern)
*/
public void setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/**
* @param retryWrites
* @see MongoClientSettings.Builder#retryWrites(boolean)
*/
public void setRetryWrites(@Nullable Boolean retryWrites) {
this.retryWrites = retryWrites;
}
/**
* @param readPreference
* @see MongoClientSettings.Builder#readPreference(ReadPreference)
*/
public void setReadPreference(ReadPreference readPreference) {
this.readPreference = readPreference;
}
/**
* @param streamFactoryFactory
* @see MongoClientSettings.Builder#streamFactoryFactory(StreamFactoryFactory)
*/
public void setStreamFactoryFactory(StreamFactoryFactory streamFactoryFactory) {
this.streamFactoryFactory = streamFactoryFactory;
}
/**
* @param codecRegistry
* @see MongoClientSettings.Builder#codecRegistry(CodecRegistry)
*/
public void setCodecRegistry(CodecRegistry codecRegistry) {
this.codecRegistry = codecRegistry;
}
/**
* @param uUidRepresentation
*/
public void setuUidRepresentation(@Nullable UuidRepresentation uUidRepresentation) {
this.uUidRepresentation = uUidRepresentation;
}
/**
* @param autoEncryptionSettings can be {@literal null}.
* @see MongoClientSettings.Builder#autoEncryptionSettings(AutoEncryptionSettings)
*/
public void setAutoEncryptionSettings(@Nullable AutoEncryptionSettings autoEncryptionSettings) {
this.autoEncryptionSettings = autoEncryptionSettings;
}
@Override
public Class<?> getObjectType() {
return MongoClientSettings.class;
}
@Override
protected MongoClientSettings createInstance() {
Builder builder = MongoClientSettings.builder() //
.readPreference(readPreference) //
.writeConcern(writeConcern) //
.readConcern(readConcern) //
.codecRegistry(codecRegistry) //
.applicationName(applicationName) //
.autoEncryptionSettings(autoEncryptionSettings) //
.applyToClusterSettings((settings) -> {
settings.serverSelectionTimeout(clusterServerSelectionTimeoutMS, TimeUnit.MILLISECONDS);
if (clusterConnectionMode != null) {
settings.mode(clusterConnectionMode);
}
settings.requiredReplicaSetName(clusterRequiredReplicaSetName);
if (!CollectionUtils.isEmpty(clusterHosts)) {
settings.hosts(clusterHosts);
}
settings.localThreshold(clusterLocalThresholdMS, TimeUnit.MILLISECONDS);
// settings.maxWaitQueueSize(clusterMaxWaitQueueSize);
settings.requiredClusterType(custerRequiredClusterType);
if (StringUtils.hasText(clusterSrvHost)) {
settings.srvHost(clusterSrvHost);
}
}) //
.applyToConnectionPoolSettings((settings) -> {
settings.minSize(poolMinSize);
settings.maxSize(poolMaxSize);
settings.maxConnectionIdleTime(poolMaxConnectionIdleTimeMS, TimeUnit.MILLISECONDS);
settings.maxWaitTime(poolMaxWaitTimeMS, TimeUnit.MILLISECONDS);
settings.maxConnectionLifeTime(poolMaxConnectionLifeTimeMS, TimeUnit.MILLISECONDS);
// settings.maxWaitQueueSize(poolMaxWaitQueueSize);
settings.maintenanceFrequency(poolMaintenanceFrequencyMS, TimeUnit.MILLISECONDS);
settings.maintenanceInitialDelay(poolMaintenanceInitialDelayMS, TimeUnit.MILLISECONDS);
}) //
.applyToServerSettings((settings) -> {
settings.minHeartbeatFrequency(serverMinHeartbeatFrequencyMS, TimeUnit.MILLISECONDS);
settings.heartbeatFrequency(serverHeartbeatFrequencyMS, TimeUnit.MILLISECONDS);
}) //
.applyToSocketSettings((settings) -> {
settings.connectTimeout(socketConnectTimeoutMS, TimeUnit.MILLISECONDS);
settings.readTimeout(socketReadTimeoutMS, TimeUnit.MILLISECONDS);
settings.receiveBufferSize(socketReceiveBufferSize);
settings.sendBufferSize(socketSendBufferSize);
}) //
.applyToSslSettings((settings) -> {
settings.enabled(sslEnabled);
if (sslEnabled) {
settings.invalidHostNameAllowed(sslInvalidHostNameAllowed);
try {
settings.context(
StringUtils.hasText(sslProvider) ? SSLContext.getInstance(sslProvider) : SSLContext.getDefault());
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
});
if (streamFactoryFactory != null) {
builder = builder.streamFactoryFactory(streamFactoryFactory);
}
if (retryReads != null) {
builder = builder.retryReads(retryReads);
}
if (retryWrites != null) {
builder = builder.retryWrites(retryWrites);
}
if (uUidRepresentation != null) {
builder.uuidRepresentation(uUidRepresentation);
}
return builder.build();
}
}

View File

@@ -16,9 +16,10 @@
package org.springframework.data.mongodb.core;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.WriteConcernResult;
import com.mongodb.WriteResult;
/**
* Mongo-specific {@link DataIntegrityViolationException}.
@@ -29,18 +30,18 @@ public class MongoDataIntegrityViolationException extends DataIntegrityViolation
private static final long serialVersionUID = -186980521176764046L;
private final WriteConcernResult writeResult;
private final WriteResult writeResult;
private final MongoActionOperation actionOperation;
/**
* Creates a new {@link MongoDataIntegrityViolationException} using the given message and {@link WriteConcernResult}.
* Creates a new {@link MongoDataIntegrityViolationException} using the given message and {@link WriteResult}.
*
* @param message the exception message
* @param writeResult the {@link WriteConcernResult} that causes the exception, must not be {@literal null}.
* @param writeResult the {@link WriteResult} that causes the exception, must not be {@literal null}.
* @param actionOperation the {@link MongoActionOperation} that caused the exception, must not be {@literal null}.
*/
public MongoDataIntegrityViolationException(String message, WriteConcernResult writeResult,
MongoActionOperation actionOperation) {
public MongoDataIntegrityViolationException(String message, WriteResult writeResult,
MongoActionOperation actionOperation) {
super(message);
@@ -52,11 +53,11 @@ public class MongoDataIntegrityViolationException extends DataIntegrityViolation
}
/**
* Returns the {@link WriteConcernResult} that caused the exception.
* Returns the {@link WriteResult} that caused the exception.
*
* @return the writeResult
*/
public WriteConcernResult getWriteResult() {
public WriteResult getWriteResult() {
return writeResult;
}

View File

@@ -1,298 +0,0 @@
/*
* Copyright 2018-2019 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
*
* https://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 org.springframework.aop.framework.ProxyFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.WriteConcern;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* Common base class for usage with both {@link com.mongodb.client.MongoClients} defining common properties such as
* database name and exception translator.
* <p/>
* Not intended to be used directly.
*
* @author Christoph Strobl
* @author Mark Paluch
* @param <C> Client type.
* @since 3.0
* @see SimpleMongoClientDatabaseFactory
*/
public abstract class MongoDatabaseFactorySupport<C> implements MongoDatabaseFactory {
private final C mongoClient;
private final String databaseName;
private final boolean mongoInstanceCreated;
private final PersistenceExceptionTranslator exceptionTranslator;
private @Nullable WriteConcern writeConcern;
/**
* Create a new {@link MongoDatabaseFactorySupport} object given {@code mongoClient}, {@code databaseName},
* {@code mongoInstanceCreated} and {@link PersistenceExceptionTranslator}.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @param mongoInstanceCreated {@literal true} if the client instance was created by a subclass of
* {@link MongoDatabaseFactorySupport} to close the client on {@link #destroy()}.
* @param exceptionTranslator must not be {@literal null}.
*/
protected MongoDatabaseFactorySupport(C mongoClient, String databaseName, boolean mongoInstanceCreated,
PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(mongoClient, "MongoClient must not be null!");
Assert.hasText(databaseName, "Database name must not be empty!");
Assert.isTrue(databaseName.matches("[^/\\\\.$\"\\s]+"),
"Database name must not contain slashes, dots, spaces, quotes, or dollar signs!");
this.mongoClient = mongoClient;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.exceptionTranslator = exceptionTranslator;
}
/**
* Configures the {@link WriteConcern} to be used on the {@link MongoDatabase} instance being created.
*
* @param writeConcern the writeConcern to set
*/
public void setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase()
*/
public MongoDatabase getMongoDatabase() throws DataAccessException {
return getMongoDatabase(getDefaultDatabaseName());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase(java.lang.String)
*/
@Override
public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
Assert.hasText(dbName, "Database name must not be empty!");
MongoDatabase db = doGetMongoDatabase(dbName);
if (writeConcern == null) {
return db;
}
return db.withWriteConcern(writeConcern);
}
/**
* Get the actual {@link MongoDatabase} from the client.
*
* @param dbName must not be {@literal null} or empty.
* @return
*/
protected abstract MongoDatabase doGetMongoDatabase(String dbName);
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
if (mongoInstanceCreated) {
closeClient();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
public PersistenceExceptionTranslator getExceptionTranslator() {
return this.exceptionTranslator;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.Session)
*/
public MongoDatabaseFactory withSession(ClientSession session) {
return new MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session, this);
}
/**
* Close the client instance.
*/
protected abstract void closeClient();
/**
* @return the Mongo client object.
*/
protected C getMongoClient() {
return mongoClient;
}
/**
* @return the database name.
*/
protected String getDefaultDatabaseName() {
return databaseName;
}
/**
* {@link ClientSession} bound {@link MongoDatabaseFactory} decorating the database with a
* {@link SessionAwareMethodInterceptor}.
*
* @author Christoph Strobl
* @since 2.1
*/
static final class ClientSessionBoundMongoDbFactory implements MongoDatabaseFactory {
private final ClientSession session;
private final MongoDatabaseFactory delegate;
public ClientSessionBoundMongoDbFactory(ClientSession session, MongoDatabaseFactory delegate) {
this.session = session;
this.delegate = delegate;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase()
*/
@Override
public MongoDatabase getMongoDatabase() throws DataAccessException {
return proxyMongoDatabase(delegate.getMongoDatabase());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase(java.lang.String)
*/
@Override
public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
return proxyMongoDatabase(delegate.getMongoDatabase(dbName));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
@Override
public PersistenceExceptionTranslator getExceptionTranslator() {
return delegate.getExceptionTranslator();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
*/
@Override
public ClientSession getSession(ClientSessionOptions options) {
return delegate.getSession(options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.ClientSession)
*/
@Override
public MongoDatabaseFactory withSession(ClientSession session) {
return delegate.withSession(session);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#isTransactionActive()
*/
@Override
public boolean isTransactionActive() {
return session != null && session.hasActiveTransaction();
}
private MongoDatabase proxyMongoDatabase(MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}
private MongoDatabase proxyDatabase(com.mongodb.session.ClientSession session, MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}
private MongoCollection<?> proxyCollection(com.mongodb.session.ClientSession session,
MongoCollection<?> collection) {
return createProxyInstance(session, collection, MongoCollection.class);
}
private <T> T createProxyInstance(com.mongodb.session.ClientSession session, T target, Class<T> targetType) {
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.setInterfaces(targetType);
factory.setOpaque(true);
factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class,
this::proxyDatabase, MongoCollection.class, this::proxyCollection));
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
public ClientSession getSession() {
return this.session;
}
public MongoDatabaseFactory getDelegate() {
return this.delegate;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ClientSessionBoundMongoDbFactory that = (ClientSessionBoundMongoDbFactory) o;
if (!ObjectUtils.nullSafeEquals(this.session, that.session)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.delegate, that.delegate);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(this.session);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.delegate);
return result;
}
public String toString() {
return "MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session=" + this.getSession() + ", delegate="
+ this.getDelegate() + ")";
}
}
}

View File

@@ -15,11 +15,26 @@
*/
package org.springframework.data.mongodb.core;
import lombok.Value;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.ClientSessionOptions;
import com.mongodb.DB;
import com.mongodb.WriteConcern;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* Common base class for usage with both {@link com.mongodb.client.MongoClients} defining common properties such as
* database name and exception translator.
* Common base class for usage with both {@link com.mongodb.client.MongoClients} and {@link com.mongodb.MongoClient}
* defining common properties such as database name and exception translator.
* <p/>
* Not intended to be used directly.
*
@@ -27,16 +42,22 @@ import org.springframework.dao.support.PersistenceExceptionTranslator;
* @author Mark Paluch
* @param <C> Client type.
* @since 2.1
* @see SimpleMongoClientDatabaseFactory
* @deprecated since 3.0, use {@link MongoDatabaseFactorySupport} instead.
* @see SimpleMongoDbFactory
* @see SimpleMongoClientDbFactory
*/
@Deprecated
public abstract class MongoDbFactorySupport<C> extends MongoDatabaseFactorySupport<C> {
public abstract class MongoDbFactorySupport<C> implements MongoDbFactory {
private final C mongoClient;
private final String databaseName;
private final boolean mongoInstanceCreated;
private final PersistenceExceptionTranslator exceptionTranslator;
private @Nullable WriteConcern writeConcern;
/**
* Create a new {@link MongoDbFactorySupport} object given {@code mongoClient}, {@code databaseName},
* {@code mongoInstanceCreated} and {@link PersistenceExceptionTranslator}.
*
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @param mongoInstanceCreated {@literal true} if the client instance was created by a subclass of
@@ -45,6 +66,207 @@ public abstract class MongoDbFactorySupport<C> extends MongoDatabaseFactorySuppo
*/
protected MongoDbFactorySupport(C mongoClient, String databaseName, boolean mongoInstanceCreated,
PersistenceExceptionTranslator exceptionTranslator) {
super(mongoClient, databaseName, mongoInstanceCreated, exceptionTranslator);
Assert.notNull(mongoClient, "MongoClient must not be null!");
Assert.hasText(databaseName, "Database name must not be empty!");
Assert.isTrue(databaseName.matches("[^/\\\\.$\"\\s]+"),
"Database name must not contain slashes, dots, spaces, quotes, or dollar signs!");
this.mongoClient = mongoClient;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.exceptionTranslator = exceptionTranslator;
}
/**
* Configures the {@link WriteConcern} to be used on the {@link MongoDatabase} instance being created.
*
* @param writeConcern the writeConcern to set
*/
public void setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getDb()
*/
public MongoDatabase getDb() throws DataAccessException {
return getDb(databaseName);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getDb(java.lang.String)
*/
@Override
public MongoDatabase getDb(String dbName) throws DataAccessException {
Assert.hasText(dbName, "Database name must not be empty!");
MongoDatabase db = doGetMongoDatabase(dbName);
if (writeConcern == null) {
return db;
}
return db.withWriteConcern(writeConcern);
}
/**
* Get the actual {@link MongoDatabase} from the client.
*
* @param dbName must not be {@literal null} or empty.
* @return
*/
protected abstract MongoDatabase doGetMongoDatabase(String dbName);
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
if (mongoInstanceCreated) {
closeClient();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
public PersistenceExceptionTranslator getExceptionTranslator() {
return this.exceptionTranslator;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.Session)
*/
public MongoDbFactory withSession(ClientSession session) {
return new MongoDbFactorySupport.ClientSessionBoundMongoDbFactory(session, this);
}
/**
* Close the client instance.
*/
protected abstract void closeClient();
/**
* @return the Mongo client object.
*/
protected C getMongoClient() {
return mongoClient;
}
/**
* @return the database name.
*/
protected String getDefaultDatabaseName() {
return databaseName;
}
/**
* {@link ClientSession} bound {@link MongoDbFactory} decorating the database with a
* {@link SessionAwareMethodInterceptor}.
*
* @author Christoph Strobl
* @since 2.1
*/
@Value
static class ClientSessionBoundMongoDbFactory implements MongoDbFactory {
ClientSession session;
MongoDbFactory delegate;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getDb()
*/
@Override
public MongoDatabase getDb() throws DataAccessException {
return proxyMongoDatabase(delegate.getDb());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getDb(java.lang.String)
*/
@Override
public MongoDatabase getDb(String dbName) throws DataAccessException {
return proxyMongoDatabase(delegate.getDb(dbName));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
*/
@Override
public PersistenceExceptionTranslator getExceptionTranslator() {
return delegate.getExceptionTranslator();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getLegacyDb()
*/
@Override
public DB getLegacyDb() {
return delegate.getLegacyDb();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
*/
@Override
public ClientSession getSession(ClientSessionOptions options) {
return delegate.getSession(options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.ClientSession)
*/
@Override
public MongoDbFactory withSession(ClientSession session) {
return delegate.withSession(session);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#isTransactionActive()
*/
@Override
public boolean isTransactionActive() {
return session != null && session.hasActiveTransaction();
}
private MongoDatabase proxyMongoDatabase(MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}
private MongoDatabase proxyDatabase(com.mongodb.session.ClientSession session, MongoDatabase database) {
return createProxyInstance(session, database, MongoDatabase.class);
}
private MongoCollection<?> proxyCollection(com.mongodb.session.ClientSession session,
MongoCollection<?> collection) {
return createProxyInstance(session, collection, MongoCollection.class);
}
private <T> T createProxyInstance(com.mongodb.session.ClientSession session, T target, Class<T> targetType) {
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.setInterfaces(targetType);
factory.setOpaque(true);
factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class,
this::proxyDatabase, MongoCollection.class, this::proxyCollection));
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.dao.PermissionDeniedDataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.ClientSessionException;
import org.springframework.data.mongodb.MongoTransactionException;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
@@ -36,6 +37,7 @@ import org.springframework.data.mongodb.util.MongoDbErrorCodes;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import com.mongodb.BulkWriteException;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.MongoException;
import com.mongodb.MongoServerException;
@@ -110,6 +112,10 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (ex instanceof BulkWriteException) {
return new BulkOperationException(ex.getMessage(), (BulkWriteException) ex);
}
// All other MongoExceptions
if (ex instanceof MongoException) {
@@ -129,7 +135,6 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
} else if (MongoDbErrorCodes.isTransactionFailureCode(code)) {
return new MongoTransactionException(ex.getMessage(), ex);
}
return new UncategorizedMongoDbException(ex.getMessage(), ex);
}

View File

@@ -27,7 +27,6 @@ import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
@@ -41,13 +40,13 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.Cursor;
import com.mongodb.ReadPreference;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoCollection;
@@ -58,7 +57,7 @@ import com.mongodb.client.result.UpdateResult;
* Interface that specifies a basic set of MongoDB operations. Implemented by {@link MongoTemplate}. Not often used but
* a useful option for extensibility and testability (as it can be easily mocked, stubbed, or be the target of a JDK
* proxy).
* <p/>
* <p />
* <strong>NOTE:</strong> Some operations cannot be executed within a MongoDB transaction. Please refer to the MongoDB
* specific documentation to learn more about <a href="https://docs.mongodb.com/manual/core/transactions/">Multi
* Document Transactions</a>.
@@ -176,7 +175,7 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Obtain a {@link ClientSession session} bound instance of {@link SessionScoped} binding the {@link ClientSession}
* provided by the given {@link Supplier} to each and every command issued against MongoDB.
* <p/>
* <p />
* <strong>Note:</strong> It is up to the caller to manage the {@link ClientSession} lifecycle. Use the
* {@link SessionScoped#execute(SessionCallback, Consumer)} hook to potentially close the {@link ClientSession}.
*
@@ -212,7 +211,7 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Obtain a {@link ClientSession} bound instance of {@link MongoOperations}.
* <p/>
* <p />
* <strong>Note:</strong> It is up to the caller to manage the {@link ClientSession} lifecycle.
*
* @param session must not be {@literal null}.
@@ -223,10 +222,9 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Executes the given {@link Query} on the entity collection of the specified {@code entityType} backed by a Mongo DB
* {@link com.mongodb.client.FindIterable}.
* {@link Cursor}.
* <p>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to
* be closed.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed.
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification. Must not be {@literal null}.
@@ -239,10 +237,9 @@ public interface MongoOperations extends FluentMongoOperations {
/**
* Executes the given {@link Query} on the entity collection of the specified {@code entityType} and collection backed
* by a Mongo DB {@link com.mongodb.client.FindIterable}.
* by a Mongo DB {@link Cursor}.
* <p>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.FindIterable} that needs to
* be closed.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed.
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification. Must not be {@literal null}.
@@ -361,7 +358,7 @@ public interface MongoOperations extends FluentMongoOperations {
IndexOperations indexOps(Class<?> entityClass);
/**
* Returns the {@link ScriptOperations} that can be performed on {@link com.mongodb.client.MongoDatabase} level.
* Returns the {@link ScriptOperations} that can be performed on {@link com.mongodb.DB} level.
*
* @return never {@literal null}.
* @since 1.7
@@ -519,11 +516,11 @@ public interface MongoOperations extends FluentMongoOperations {
<O> AggregationResults<O> aggregate(Aggregation aggregation, String collectionName, Class<O> outputType);
/**
* Execute an aggregation operation backed by a Mongo DB {@link com.mongodb.client.AggregateIterable}.
* Execute an aggregation operation backed by a Mongo DB {@link Cursor}.
* <p>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.AggregateIterable} that
* needs to be closed. The raw results will be mapped to the given entity class. The name of the inputCollection is
* derived from the inputType of the aggregation.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed. The raw
* results will be mapped to the given entity class. The name of the inputCollection is derived from the inputType of
* the aggregation.
* <p>
* Aggregation streaming can't be used with {@link AggregationOptions#isExplain() aggregation explain}. Enabling
* explanation mode will throw an {@link IllegalArgumentException}.
@@ -538,11 +535,11 @@ public interface MongoOperations extends FluentMongoOperations {
<O> CloseableIterator<O> aggregateStream(TypedAggregation<?> aggregation, String collectionName, Class<O> outputType);
/**
* Execute an aggregation operation backed by a Mongo DB {@link com.mongodb.client.AggregateIterable}.
* Execute an aggregation operation backed by a Mongo DB {@link Cursor}.
* <p/>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.AggregateIterable} that
* needs to be closed. The raw results will be mapped to the given entity class and are returned as stream. The name
* of the inputCollection is derived from the inputType of the aggregation.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed. The raw
* results will be mapped to the given entity class and are returned as stream. The name of the inputCollection is
* derived from the inputType of the aggregation.
* <p/>
* Aggregation streaming can't be used with {@link AggregationOptions#isExplain() aggregation explain}. Enabling
* explanation mode will throw an {@link IllegalArgumentException}.
@@ -556,10 +553,10 @@ public interface MongoOperations extends FluentMongoOperations {
<O> CloseableIterator<O> aggregateStream(TypedAggregation<?> aggregation, Class<O> outputType);
/**
* Execute an aggregation operation backed by a Mongo DB {@link com.mongodb.client.AggregateIterable}.
* Execute an aggregation operation backed by a Mongo DB {@link Cursor}.
* <p/>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.AggregateIterable} that
* needs to be closed. The raw results will be mapped to the given entity class.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed. The raw
* results will be mapped to the given entity class.
* <p/>
* Aggregation streaming can't be used with {@link AggregationOptions#isExplain() aggregation explain}. Enabling
* explanation mode will throw an {@link IllegalArgumentException}.
@@ -575,10 +572,10 @@ public interface MongoOperations extends FluentMongoOperations {
<O> CloseableIterator<O> aggregateStream(Aggregation aggregation, Class<?> inputType, Class<O> outputType);
/**
* Execute an aggregation operation backed by a Mongo DB {@link com.mongodb.client.AggregateIterable}.
* Execute an aggregation operation backed by a Mongo DB {@link Cursor}.
* <p/>
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.client.AggregateIterable} that
* needs to be closed. The raw results will be mapped to the given entity class.
* Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link Cursor} that needs to be closed. The raw
* results will be mapped to the given entity class.
* <p/>
* Aggregation streaming can't be used with {@link AggregationOptions#isExplain() aggregation explain}. Enabling
* explanation mode will throw an {@link IllegalArgumentException}.
@@ -886,15 +883,12 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @return the converted object that was updated before it was updated or {@literal null}, if not found.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
@Nullable
<T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify <a/>
@@ -902,16 +896,13 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @param collectionName the collection to query. Must not be {@literal null}.
* @return the converted object that was updated before it was updated or {@literal null}, if not found.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
@Nullable
<T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName);
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify <a/>
@@ -920,18 +911,15 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification.
* @param update the {@link UpdateDefinition} to apply on matching documents.
* @param update the {@link Update} to apply on matching documents.
* @param options the {@link FindAndModifyOptions} holding additional information.
* @param entityClass the parametrized type.
* @return the converted object that was updated or {@literal null}, if not found. Depending on the value of
* {@link FindAndModifyOptions#isReturnNew()} this will either be the object as it was before the update or as
* it is after the update.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
@Nullable
<T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify <a/>
@@ -940,19 +928,16 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @param collectionName the collection to query. Must not be {@literal null}.
* @return the converted object that was updated or {@literal null}, if not found. Depending on the value of
* {@link FindAndModifyOptions#isReturnNew()} this will either be the object as it was before the update or as
* it is after the update.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
@Nullable
<T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
String collectionName);
/**
@@ -1184,29 +1169,6 @@ public interface MongoOperations extends FluentMongoOperations {
*/
long count(Query query, String collectionName);
/**
* Estimate the number of documents, in the collection {@link #getCollectionName(Class) identified by the given type},
* based on collection statistics.
*
* @param entityClass must not be {@literal null}.
* @return the estimated number of documents.
* @since 3.1
*/
default long estimatedCount(Class<?> entityClass) {
Assert.notNull(entityClass, "Entity class must not be null!");
return estimatedCount(getCollectionName(entityClass));
}
/**
* Estimate the number of documents in the given collection based on collection statistics.
*
* @param collectionName must not be {@literal null}.
* @return the estimated number of documents.
* @since 3.1
*/
long estimatedCount(String collectionName);
/**
* Returns the number of documents for the given {@link Query} by querying the given collection using the given entity
* class to map the given {@link Query}. <br />
@@ -1324,111 +1286,99 @@ public interface MongoOperations extends FluentMongoOperations {
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, FindAndModifyOptions, Class, String)} instead.
* Use {@link #findAndModify(Query, Update, FindAndModifyOptions, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param entityClass class that determines the collection to use. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult upsert(Query query, UpdateDefinition update, Class<?> entityClass);
UpdateResult upsert(Query query, Update update, Class<?> entityClass);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #upsert(Query, UpdateDefinition, Class, String)} to get full type specific
* support. <br />
* domain type information. Use {@link #upsert(Query, Update, Class, String)} to get full type specific support.
* <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, FindAndModifyOptions, Class, String)} instead.
* Use {@link #findAndModify(Query, Update, FindAndModifyOptions, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult upsert(Query query, UpdateDefinition update, String collectionName);
UpdateResult upsert(Query query, Update update, String collectionName);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
*
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, FindAndModifyOptions, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult upsert(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
UpdateResult upsert(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates the first object that is found in the collection of the entity class that matches the query document with
* the provided update document.
* the provided update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class that determines the collection to use.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateFirst(Query query, UpdateDefinition update, Class<?> entityClass);
UpdateResult updateFirst(Query query, Update update, Class<?> entityClass);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* the provided updated document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #updateFirst(Query, UpdateDefinition, Class, String)} to get full type specific
* support. <br />
* domain type information. Use {@link #updateFirst(Query, Update, Class, String)} to get full type specific support.
* <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, Class, String)} instead.
* Use {@link #findAndModify(Query, Update, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateFirst(Query query, UpdateDefinition update, String collectionName);
UpdateResult updateFirst(Query query, Update update, String collectionName);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* the provided updated document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateFirst(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
UpdateResult updateFirst(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
@@ -1436,34 +1386,27 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateMulti(Query query, UpdateDefinition update, Class<?> entityClass);
UpdateResult updateMulti(Query query, Update update, Class<?> entityClass);
/**
* Updates all objects that are found in the specified collection that matches the query document criteria with the
* provided updated document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #updateMulti(Query, UpdateDefinition, Class, String)} to get full type specific
* support.
* domain type information. Use {@link #updateMulti(Query, Update, Class, String)} to get full type specific support.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateMulti(Query query, UpdateDefinition update, String collectionName);
UpdateResult updateMulti(Query query, Update update, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
@@ -1471,16 +1414,13 @@ public interface MongoOperations extends FluentMongoOperations {
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
UpdateResult updateMulti(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
UpdateResult updateMulti(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Remove the given object from the collection by {@literal id} and (if applicable) its

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.bson.Document;
import org.springframework.data.mapping.SimplePropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
@@ -30,14 +33,11 @@ import org.springframework.util.ClassUtils;
* @author Christoph Strobl
* @since 2.1
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
class PropertyOperations {
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
PropertyOperations(MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
this.mappingContext = mappingContext;
}
/**
* For cases where {@code fields} is {@link Document#isEmpty() empty} include only fields that are required for
* creating the projection (target) type if the {@code targetType} is a {@literal DTO projection} or a

View File

@@ -1,763 +0,0 @@
/*
* Copyright 2020 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.CodecRegistryProvider;
import org.springframework.data.mongodb.core.MappedDocument.MappedUpdate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.ShardKey;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.UpdateOptions;
/**
* {@link QueryOperations} centralizes common operations required before an operation is actually ready to be executed.
* This involves mapping {@link Query queries} into their respective MongoDB representation, computing execution options
* for {@literal count}, {@literal remove}, and other methods.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0
*/
class QueryOperations {
private final QueryMapper queryMapper;
private final UpdateMapper updateMapper;
private final EntityOperations entityOperations;
private final PropertyOperations propertyOperations;
private final CodecRegistryProvider codecRegistryProvider;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final AggregationUtil aggregationUtil;
private final Map<Class<?>, Document> mappedShardKey = new ConcurrentHashMap<>(1);
/**
* Create a new instance of {@link QueryOperations}.
*
* @param queryMapper must not be {@literal null}.
* @param updateMapper must not be {@literal null}.
* @param entityOperations must not be {@literal null}.
* @param propertyOperations must not be {@literal null}.
* @param codecRegistryProvider must not be {@literal null}.
*/
QueryOperations(QueryMapper queryMapper, UpdateMapper updateMapper, EntityOperations entityOperations,
PropertyOperations propertyOperations, CodecRegistryProvider codecRegistryProvider) {
this.queryMapper = queryMapper;
this.updateMapper = updateMapper;
this.entityOperations = entityOperations;
this.propertyOperations = propertyOperations;
this.codecRegistryProvider = codecRegistryProvider;
this.mappingContext = queryMapper.getMappingContext();
this.aggregationUtil = new AggregationUtil(queryMapper, mappingContext);
}
/**
* Create a new {@link QueryContext} instance.
*
* @param query must not be {@literal null}.
* @return new instance of {@link QueryContext}.
*/
QueryContext createQueryContext(Query query) {
return new QueryContext(query);
}
/**
* Create a new {@link DistinctQueryContext} instance.
*
* @param query must not be {@literal null}.
* @return new instance of {@link DistinctQueryContext}.
*/
DistinctQueryContext distinctQueryContext(Query query, String fieldName) {
return new DistinctQueryContext(query, fieldName);
}
/**
* Create a new {@link CountContext} instance.
*
* @param query must not be {@literal null}.
* @return new instance of {@link CountContext}.
*/
CountContext countQueryContext(Query query) {
return new CountContext(query);
}
/**
* Create a new {@link UpdateContext} instance affecting multiple documents.
*
* @param updateDefinition must not be {@literal null}.
* @param query must not be {@literal null}.
* @param upsert use {@literal true} to insert diff when no existing document found.
* @return new instance of {@link UpdateContext}.
*/
UpdateContext updateContext(UpdateDefinition updateDefinition, Query query, boolean upsert) {
return new UpdateContext(updateDefinition, query, true, upsert);
}
/**
* Create a new {@link UpdateContext} instance affecting a single document.
*
* @param updateDefinition must not be {@literal null}.
* @param query must not be {@literal null}.
* @param upsert use {@literal true} to insert diff when no existing document found.
* @return new instance of {@link UpdateContext}.
*/
UpdateContext updateSingleContext(UpdateDefinition updateDefinition, Query query, boolean upsert) {
return new UpdateContext(updateDefinition, query, false, upsert);
}
/**
* Create a new {@link UpdateContext} instance affecting a single document.
*
* @param updateDefinition must not be {@literal null}.
* @param query must not be {@literal null}.
* @param upsert use {@literal true} to insert diff when no existing document found.
* @return new instance of {@link UpdateContext}.
*/
UpdateContext updateSingleContext(UpdateDefinition updateDefinition, Document query, boolean upsert) {
return new UpdateContext(updateDefinition, query, false, upsert);
}
/**
* @param replacement the {@link MappedDocument mapped replacement} document.
* @param upsert use {@literal true} to insert diff when no existing document found.
* @return new instance of {@link UpdateContext}.
*/
UpdateContext replaceSingleContext(MappedDocument replacement, boolean upsert) {
return new UpdateContext(replacement, upsert);
}
/**
* Create a new {@link DeleteContext} instance removing all matching documents.
*
* @param query must not be {@literal null}.
* @return new instance of {@link QueryContext}.
*/
DeleteContext deleteQueryContext(Query query) {
return new DeleteContext(query, true);
}
/**
* Create a new {@link DeleteContext} instance only the first matching document.
*
* @param query must not be {@literal null}.
* @return new instance of {@link QueryContext}.
*/
DeleteContext deleteSingleContext(Query query) {
return new DeleteContext(query, false);
}
/**
* {@link QueryContext} encapsulates common tasks required to convert a {@link Query} into its MongoDB document
* representation, mapping fieldnames, as well as determinging and applying {@link Collation collations}.
*
* @author Christoph Strobl
*/
class QueryContext {
private final Query query;
/**
* Create new a {@link QueryContext} instance from the given {@literal query} (can be eihter a {@link Query} or a
* plain {@link Document}.
*
* @param query can be {@literal null}.
*/
private QueryContext(@Nullable Query query) {
this.query = query != null ? query : new Query();
}
/**
* @return never {@literal null}.
*/
Query getQuery() {
return query;
}
/**
* Extract the raw {@link Query#getQueryObject() unmapped document} from the {@link Query}.
*
* @return
*/
Document getQueryObject() {
return query.getQueryObject();
}
/**
* Get the already mapped MongoDB query representation.
*
* @param domainType can be {@literal null}.
* @param entityLookup the {@link Function lookup} used to provide the {@link MongoPersistentEntity} for the
* given{@literal domainType}
* @param <T>
* @return never {@literal null}.
*/
<T> Document getMappedQuery(@Nullable Class<T> domainType,
Function<Class<T>, MongoPersistentEntity<?>> entityLookup) {
return getMappedQuery(domainType == null ? null : entityLookup.apply(domainType));
}
/**
* Get the already mapped MongoDB query representation.
*
* @param entity the Entity to map field names to. Can be {@literal null}.
* @param <T>
* @return never {@literal null}.
*/
<T> Document getMappedQuery(@Nullable MongoPersistentEntity<T> entity) {
return queryMapper.getMappedObject(getQueryObject(), entity);
}
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, Class<?> targetType,
ProjectionFactory projectionFactory) {
Document fields = query.getFieldsObject();
Document mappedFields = fields;
if (entity == null) {
return mappedFields;
}
Document projectedFields = propertyOperations.computeFieldsForProjection(projectionFactory, fields,
entity.getType(), targetType);
if (ObjectUtils.nullSafeEquals(fields, projectedFields)) {
mappedFields = queryMapper.getMappedFields(projectedFields, entity);
} else {
mappedFields = queryMapper.getMappedFields(projectedFields,
mappingContext.getRequiredPersistentEntity(targetType));
}
if (entity != null && entity.hasTextScoreProperty() && !query.getQueryObject().containsKey("$text")) {
mappedFields.remove(entity.getTextScoreProperty().getFieldName());
}
return mappedFields;
}
/**
* Get the already mapped {@link Query#getSortObject() sort} option.
*
* @param entity the Entity to map field names to. Can be {@literal null}.
* @return never {@literal null}.
*/
Document getMappedSort(@Nullable MongoPersistentEntity<?> entity) {
return queryMapper.getMappedSort(query.getSortObject(), entity);
}
/**
* Apply the {@link com.mongodb.client.model.Collation} if present extracted from the {@link Query} or fall back to
* the {@literal domain types} default {@link org.springframework.data.mongodb.core.mapping.Document#collation()
* collation}.
*
* @param domainType can be {@literal null}.
* @param consumer must not be {@literal null}.
*/
void applyCollation(@Nullable Class<?> domainType, Consumer<com.mongodb.client.model.Collation> consumer) {
getCollation(domainType).ifPresent(consumer::accept);
}
/**
* Get the {@link com.mongodb.client.model.Collation} extracted from the {@link Query} if present or fall back to
* the {@literal domain types} default {@link org.springframework.data.mongodb.core.mapping.Document#collation()
* collation}.
*
* @param domainType can be {@literal null}.
* @return never {@literal null}.
*/
Optional<com.mongodb.client.model.Collation> getCollation(@Nullable Class<?> domainType) {
return entityOperations.forType(domainType).getCollation(query) //
.map(Collation::toMongoCollation);
}
}
/**
* A {@link QueryContext} that encapsulates common tasks required when running {@literal distinct} queries.
*
* @author Christoph Strobl
*/
class DistinctQueryContext extends QueryContext {
private final String fieldName;
/**
* Create a new {@link DistinctQueryContext} instance.
*
* @param query can be {@literal null}.
* @param fieldName must not be {@literal null}.
*/
private DistinctQueryContext(@Nullable Object query, String fieldName) {
super(query instanceof Document ? new BasicQuery((Document) query) : (Query) query);
this.fieldName = fieldName;
}
@Override
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, Class<?> targetType, ProjectionFactory projectionFactory) {
return getMappedFields(entity);
}
Document getMappedFields(@Nullable MongoPersistentEntity<?> entity) {
return queryMapper.getMappedFields(new Document(fieldName, 1), entity);
}
/**
* Get the mapped field name to project to.
*
* @param entity can be {@literal null}.
* @return never {@literal null}.
*/
String getMappedFieldName(@Nullable MongoPersistentEntity<?> entity) {
return getMappedFields(entity).keySet().iterator().next();
}
/**
* Get the MongoDB native representation of the given {@literal type}.
*
* @param type must not be {@literal null}.
* @param <T>
* @return never {@literal null}.
*/
@SuppressWarnings("unchecked")
<T> Class<T> getDriverCompatibleClass(Class<T> type) {
return codecRegistryProvider.getCodecFor(type) //
.map(Codec::getEncoderClass) //
.orElse((Class<T>) BsonValue.class);
}
/**
* Get the most speficic read target type based on the user {@literal requestedTargetType} an the property type
* based on meta information extracted from the {@literal domainType}.
*
* @param requestedTargetType must not be {@literal null}.
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
Class<?> getMostSpecificConversionTargetType(Class<?> requestedTargetType, Class<?> domainType) {
Class<?> conversionTargetType = requestedTargetType;
try {
Class<?> propertyType = PropertyPath.from(fieldName, domainType).getLeafProperty().getLeafType();
// use the more specific type but favor UserType over property one
if (ClassUtils.isAssignable(requestedTargetType, propertyType)) {
conversionTargetType = propertyType;
}
} catch (PropertyReferenceException e) {
// just don't care about it as we default to Object.class anyway.
}
return conversionTargetType;
}
}
/**
* A {@link QueryContext} that encapsulates common tasks required when running {@literal count} queries.
*
* @author Christoph Strobl
*/
class CountContext extends QueryContext {
/**
* Creates a new {@link CountContext} instance.
*
* @param query can be {@literal null}.
*/
CountContext(@Nullable Query query) {
super(query);
}
/**
* Get the {@link CountOptions} applicable for the {@link Query}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
CountOptions getCountOptions(@Nullable Class<?> domainType) {
return getCountOptions(domainType, null);
}
/**
* Get the {@link CountOptions} applicable for the {@link Query}.
*
* @param domainType can be {@literal null}.
* @param callback a callback to modify the generated options. Can be {@literal null}.
* @return
*/
CountOptions getCountOptions(@Nullable Class<?> domainType, @Nullable Consumer<CountOptions> callback) {
CountOptions options = new CountOptions();
Query query = getQuery();
applyCollation(domainType, options::collation);
if (query.getLimit() > 0) {
options.limit(query.getLimit());
}
if (query.getSkip() > 0) {
options.skip((int) query.getSkip());
}
if (StringUtils.hasText(query.getHint())) {
String hint = query.getHint();
if (BsonUtils.isJsonDocument(hint)) {
options.hint(BsonUtils.parse(hint, codecRegistryProvider));
} else {
options.hintString(hint);
}
}
if (callback != null) {
callback.accept(options);
}
return options;
}
}
/**
* A {@link QueryContext} that encapsulates common tasks required when running {@literal delete} queries.
*
* @author Christoph Strobl
*/
class DeleteContext extends QueryContext {
private final boolean multi;
/**
* Crate a new {@link DeleteContext} instance.
*
* @param query can be {@literal null}.
* @param multi use {@literal true} to remove all matching documents, {@literal false} for just the first one.
*/
DeleteContext(@Nullable Query query, boolean multi) {
super(query);
this.multi = multi;
}
/**
* Get the {@link DeleteOptions} applicable for the {@link Query}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
DeleteOptions getDeleteOptions(@Nullable Class<?> domainType) {
return getDeleteOptions(domainType, null);
}
/**
* Get the {@link DeleteOptions} applicable for the {@link Query}.
*
* @param domainType can be {@literal null}.
* @param callback a callback to modify the generated options. Can be {@literal null}.
* @return
*/
DeleteOptions getDeleteOptions(@Nullable Class<?> domainType, @Nullable Consumer<DeleteOptions> callback) {
DeleteOptions options = new DeleteOptions();
applyCollation(domainType, options::collation);
if (callback != null) {
callback.accept(options);
}
return options;
}
/**
* @return {@literal true} if all matching documents shall be deleted.
*/
boolean isMulti() {
return multi;
}
}
/**
* A {@link QueryContext} that encapsulates common tasks required when running {@literal updates}.
*/
class UpdateContext extends QueryContext {
private final boolean multi;
private final boolean upsert;
private final @Nullable UpdateDefinition update;
private final @Nullable MappedDocument mappedDocument;
/**
* Create a new {@link UpdateContext} instance.
*
* @param update must not be {@literal null}.
* @param query must not be {@literal null}.
* @param multi use {@literal true} to update all matching documents.
* @param upsert use {@literal true} to insert a new document if none match.
*/
UpdateContext(UpdateDefinition update, Document query, boolean multi, boolean upsert) {
this(update, new BasicQuery(query), multi, upsert);
}
/**
* Create a new {@link UpdateContext} instance.
*
* @param update must not be {@literal null}.
* @param query can be {@literal null}.
* @param multi use {@literal true} to update all matching documents.
* @param upsert use {@literal true} to insert a new document if none match.
*/
UpdateContext(UpdateDefinition update, @Nullable Query query, boolean multi, boolean upsert) {
super(query);
this.multi = multi;
this.upsert = upsert;
this.update = update;
this.mappedDocument = null;
}
UpdateContext(MappedDocument update, boolean upsert) {
super(new BasicQuery(new Document(BsonUtils.asMap(update.getIdFilter()))));
this.multi = false;
this.upsert = upsert;
this.mappedDocument = update;
this.update = null;
}
/**
* Get the {@link UpdateOptions} applicable for the {@link Query}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
UpdateOptions getUpdateOptions(@Nullable Class<?> domainType) {
return getUpdateOptions(domainType, null);
}
/**
* Get the {@link UpdateOptions} applicable for the {@link Query}.
*
* @param domainType can be {@literal null}.
* @param callback a callback to modify the generated options. Can be {@literal null}.
* @return
*/
UpdateOptions getUpdateOptions(@Nullable Class<?> domainType, @Nullable Consumer<UpdateOptions> callback) {
UpdateOptions options = new UpdateOptions();
options.upsert(upsert);
if (update != null && update.hasArrayFilters()) {
options
.arrayFilters(update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()));
}
applyCollation(domainType, options::collation);
if (callback != null) {
callback.accept(options);
}
return options;
}
/**
* Get the {@link ReplaceOptions} applicable for the {@link Query}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
ReplaceOptions getReplaceOptions(@Nullable Class<?> domainType) {
return getReplaceOptions(domainType, null);
}
/**
* Get the {@link ReplaceOptions} applicable for the {@link Query}.
*
* @param domainType can be {@literal null}.
* @param callback a callback to modify the generated options. Can be {@literal null}.
* @return
*/
ReplaceOptions getReplaceOptions(@Nullable Class<?> domainType, @Nullable Consumer<ReplaceOptions> callback) {
UpdateOptions updateOptions = getUpdateOptions(domainType);
ReplaceOptions options = new ReplaceOptions();
options.collation(updateOptions.getCollation());
options.upsert(updateOptions.isUpsert());
if (callback != null) {
callback.accept(options);
}
return options;
}
@Override
<T> Document getMappedQuery(@Nullable MongoPersistentEntity<T> domainType) {
Document mappedQuery = super.getMappedQuery(domainType);
if (multi && update.isIsolated() && !mappedQuery.containsKey("$isolated")) {
mappedQuery.put("$isolated", 1);
}
return mappedQuery;
}
<T> Document applyShardKey(MongoPersistentEntity<T> domainType, Document filter, @Nullable Document existing) {
Document shardKeySource = existing != null ? existing
: mappedDocument != null ? mappedDocument.getDocument() : getMappedUpdate(domainType);
Document filterWithShardKey = new Document(filter);
getMappedShardKeyFields(domainType).forEach(key -> filterWithShardKey.putIfAbsent(key, shardKeySource.get(key)));
return filterWithShardKey;
}
boolean requiresShardKey(Document filter, @Nullable MongoPersistentEntity<?> domainType) {
return !multi && domainType != null && domainType.isSharded() && !shardedById(domainType)
&& !filter.keySet().containsAll(getMappedShardKeyFields(domainType));
}
/**
* @return {@literal true} if the {@link MongoPersistentEntity#getShardKey() shard key} is the entities
* {@literal id} property.
* @since 3.0
*/
private boolean shardedById(MongoPersistentEntity<?> domainType) {
ShardKey shardKey = domainType.getShardKey();
if (shardKey.size() != 1) {
return false;
}
String key = shardKey.getPropertyNames().iterator().next();
if ("_id".equals(key)) {
return true;
}
MongoPersistentProperty idProperty = domainType.getIdProperty();
return idProperty != null && idProperty.getName().equals(key);
}
Set<String> getMappedShardKeyFields(MongoPersistentEntity<?> entity) {
return getMappedShardKey(entity).keySet();
}
Document getMappedShardKey(MongoPersistentEntity<?> entity) {
return mappedShardKey.computeIfAbsent(entity.getType(),
key -> queryMapper.getMappedFields(entity.getShardKey().getDocument(), entity));
}
/**
* Get the already mapped aggregation pipeline to use with an {@link #isAggregationUpdate()}.
*
* @param domainType must not be {@literal null}.
* @return never {@literal null}.
*/
List<Document> getUpdatePipeline(@Nullable Class<?> domainType) {
AggregationOperationContext context = domainType != null
? new RelaxedTypeBasedAggregationOperationContext(domainType, mappingContext, queryMapper)
: Aggregation.DEFAULT_CONTEXT;
return aggregationUtil.createPipeline((AggregationUpdate) update, context);
}
/**
* Get the already mapped update {@link Document}.
*
* @param entity
* @return
*/
Document getMappedUpdate(@Nullable MongoPersistentEntity<?> entity) {
if (update != null) {
return update instanceof MappedUpdate ? update.getUpdateObject()
: updateMapper.getMappedObject(update.getUpdateObject(), entity);
}
return mappedDocument.getDocument();
}
/**
* Increase a potential {@link MongoPersistentEntity#getVersionProperty() version property} prior to update if not
* already done in the actual {@link UpdateDefinition}
*
* @param persistentEntity can be {@literal null}.
*/
void increaseVersionForUpdateIfNecessary(@Nullable MongoPersistentEntity<?> persistentEntity) {
if (persistentEntity != null && persistentEntity.hasVersionProperty()) {
String versionFieldName = persistentEntity.getRequiredVersionProperty().getFieldName();
if (!update.modifies(versionFieldName)) {
update.inc(versionFieldName);
}
}
}
/**
* @return {@literal true} if the update holds an aggregation pipeline.
*/
boolean isAggregationUpdate() {
return update instanceof AggregationUpdate;
}
/**
* @return {@literal true} if all matching documents should be updated.
*/
boolean isMulti() {
return multi;
}
}
}

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Flux;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
@@ -58,22 +62,15 @@ class ReactiveAggregationOperationSupport implements ReactiveAggregationOperatio
return new ReactiveAggregationSupport<>(template, domainType, null, null);
}
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveAggregationSupport<T>
implements AggregationOperationWithAggregation<T>, ReactiveAggregation<T>, TerminatingAggregationOperation<T> {
private final ReactiveMongoTemplate template;
private final Class<T> domainType;
private final Aggregation aggregation;
private final String collection;
ReactiveAggregationSupport(ReactiveMongoTemplate template, Class<T> domainType, Aggregation aggregation,
String collection) {
this.template = template;
this.domainType = domainType;
this.aggregation = aggregation;
this.collection = collection;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<T> domainType;
Aggregation aggregation;
String collection;
/*
* (non-Javadoc)

View File

@@ -19,7 +19,6 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
@@ -39,14 +38,13 @@ import org.springframework.data.mongodb.core.query.Query;
* query(Human.class)
* .inCollection("star-wars")
* .as(Jedi.class)
* .matching(where("firstname").is("luke"))
* .matching(query(where("firstname").is("luke")))
* .all();
* </code>
* </pre>
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Juergen Zimmermann
* @since 2.0
*/
public interface ReactiveFindOperation {
@@ -146,18 +144,6 @@ public interface ReactiveFindOperation {
*/
TerminatingFind<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingFind}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingFind<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
/**
* Set the filter query for the geoNear execution.
*
@@ -273,21 +259,9 @@ public interface ReactiveFindOperation {
*
* @param query must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if query is {@literal null}.
* @throws IllegalArgumentException if resultType is {@literal null}.
*/
TerminatingDistinct<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingDistinct<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -15,10 +15,15 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.bson.Document;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
@@ -34,15 +39,12 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
class ReactiveFindOperationSupport implements ReactiveFindOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveMongoTemplate template;
ReactiveFindOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
private final @NonNull ReactiveMongoTemplate template;
/*
* (non-Javadoc)
@@ -62,24 +64,16 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation {
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveFindSupport<T>
implements ReactiveFind<T>, FindWithCollection<T>, FindWithProjection<T>, FindWithQuery<T> {
private final ReactiveMongoTemplate template;
private final Class<?> domainType;
private final Class<T> returnType;
private final String collection;
private final Query query;
ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType,
String collection, Query query) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.collection = collection;
this.query = query;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<?> domainType;
Class<T> returnType;
String collection;
Query query;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -30,13 +34,10 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
class ReactiveInsertOperationSupport implements ReactiveInsertOperation {
private final ReactiveMongoTemplate template;
ReactiveInsertOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
private final @NonNull ReactiveMongoTemplate template;
/*
* (non-Javadoc)
@@ -50,18 +51,13 @@ class ReactiveInsertOperationSupport implements ReactiveInsertOperation {
return new ReactiveInsertSupport<>(template, domainType, null);
}
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveInsertSupport<T> implements ReactiveInsert<T> {
private final ReactiveMongoTemplate template;
private final Class<T> domainType;
private final String collection;
ReactiveInsertSupport(ReactiveMongoTemplate template, Class<T> domainType, String collection) {
this.template = template;
this.domainType = domainType;
this.collection = collection;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<T> domainType;
String collection;
/*
* (non-Javadoc)

View File

@@ -19,7 +19,6 @@ import reactor.core.publisher.Flux;
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
/**
@@ -31,7 +30,7 @@ import org.springframework.data.mongodb.core.query.Query;
* The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
* via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
* collection name for the execution.
*
*
* <pre>
* <code>
* mapReduce(Human.class)
@@ -147,18 +146,6 @@ public interface ReactiveMapReduceOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingMapReduce<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingMapReduce}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingMapReduce<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Flux;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
@@ -29,15 +31,12 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @since 2.1
*/
@RequiredArgsConstructor
class ReactiveMapReduceOperationSupport implements ReactiveMapReduceOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveMongoTemplate template;
ReactiveMapReduceOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
private final @NonNull ReactiveMongoTemplate template;
/*
* (non-Javascript)

View File

@@ -22,7 +22,7 @@ import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import com.mongodb.MongoClientSettings;
import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;

View File

@@ -15,17 +15,192 @@
*/
package org.springframework.data.mongodb.core;
import com.mongodb.MongoClientSettings;
import java.util.ArrayList;
import java.util.List;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.util.Assert;
import com.mongodb.MongoCredential;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.connection.ClusterSettings;
import com.mongodb.connection.ConnectionPoolSettings;
import com.mongodb.connection.ServerSettings;
import com.mongodb.connection.SocketSettings;
import com.mongodb.connection.SslSettings;
import com.mongodb.connection.StreamFactoryFactory;
/**
* A factory bean for construction of a {@link MongoClientSettings} instance to be used with the async MongoDB driver.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.0
* @deprecated since 3.0 - Use {@link MongoClientSettingsFactoryBean} instead.
*/
@Deprecated
public class ReactiveMongoClientSettingsFactoryBean extends MongoClientSettingsFactoryBean {
public class ReactiveMongoClientSettingsFactoryBean extends AbstractFactoryBean<MongoClientSettings> {
private static final MongoClientSettings DEFAULT_MONGO_SETTINGS = MongoClientSettings.builder().build();
private ReadPreference readPreference = DEFAULT_MONGO_SETTINGS.getReadPreference();
private WriteConcern writeConcern = DEFAULT_MONGO_SETTINGS.getWriteConcern();
private ReadConcern readConcern = DEFAULT_MONGO_SETTINGS.getReadConcern();
private List<MongoCredential> credentialList = new ArrayList<>();
private StreamFactoryFactory streamFactoryFactory = DEFAULT_MONGO_SETTINGS.getStreamFactoryFactory();
private CodecRegistry codecRegistry = DEFAULT_MONGO_SETTINGS.getCodecRegistry();
private ClusterSettings clusterSettings = DEFAULT_MONGO_SETTINGS.getClusterSettings();
private SocketSettings socketSettings = DEFAULT_MONGO_SETTINGS.getSocketSettings();
private SocketSettings heartbeatSocketSettings = DEFAULT_MONGO_SETTINGS.getHeartbeatSocketSettings();
private ConnectionPoolSettings connectionPoolSettings = DEFAULT_MONGO_SETTINGS.getConnectionPoolSettings();
private ServerSettings serverSettings = DEFAULT_MONGO_SETTINGS.getServerSettings();
private SslSettings sslSettings = DEFAULT_MONGO_SETTINGS.getSslSettings();
/**
* Set the {@link ReadPreference}.
*
* @param readPreference
*/
public void setReadPreference(ReadPreference readPreference) {
this.readPreference = readPreference;
}
/**
* Set the {@link WriteConcern}.
*
* @param writeConcern
*/
public void setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
}
/**
* Set the {@link ReadConcern}.
*
* @param readConcern
*/
public void setReadConcern(ReadConcern readConcern) {
this.readConcern = readConcern;
}
/**
* Set the List of {@link MongoCredential}s.
*
* @param credentialList must not be {@literal null}.
*/
public void setCredentialList(List<MongoCredential> credentialList) {
Assert.notNull(credentialList, "CredendialList must not be null!");
this.credentialList.addAll(credentialList);
}
/**
* Adds the {@link MongoCredential} to the list of credentials.
*
* @param mongoCredential must not be {@literal null}.
*/
public void addMongoCredential(MongoCredential mongoCredential) {
Assert.notNull(mongoCredential, "MongoCredential must not be null!");
this.credentialList.add(mongoCredential);
}
/**
* Set the {@link StreamFactoryFactory}.
*
* @param streamFactoryFactory
*/
public void setStreamFactoryFactory(StreamFactoryFactory streamFactoryFactory) {
this.streamFactoryFactory = streamFactoryFactory;
}
/**
* Set the {@link CodecRegistry}.
*
* @param codecRegistry
*/
public void setCodecRegistry(CodecRegistry codecRegistry) {
this.codecRegistry = codecRegistry;
}
/**
* Set the {@link ClusterSettings}.
*
* @param clusterSettings
*/
public void setClusterSettings(ClusterSettings clusterSettings) {
this.clusterSettings = clusterSettings;
}
/**
* Set the {@link SocketSettings}.
*
* @param socketSettings
*/
public void setSocketSettings(SocketSettings socketSettings) {
this.socketSettings = socketSettings;
}
/**
* Set the heartbeat {@link SocketSettings}.
*
* @param heartbeatSocketSettings
*/
public void setHeartbeatSocketSettings(SocketSettings heartbeatSocketSettings) {
this.heartbeatSocketSettings = heartbeatSocketSettings;
}
/**
* Set the {@link ConnectionPoolSettings}.
*
* @param connectionPoolSettings
*/
public void setConnectionPoolSettings(ConnectionPoolSettings connectionPoolSettings) {
this.connectionPoolSettings = connectionPoolSettings;
}
/**
* Set the {@link ServerSettings}.
*
* @param serverSettings
*/
public void setServerSettings(ServerSettings serverSettings) {
this.serverSettings = serverSettings;
}
/**
* Set the {@link SslSettings}.
*
* @param sslSettings
*/
public void setSslSettings(SslSettings sslSettings) {
this.sslSettings = sslSettings;
}
@Override
public Class<?> getObjectType() {
return MongoClientSettings.class;
}
@Override
protected MongoClientSettings createInstance() throws Exception {
return MongoClientSettings.builder() //
.readPreference(readPreference) //
.writeConcern(writeConcern) //
.readConcern(readConcern) //
.credentialList(credentialList) //
.streamFactoryFactory(streamFactoryFactory) //
.codecRegistry(codecRegistry) //
.clusterSettings(clusterSettings) //
.socketSettings(socketSettings) //
.heartbeatSocketSettings(heartbeatSocketSettings) //
.connectionPoolSettings(connectionPoolSettings) //
.serverSettings(serverSettings) //
.sslSettings(sslSettings) //
.build();
}
}

View File

@@ -15,15 +15,11 @@
*/
package org.springframework.data.mongodb.core;
import org.reactivestreams.Publisher;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.util.Assert;
import com.mongodb.reactivestreams.client.ClientSession;
/**
@@ -33,7 +29,7 @@ import com.mongodb.reactivestreams.client.ClientSession;
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.1
* @see Mono#deferContextual(Function)
* @see Mono#subscriberContext()
* @see Context
*/
public class ReactiveMongoContext {
@@ -50,14 +46,8 @@ public class ReactiveMongoContext {
*/
public static Mono<ClientSession> getSession() {
return Mono.deferContextual(ctx -> {
if (ctx.hasKey(SESSION_KEY)) {
return ctx.<Mono<ClientSession>> get(SESSION_KEY);
}
return Mono.empty();
});
return Mono.subscriberContext().filter(ctx -> ctx.hasKey(SESSION_KEY))
.flatMap(ctx -> ctx.<Mono<ClientSession>> get(SESSION_KEY));
}
/**

View File

@@ -29,7 +29,6 @@ import org.springframework.data.geo.GeoResult;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
@@ -40,7 +39,6 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
import org.springframework.transaction.reactive.TransactionalOperator;
import org.springframework.util.Assert;
@@ -66,7 +64,6 @@ import com.mongodb.reactivestreams.client.MongoCollection;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mathieu Ouellet
* @since 2.0
* @see Flux
* @see Mono
@@ -299,7 +296,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* @param collectionName name of the collection.
* @return an existing collection or one created on first server interaction.
*/
Mono<MongoCollection<Document>> getCollection(String collectionName);
MongoCollection<Document> getCollection(String collectionName);
/**
* Check to see if a collection with a name indicated by the entity class exists.
@@ -681,14 +678,11 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @return the converted object that was updated before it was updated.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
<T> Mono<T> findAndModify(Query query, UpdateDefinition update, Class<T> entityClass);
<T> Mono<T> findAndModify(Query query, Update update, Class<T> entityClass);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
@@ -696,15 +690,12 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @param collectionName the collection to query. Must not be {@literal null}.
* @return the converted object that was updated before it was updated.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
<T> Mono<T> findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName);
<T> Mono<T> findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
@@ -713,16 +704,13 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification.
* @param update the {@link UpdateDefinition} to apply on matching documents.
* @param update the {@link Update} to apply on matching documents.
* @param options the {@link FindAndModifyOptions} holding additional information.
* @param entityClass the parametrized type.
* @return the converted object that was updated. Depending on the value of {@link FindAndModifyOptions#isReturnNew()}
* this will either be the object as it was before the update or as it is after the update.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
<T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass);
<T> Mono<T> findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
/**
* Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify<a/>
@@ -731,17 +719,14 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
* fields specification. Must not be {@literal null}.
* @param update the {@link UpdateDefinition} to apply on matching documents. Must not be {@literal null}.
* @param update the {@link Update} to apply on matching documents. Must not be {@literal null}.
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
* @param entityClass the parametrized type. Must not be {@literal null}.
* @param collectionName the collection to query. Must not be {@literal null}.
* @return the converted object that was updated. Depending on the value of {@link FindAndModifyOptions#isReturnNew()}
* this will either be the object as it was before the update or as it is after the update.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
<T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
<T> Mono<T> findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass,
String collectionName);
/**
@@ -980,29 +965,6 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*/
Mono<Long> count(Query query, @Nullable Class<?> entityClass, String collectionName);
/**
* Estimate the number of documents, in the collection {@link #getCollectionName(Class) identified by the given type},
* based on collection statistics.
*
* @param entityClass must not be {@literal null}.
* @return a {@link Mono} emitting the estimated number of documents.
* @since 3.1
*/
default Mono<Long> estimatedCount(Class<?> entityClass) {
Assert.notNull(entityClass, "Entity class must not be null!");
return estimatedCount(getCollectionName(entityClass));
}
/**
* Estimate the number of documents in the given collection based on collection statistics.
*
* @param collectionName must not be {@literal null}.
* @return a {@link Mono} emitting the estimated number of documents.
* @since 3.1
*/
Mono<Long> estimatedCount(String collectionName);
/**
* Insert the object into the collection for the entity type of the object to save.
* <p/>
@@ -1187,111 +1149,99 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, Class)} instead.
* Use {@link #findAndModify(Query, Update, Class)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param entityClass class that determines the collection to use. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> upsert(Query query, UpdateDefinition update, Class<?> entityClass);
Mono<UpdateResult> upsert(Query query, Update update, Class<?> entityClass);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #upsert(Query, UpdateDefinition, Class, String)} to get full type specific
* support.
* domain type information. Use {@link #upsert(Query, Update, Class, String)} to get full type specific support.
* <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> upsert(Query query, UpdateDefinition update, String collectionName);
Mono<UpdateResult> upsert(Query query, Update update, String collectionName);
/**
* Performs an upsert. If no document is found that matches the query, a new document is created and inserted by
* combining the query document and the update document.
* combining the query document and the update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be upserted. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing object. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing
* object. Must not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> upsert(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
Mono<UpdateResult> upsert(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates the first object that is found in the collection of the entity class that matches the query document with
* the provided update document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, Class)} instead.
* Use {@link #findAndModify(Query, Update, Class)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class that determines the collection to use.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateFirst(Query query, UpdateDefinition update, Class<?> entityClass);
Mono<UpdateResult> updateFirst(Query query, Update update, Class<?> entityClass);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* the provided updated document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #updateFirst(Query, UpdateDefinition, Class, String)} to get full type specific
* support. <br />
* domain type information. Use {@link #updateFirst(Query, Update, Class, String)} to get full type specific support.
* <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, UpdateDefinition, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateFirst(Query query, UpdateDefinition update, String collectionName);
Mono<UpdateResult> updateFirst(Query query, Update update, String collectionName);
/**
* Updates the first object that is found in the specified collection that matches the query document criteria with
* the provided updated document. <br />
* <strong>NOTE:</strong> {@link Query#getSortObject() sorting} is not supported by {@code db.collection.updateOne}.
* Use {@link #findAndModify(Query, Update, Class, String)} instead.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateFirst(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
Mono<UpdateResult> updateFirst(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
@@ -1299,34 +1249,27 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateMulti(Query query, UpdateDefinition update, Class<?> entityClass);
Mono<UpdateResult> updateMulti(Query query, Update update, Class<?> entityClass);
/**
* Updates all objects that are found in the specified collection that matches the query document criteria with the
* provided updated document. <br />
* <strong>NOTE:</strong> Any additional support for field mapping, versions, etc. is not available due to the lack of
* domain type information. Use {@link #updateMulti(Query, UpdateDefinition, Class, String)} to get full type specific
* support.
* domain type information. Use {@link #updateMulti(Query, Update, Class, String)} to get full type specific support.
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateMulti(Query query, UpdateDefinition update, String collectionName);
Mono<UpdateResult> updateMulti(Query query, Update update, String collectionName);
/**
* Updates all objects that are found in the collection for the entity class that matches the query document criteria
@@ -1334,16 +1277,13 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations {
*
* @param query the query document that specifies the criteria used to select a record to be updated. Must not be
* {@literal null}.
* @param update the {@link UpdateDefinition} that contains the updated object or {@code $} operators to manipulate
* the existing. Must not be {@literal null}.
* @param update the update document that contains the updated object or $ operators to manipulate the existing. Must
* not be {@literal null}.
* @param entityClass class of the pojo to be operated on. Must not be {@literal null}.
* @param collectionName name of the collection to update the object in. Must not be {@literal null}.
* @return the {@link UpdateResult} which lets you access the results of the previous write.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
Mono<UpdateResult> updateMulti(Query query, UpdateDefinition update, Class<?> entityClass, String collectionName);
Mono<UpdateResult> updateMulti(Query query, Update update, Class<?> entityClass, String collectionName);
/**
* Remove the given object from the collection by id.

View File

@@ -18,7 +18,6 @@ package org.springframework.data.mongodb.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.client.result.DeleteResult;
@@ -107,18 +106,6 @@ public interface ReactiveRemoveOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingRemove<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingRemove}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingRemove<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
interface ReactiveRemove<T> extends RemoveWithCollection<T> {}

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -31,15 +35,12 @@ import com.mongodb.client.result.DeleteResult;
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveMongoTemplate tempate;
ReactiveRemoveOperationSupport(ReactiveMongoTemplate tempate) {
this.tempate = tempate;
}
private final @NonNull ReactiveMongoTemplate tempate;
/*
* (non-Javadoc)
@@ -53,20 +54,14 @@ class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation {
return new ReactiveRemoveSupport<>(tempate, domainType, ALL_QUERY, null);
}
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveRemoveSupport<T> implements ReactiveRemove<T>, RemoveWithCollection<T> {
private final ReactiveMongoTemplate template;
private final Class<T> domainType;
private final Query query;
private final String collection;
ReactiveRemoveSupport(ReactiveMongoTemplate template, Class<T> domainType, Query query, String collection) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.collection = collection;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<T> domainType;
Query query;
String collection;
/*
* (non-Javadoc)

View File

@@ -17,11 +17,8 @@ package org.springframework.data.mongodb.core;
import reactor.core.publisher.Mono;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import com.mongodb.client.result.UpdateResult;
@@ -121,16 +118,13 @@ public interface ReactiveUpdateOperation {
interface UpdateWithUpdate<T> {
/**
* Set the {@link UpdateDefinition} to be applied.
* Set the {@link org.springframework.data.mongodb.core.query.Update} to be applied.
*
* @param update must not be {@literal null}.
* @return new instance of {@link TerminatingUpdate}. Never {@literal null}.
* @throws IllegalArgumentException if update is {@literal null}.
* @since 3.0
* @see Update
* @see AggregationUpdate
*/
TerminatingUpdate<T> apply(UpdateDefinition update);
TerminatingUpdate<T> apply(org.springframework.data.mongodb.core.query.Update update);
/**
* Specify {@code replacement} object.
@@ -172,18 +166,6 @@ public interface ReactiveUpdateOperation {
* @throws IllegalArgumentException if query is {@literal null}.
*/
UpdateWithUpdate<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link UpdateWithUpdate}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default UpdateWithUpdate<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -15,10 +15,13 @@
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import reactor.core.publisher.Mono;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -32,15 +35,12 @@ import com.mongodb.client.result.UpdateResult;
* @author Christoph Strobl
* @since 2.0
*/
@RequiredArgsConstructor
class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
private static final Query ALL_QUERY = new Query();
private final ReactiveMongoTemplate template;
ReactiveUpdateOperationSupport(ReactiveMongoTemplate template) {
this.template = template;
}
private final @NonNull ReactiveMongoTemplate template;
/*
* (non-Javadoc)
@@ -54,41 +54,28 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
return new ReactiveUpdateSupport<>(template, domainType, ALL_QUERY, null, null, null, null, null, domainType);
}
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
static class ReactiveUpdateSupport<T>
implements ReactiveUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T>,
FindAndReplaceWithOptions<T>, FindAndReplaceWithProjection<T>, TerminatingFindAndReplace<T> {
private final ReactiveMongoTemplate template;
private final Class<?> domainType;
private final Query query;
private final org.springframework.data.mongodb.core.query.UpdateDefinition update;
@Nullable private final String collection;
@Nullable private final FindAndModifyOptions findAndModifyOptions;
@Nullable private final FindAndReplaceOptions findAndReplaceOptions;
@Nullable private final Object replacement;
private final Class<T> targetType;
ReactiveUpdateSupport(ReactiveMongoTemplate template, Class<?> domainType, Query query, UpdateDefinition update,
String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions,
Object replacement, Class<T> targetType) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.update = update;
this.collection = collection;
this.findAndModifyOptions = findAndModifyOptions;
this.findAndReplaceOptions = findAndReplaceOptions;
this.replacement = replacement;
this.targetType = targetType;
}
@NonNull ReactiveMongoTemplate template;
@NonNull Class<?> domainType;
Query query;
org.springframework.data.mongodb.core.query.Update update;
@Nullable String collection;
@Nullable FindAndModifyOptions findAndModifyOptions;
@Nullable FindAndReplaceOptions findAndReplaceOptions;
@Nullable Object replacement;
@NonNull Class<T> targetType;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithUpdate#apply(org.springframework.data.mongodb.core.query.UpdateDefinition)
* @see org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithUpdate#apply(org.springframework.data.mongodb.core.query.Update)
*/
@Override
public TerminatingUpdate<T> apply(org.springframework.data.mongodb.core.query.UpdateDefinition update) {
public TerminatingUpdate<T> apply(org.springframework.data.mongodb.core.query.Update update) {
Assert.notNull(update, "Update must not be null!");
@@ -136,9 +123,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
String collectionName = getCollectionName();
return template.findAndModify(query, update,
findAndModifyOptions != null ? findAndModifyOptions : FindAndModifyOptions.none(), targetType,
collectionName);
return template.findAndModify(query, update, findAndModifyOptions != null ? findAndModifyOptions : FindAndModifyOptions.none(), targetType, collectionName);
}
/*

View File

@@ -21,6 +21,7 @@ import org.springframework.data.mongodb.core.script.ExecutableMongoScript;
import org.springframework.data.mongodb.core.script.NamedMongoScript;
import org.springframework.lang.Nullable;
import com.mongodb.DB;
/**
* Script operations on {@link com.mongodb.DB} level. Allows interaction with server side JavaScript functions.

View File

@@ -15,7 +15,12 @@
*/
package org.springframework.data.mongodb.core;
import org.springframework.beans.factory.DisposableBean;
import com.mongodb.ClientSessionOptions;
import com.mongodb.ConnectionString;
import com.mongodb.DB;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
@@ -25,10 +30,8 @@ import com.mongodb.client.MongoDatabase;
*
* @author Christoph Strobl
* @since 2.1
* @deprecated since 3.0, use {@link SimpleMongoClientDatabaseFactory} instead.
*/
@Deprecated
public class SimpleMongoClientDbFactory extends SimpleMongoClientDatabaseFactory {
public class SimpleMongoClientDbFactory extends MongoDbFactorySupport<MongoClient> implements DisposableBean {
/**
* Creates a new {@link SimpleMongoClientDbFactory} instance for the given {@code connectionString}.
@@ -69,6 +72,45 @@ public class SimpleMongoClientDbFactory extends SimpleMongoClientDatabaseFactory
* @param mongoInstanceCreated
*/
private SimpleMongoClientDbFactory(MongoClient mongoClient, String databaseName, boolean mongoInstanceCreated) {
super(mongoClient, databaseName, mongoInstanceCreated);
super(mongoClient, databaseName, mongoInstanceCreated, new MongoExceptionTranslator());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getLegacyDb()
*/
@Override
public DB getLegacyDb() {
throw new UnsupportedOperationException(String.format(
"%s does not support legacy DBObject API! Please consider using SimpleMongoDbFactory for that purpose.",
MongoClient.class));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
*/
@Override
public ClientSession getSession(ClientSessionOptions options) {
return getMongoClient().startSession(options);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoDbFactoryBase#closeClient()
*/
@Override
protected void closeClient() {
getMongoClient().close();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoDbFactoryBase#doGetMongoDatabase(java.lang.String)
*/
@Override
protected MongoDatabase doGetMongoDatabase(String dbName) {
return getMongoClient().getDatabase(dbName);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 the original author or authors.
* Copyright 2011-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,63 +18,66 @@ package org.springframework.data.mongodb.core;
import org.springframework.beans.factory.DisposableBean;
import com.mongodb.ClientSessionOptions;
import com.mongodb.ConnectionString;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
/**
* Factory to create {@link MongoDatabase} instances from a {@link MongoClient} instance.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @since 3.0
* @author George Moraitis
* @author Mark Paluch
* @deprecated since 2.2 in favor of {@link SimpleMongoClientDbFactory}.
*/
public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySupport<MongoClient>
implements DisposableBean {
@Deprecated
public class SimpleMongoDbFactory extends MongoDbFactorySupport<MongoClient> implements DisposableBean {
/**
* Creates a new {@link SimpleMongoClientDatabaseFactory} instance for the given {@code connectionString}.
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClientURI}.
*
* @param connectionString connection coordinates for a database connection. Must contain a database name and must not
* be {@literal null} or empty.
* @see <a href="https://docs.mongodb.com/manual/reference/connection-string/">MongoDB Connection String reference</a>
* @param uri coordinates for a database connection. Must contain a database name and must not be {@literal null}.
* @since 1.7
*/
public SimpleMongoClientDatabaseFactory(String connectionString) {
this(new ConnectionString(connectionString));
public SimpleMongoDbFactory(MongoClientURI uri) {
this(new MongoClient(uri), uri.getDatabase(), true);
}
/**
* Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}.
*
* @param connectionString connection coordinates for a database connection. Must contain also a database name and not
* be {@literal null}.
*/
public SimpleMongoClientDatabaseFactory(ConnectionString connectionString) {
this(MongoClients.create(connectionString), connectionString.getDatabase(), true);
}
/**
* Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}.
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClient}.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @since 1.7
*/
public SimpleMongoClientDatabaseFactory(MongoClient mongoClient, String databaseName) {
public SimpleMongoDbFactory(MongoClient mongoClient, String databaseName) {
this(mongoClient, databaseName, false);
}
/**
* Creates a new {@link SimpleMongoClientDatabaseFactory} instance from the given {@link MongoClient}.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @param mongoClient
* @param databaseName
* @param mongoInstanceCreated
* @since 1.7
*/
SimpleMongoClientDatabaseFactory(MongoClient mongoClient, String databaseName, boolean mongoInstanceCreated) {
private SimpleMongoDbFactory(MongoClient mongoClient, String databaseName, boolean mongoInstanceCreated) {
super(mongoClient, databaseName, mongoInstanceCreated, new MongoExceptionTranslator());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getLegacyDb()
*/
@Override
public DB getLegacyDb() {
return getMongoClient().getDB(getDefaultDatabaseName());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
@@ -84,7 +87,7 @@ public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySuppor
return getMongoClient().startSession(options);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoDbFactoryBase#closeClient()
*/
@@ -93,7 +96,7 @@ public class SimpleMongoClientDatabaseFactory extends MongoDatabaseFactorySuppor
getMongoClient().close();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoDbFactoryBase#doGetMongoDatabase(java.lang.String)
*/

View File

@@ -15,9 +15,9 @@
*/
package org.springframework.data.mongodb.core;
import lombok.Value;
import reactor.core.publisher.Mono;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.dao.DataAccessException;
@@ -26,7 +26,6 @@ import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.ClientSessionOptions;
import com.mongodb.ConnectionString;
@@ -42,7 +41,6 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Mathieu Ouellet
* @since 2.0
*/
public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, ReactiveMongoDatabaseFactory {
@@ -101,7 +99,7 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDbFactory#getMongoDatabase()
*/
public Mono<MongoDatabase> getMongoDatabase() throws DataAccessException {
public MongoDatabase getMongoDatabase() throws DataAccessException {
return getMongoDatabase(databaseName);
}
@@ -109,16 +107,12 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDbFactory#getMongoDatabase(java.lang.String)
*/
public Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException {
public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
Assert.hasText(dbName, "Database name must not be empty.");
return Mono.fromSupplier(() -> {
MongoDatabase db = mongo.getDatabase(dbName);
return writeConcern != null ? db.withWriteConcern(writeConcern) : db;
});
MongoDatabase db = mongo.getDatabase(dbName);
return writeConcern != null ? db.withWriteConcern(writeConcern) : db;
}
/**
@@ -141,15 +135,6 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
return this.exceptionTranslator;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getCodecRegistry()
*/
@Override
public CodecRegistry getCodecRegistry() {
return this.mongo.getDatabase(databaseName).getCodecRegistry();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
@@ -175,24 +160,19 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* @author Christoph Strobl
* @since 2.1
*/
static final class ClientSessionBoundMongoDbFactory implements ReactiveMongoDatabaseFactory {
@Value
static class ClientSessionBoundMongoDbFactory implements ReactiveMongoDatabaseFactory {
private final ClientSession session;
private final ReactiveMongoDatabaseFactory delegate;
ClientSessionBoundMongoDbFactory(ClientSession session, ReactiveMongoDatabaseFactory delegate) {
this.session = session;
this.delegate = delegate;
}
ClientSession session;
ReactiveMongoDatabaseFactory delegate;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getMongoDatabase()
*/
@Override
public Mono<MongoDatabase> getMongoDatabase() throws DataAccessException {
return delegate.getMongoDatabase().map(this::decorateDatabase);
public MongoDatabase getMongoDatabase() throws DataAccessException {
return decorateDatabase(delegate.getMongoDatabase());
}
/*
@@ -200,8 +180,8 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getMongoDatabase(java.lang.String)
*/
@Override
public Mono<MongoDatabase> getMongoDatabase(String dbName) throws DataAccessException {
return delegate.getMongoDatabase(dbName).map(this::decorateDatabase);
public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
return decorateDatabase(delegate.getMongoDatabase(dbName));
}
/*
@@ -213,15 +193,6 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
return delegate.getExceptionTranslator();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getCodecRegistry()
*/
@Override
public CodecRegistry getCodecRegistry() {
return delegate.getCodecRegistry();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.ReactiveMongoDatabaseFactory#getSession(com.mongodb.ClientSessionOptions)
@@ -273,40 +244,5 @@ public class SimpleReactiveMongoDatabaseFactory implements DisposableBean, React
return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
}
public ClientSession getSession() {
return this.session;
}
public ReactiveMongoDatabaseFactory getDelegate() {
return this.delegate;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ClientSessionBoundMongoDbFactory that = (ClientSessionBoundMongoDbFactory) o;
if (!ObjectUtils.nullSafeEquals(this.session, that.session)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.delegate, that.delegate);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(this.session);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.delegate);
return result;
}
public String toString() {
return "SimpleReactiveMongoDatabaseFactory.ClientSessionBoundMongoDbFactory(session=" + this.getSession()
+ ", delegate=" + this.getDelegate() + ")";
}
}
}

View File

@@ -29,11 +29,8 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Support class for {@link AggregationExpression} implementations.
*
* @author Christoph Strobl
* @author Matt Morrissette
* @author Mark Paluch
* @since 1.10
*/
abstract class AbstractAggregationExpression implements AggregationExpression {
@@ -52,6 +49,7 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return toDocument(this.value, context);
}
@SuppressWarnings("unchecked")
public Document toDocument(Object value, AggregationOperationContext context) {
return new Document(getMongoMethod(), unpack(value, context));
}
@@ -103,19 +101,17 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return value;
}
@SuppressWarnings("unchecked")
protected List<Object> append(Object value, Expand expandList) {
if (this.value instanceof List) {
List<Object> clone = new ArrayList<>((List<Object>) this.value);
List<Object> clone = new ArrayList<Object>((List) this.value);
if (value instanceof Collection && Expand.EXPAND_VALUES.equals(expandList)) {
clone.addAll((Collection<?>) value);
} else {
clone.add(value);
}
return clone;
}
@@ -133,72 +129,25 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return append(value, Expand.EXPAND_VALUES);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Map<String, Object> append(String key, Object value) {
@SuppressWarnings("unchecked")
protected java.util.Map<String, Object> append(String key, Object value) {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
Map<String, Object> clone = new LinkedHashMap<>((java.util.Map) this.value);
java.util.Map<String, Object> clone = new LinkedHashMap<>((java.util.Map) this.value);
clone.put(key, value);
return clone;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Map<String, Object> remove(String key) {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
Map<String, Object> clone = new LinkedHashMap<>((java.util.Map) this.value);
clone.remove(key);
return clone;
}
/**
* Append the given key at the position in the underlying {@link LinkedHashMap}.
*
* @param index
* @param key
* @param value
* @return
* @since 3.1
*/
@SuppressWarnings({ "unchecked" })
protected Map<String, Object> appendAt(int index, String key, Object value) {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
Map<String, Object> clone = new LinkedHashMap<>();
int i = 0;
for (Map.Entry<String, Object> entry : ((Map<String, Object>) this.value).entrySet()) {
if (i == index) {
clone.put(key, value);
}
if (!entry.getKey().equals(key)) {
clone.put(entry.getKey(), entry.getValue());
}
i++;
}
if (i <= index) {
clone.put(key, value);
}
return clone;
}
@SuppressWarnings({ "rawtypes" })
protected List<Object> values() {
if (value instanceof List) {
return new ArrayList<Object>((List) value);
}
if (value instanceof java.util.Map) {
return new ArrayList<Object>(((java.util.Map) value).values());
}
return new ArrayList<>(Collections.singletonList(value));
}
@@ -228,7 +177,7 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
return (T) ((Map<String, Object>) this.value).get(key);
return (T) ((java.util.Map<String, Object>) this.value).get(key);
}
/**
@@ -238,11 +187,11 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
* @return
*/
@SuppressWarnings("unchecked")
protected Map<String, Object> argumentMap() {
protected java.util.Map<String, Object> argumentMap() {
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map!");
return Collections.unmodifiableMap((java.util.Map<String, Object>) value);
return Collections.unmodifiableMap((java.util.Map) value);
}
/**
@@ -259,7 +208,7 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
return false;
}
return ((Map<String, Object>) this.value).containsKey(key);
return ((java.util.Map<String, Object>) this.value).containsKey(key);
}
protected abstract String getMongoMethod();

View File

@@ -1,204 +0,0 @@
/*
* Copyright 2020 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
*
* https://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.aggregation;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation.AddFieldsOperationBuilder.ValueAppender;
import org.springframework.lang.Nullable;
/**
* Adds new fields to documents. {@code $addFields} outputs documents that contain all existing fields from the input
* documents and newly added fields.
*
* <pre class="code">
* AddFieldsOperation.addField("totalHomework").withValue("A+").and().addField("totalQuiz").withValue("B-")
* </pre>
*
* @author Christoph Strobl
* @since 3.0
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/">MongoDB Aggregation
* Framework: $addFields</a>
*/
public class AddFieldsOperation extends DocumentEnhancingOperation {
/**
* Create new instance of {@link AddFieldsOperation} adding map keys as exposed fields.
*
* @param source must not be {@literal null}.
*/
private AddFieldsOperation(Map<Object, Object> source) {
super(source);
}
/**
* Create new instance of {@link AddFieldsOperation}
*
* @param field must not be {@literal null}.
* @param value can be {@literal null}.
*/
public AddFieldsOperation(Object field, @Nullable Object value) {
this(Collections.singletonMap(field, value));
}
/**
* Define the {@link AddFieldsOperation} via {@link AddFieldsOperationBuilder}.
*
* @return new instance of {@link AddFieldsOperationBuilder}.
*/
public static AddFieldsOperationBuilder builder() {
return new AddFieldsOperationBuilder();
}
/**
* Concatenate another field to add.
*
* @param field must not be {@literal null}.
* @return new instance of {@link AddFieldsOperationBuilder}.
*/
public static ValueAppender addField(String field) {
return new AddFieldsOperationBuilder().addField(field);
}
/**
* Append the value for a specific field to the operation.
*
* @param field the target field to add.
* @param value the value to assign.
* @return new instance of {@link AddFieldsOperation}.
*/
public AddFieldsOperation addField(Object field, Object value) {
LinkedHashMap<Object, Object> target = new LinkedHashMap<>(getValueMap());
target.put(field, value);
return new AddFieldsOperation(target);
}
/**
* Concatenate additional fields to add.
*
* @return new instance of {@link AddFieldsOperationBuilder}.
*/
public AddFieldsOperationBuilder and() {
return new AddFieldsOperationBuilder(getValueMap());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.DocumentEnhancingOperation#mongoOperator()
*/
@Override
protected String mongoOperator() {
return "$addFields";
}
/**
* @author Christoph Strobl
* @since 3.0
*/
public static class AddFieldsOperationBuilder {
private final Map<Object, Object> valueMap;
private AddFieldsOperationBuilder() {
this.valueMap = new LinkedHashMap<>();
}
private AddFieldsOperationBuilder(Map<Object, Object> source) {
this.valueMap = new LinkedHashMap<>(source);
}
public AddFieldsOperationBuilder addFieldWithValue(String field, @Nullable Object value) {
return addField(field).withValue(value);
}
public AddFieldsOperationBuilder addFieldWithValueOf(String field, Object value) {
return addField(field).withValueOf(value);
}
/**
* Define the field to add.
*
* @param field must not be {@literal null}.
* @return new instance of {@link ValueAppender}.
*/
public ValueAppender addField(String field) {
return new ValueAppender() {
@Override
public AddFieldsOperationBuilder withValue(Object value) {
valueMap.put(field, value);
return AddFieldsOperationBuilder.this;
}
@Override
public AddFieldsOperationBuilder withValueOf(Object value) {
valueMap.put(field, value instanceof String ? Fields.fields((String) value) : value);
return AddFieldsOperationBuilder.this;
}
@Override
public AddFieldsOperationBuilder withValueOfExpression(String operation, Object... values) {
valueMap.put(field, new ExpressionProjection(operation, values));
return AddFieldsOperationBuilder.this;
}
};
}
public AddFieldsOperation build() {
return new AddFieldsOperation(valueMap);
}
/**
* @author Christoph Strobl
* @since 3.0
*/
public interface ValueAppender {
/**
* Define the value to assign as is.
*
* @param value can be {@literal null}.
* @return new instance of {@link AddFieldsOperation}.
*/
AddFieldsOperationBuilder withValue(@Nullable Object value);
/**
* Define the value to assign. Plain {@link String} values are treated as {@link Field field references}.
*
* @param value must not be {@literal null}.
* @return new instance of {@link AddFieldsOperation}.
*/
AddFieldsOperationBuilder withValueOf(Object value);
/**
* Adds a generic projection for the current field.
*
* @param operation the operation key, e.g. {@code $add}.
* @param values the values to be set for the projection operation.
* @return new instance of {@link AddFieldsOperation}.
*/
AddFieldsOperationBuilder withValueOfExpression(String operation, Object... values);
}
}
}

View File

@@ -23,11 +23,9 @@ import java.util.List;
import org.bson.Document;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation.AddFieldsOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.CountOperation.CountOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.FacetOperation.FacetOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.GraphLookupOperation.StartWithBuilder;
import org.springframework.data.mongodb.core.aggregation.MergeOperation.MergeOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootDocumentOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootOperationBuilder;
import org.springframework.data.mongodb.core.query.Criteria;
@@ -96,7 +94,7 @@ public class Aggregation {
public static final AggregationOperationContext DEFAULT_CONTEXT = AggregationOperationRenderer.DEFAULT_CONTEXT;
public static final AggregationOptions DEFAULT_OPTIONS = newAggregationOptions().build();
protected final AggregationPipeline pipeline;
protected final List<AggregationOperation> operations;
private final AggregationOptions options;
/**
@@ -117,17 +115,6 @@ public class Aggregation {
return new Aggregation(operations);
}
/**
* Creates a new {@link AggregationUpdate} from the given {@link AggregationOperation}s.
*
* @param operations can be {@literal empty} but must not be {@literal null}.
* @return new instance of {@link AggregationUpdate}.
* @since 3.0
*/
public static AggregationUpdate newUpdate(AggregationOperation... operations) {
return AggregationUpdate.from(Arrays.asList(operations));
}
/**
* Returns a copy of this {@link Aggregation} with the given {@link AggregationOptions} set. Note that options are
* supported in MongoDB version 2.6+.
@@ -139,7 +126,7 @@ public class Aggregation {
public Aggregation withOptions(AggregationOptions options) {
Assert.notNull(options, "AggregationOptions must not be null.");
return new Aggregation(this.pipeline.getOperations(), options);
return new Aggregation(this.operations, options);
}
/**
@@ -194,18 +181,30 @@ public class Aggregation {
/**
* Creates a new {@link Aggregation} from the given {@link AggregationOperation}s.
*
* @param aggregationOperations must not be {@literal null}.
* @param aggregationOperations must not be {@literal null} or empty.
* @param options must not be {@literal null} or empty.
*/
protected Aggregation(List<AggregationOperation> aggregationOperations, AggregationOptions options) {
Assert.notNull(aggregationOperations, "AggregationOperations must not be null!");
Assert.isTrue(!aggregationOperations.isEmpty(), "At least one AggregationOperation has to be provided");
Assert.notNull(options, "AggregationOptions must not be null!");
this.pipeline = new AggregationPipeline(aggregationOperations);
// check $out is the last operation if it exists
for (AggregationOperation aggregationOperation : aggregationOperations) {
if (aggregationOperation instanceof OutOperation && !isLast(aggregationOperation, aggregationOperations)) {
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
}
}
this.operations = aggregationOperations;
this.options = options;
}
private boolean isLast(AggregationOperation aggregationOperation, List<AggregationOperation> aggregationOperations) {
return aggregationOperations.indexOf(aggregationOperation) == aggregationOperations.size() - 1;
}
/**
* Get the {@link AggregationOptions}.
*
@@ -225,20 +224,6 @@ public class Aggregation {
return "_id";
}
/**
* Obtain an {@link AddFieldsOperationBuilder builder} instance to create a new {@link AddFieldsOperation}.
* <p/>
* Starting in version 4.2, MongoDB adds a new aggregation pipeline stage {@link AggregationUpdate#set $set} that is
* an alias for {@code $addFields}.
*
* @return new instance of {@link AddFieldsOperationBuilder}.
* @see AddFieldsOperation
* @since 3.0
*/
public static AddFieldsOperationBuilder addFields() {
return AddFieldsOperation.builder();
}
/**
* Creates a new {@link ProjectionOperation} including the given fields.
*
@@ -499,30 +484,6 @@ public class Aggregation {
return new MatchOperation(criteria);
}
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
* {@code distanceField} defines output field that contains the calculated distance.
*
* @param query must not be {@literal null}.
* @param distanceField must not be {@literal null} or empty.
* @return new instance of {@link GeoNearOperation}.
* @since 1.7
*/
public static GeoNearOperation geoNear(NearQuery query, String distanceField) {
return new GeoNearOperation(query, distanceField);
}
/**
* Obtain a {@link MergeOperationBuilder builder} instance to create a new {@link MergeOperation}.
*
* @return new instance of {@link MergeOperationBuilder}.
* @see MergeOperation
* @since 3.0
*/
public static MergeOperationBuilder merge() {
return MergeOperation.builder();
}
/**
* Creates a new {@link OutOperation} using the given collection name. This operation must be the last operation in
* the pipeline.
@@ -642,26 +603,6 @@ public class Aggregation {
return new CountOperationBuilder();
}
/**
* Creates a new {@link RedactOperation} that can restrict the content of a document based on information stored
* within the document itself.
*
* <pre class="code">
*
* Aggregation.redact(ConditionalOperators.when(Criteria.where("level").is(5)) //
* .then(RedactOperation.PRUNE) //
* .otherwise(RedactOperation.DESCEND));
* </pre>
*
* @param condition Any {@link AggregationExpression} that resolves to {@literal $$DESCEND}, {@literal $$PRUNE}, or
* {@literal $$KEEP}. Must not be {@literal null}.
* @return new instance of {@link RedactOperation}. Never {@literal null}.
* @since 3.0
*/
public static RedactOperation redact(AggregationExpression condition) {
return new RedactOperation(condition);
}
/**
* Creates a new {@link Fields} instance for the given field names.
*
@@ -684,6 +625,19 @@ public class Aggregation {
return Fields.from(field(name, target));
}
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
* {@code distanceField} defines output field that contains the calculated distance.
*
* @param query must not be {@literal null}.
* @param distanceField must not be {@literal null} or empty.
* @return
* @since 1.7
*/
public static GeoNearOperation geoNear(NearQuery query, String distanceField) {
return new GeoNearOperation(query, distanceField);
}
/**
* Returns a new {@link AggregationOptions.Builder}.
*
@@ -702,15 +656,7 @@ public class Aggregation {
* @since 2.1
*/
public List<Document> toPipeline(AggregationOperationContext rootContext) {
return pipeline.toDocuments(rootContext);
}
/**
* @return the {@link AggregationPipeline}.
* @since 3.0.2
*/
public AggregationPipeline getPipeline() {
return pipeline;
return AggregationOperationRenderer.toDocument(operations, rootContext);
}
/**

View File

@@ -54,15 +54,4 @@ public interface AggregationOperation {
default List<Document> toPipelineStages(AggregationOperationContext context) {
return Collections.singletonList(toDocument(context));
}
/**
* Return the MongoDB operator that is used for this {@link AggregationOperation}. Aggregation operations should
* implement this method to avoid document rendering.
*
* @return the operator used for this {@link AggregationOperation}.
* @since 3.0.2
*/
default String getOperator() {
return toDocument(Aggregation.DEFAULT_CONTEXT).keySet().iterator().next();
}
}

View File

@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import org.bson.Document;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.lang.Nullable;
@@ -102,16 +103,4 @@ public interface AggregationOperationContext {
.map(PropertyDescriptor::getName) //
.toArray(String[]::new));
}
/**
* This toggle allows the {@link AggregationOperationContext context} to use any given field name without checking for
* its existence. Typically the {@link AggregationOperationContext} fails when referencing unknown fields, those that
* are not present in one of the previous stages or the input source, throughout the pipeline.
*
* @return a more relaxed {@link AggregationOperationContext}.
* @since 3.0
*/
default AggregationOperationContext continueOnMissingFieldReference() {
return this;
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.core.aggregation;
import java.time.Duration;
import java.util.Optional;
import org.bson.Document;
@@ -23,6 +22,8 @@ import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.mongodb.DBObject;
/**
* Holds a set of configurable aggregation options that can be used within an aggregation pipeline. A list of support
* aggregation options can be found in the MongoDB reference documentation
@@ -32,7 +33,6 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @author Yadhukrishna S Pai
* @see Aggregation#withOptions(AggregationOptions)
* @see TypedAggregation#withOptions(AggregationOptions)
* @since 1.6
@@ -45,17 +45,12 @@ public class AggregationOptions {
private static final String ALLOW_DISK_USE = "allowDiskUse";
private static final String COLLATION = "collation";
private static final String COMMENT = "comment";
private static final String MAX_TIME = "maxTimeMS";
private static final String HINT = "hint";
private final boolean allowDiskUse;
private final boolean explain;
private final Optional<Document> cursor;
private final Optional<Collation> collation;
private final Optional<String> comment;
private final Optional<Document> hint;
private Duration maxTime = Duration.ZERO;
private ResultOptions resultOptions = ResultOptions.READ;
/**
* Creates a new {@link AggregationOptions}.
@@ -80,7 +75,7 @@ public class AggregationOptions {
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation) {
this(allowDiskUse, explain, cursor, collation, null, null);
this(allowDiskUse, explain, cursor, collation, null);
}
/**
@@ -96,30 +91,12 @@ public class AggregationOptions {
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation, @Nullable String comment) {
this(allowDiskUse, explain, cursor, collation, comment, null);
}
/**
* Creates a new {@link AggregationOptions}.
*
* @param allowDiskUse whether to off-load intensive sort-operations to disk.
* @param explain whether to get the execution plan for the aggregation instead of the actual results.
* @param cursor can be {@literal null}, used to pass additional options (such as {@code batchSize}) to the
* aggregation.
* @param collation collation for string comparison. Can be {@literal null}.
* @param comment execution comment. Can be {@literal null}.
* @param hint can be {@literal null}, used to provide an index that would be forcibly used by query optimizer.
* @since 3.1
*/
private AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation, @Nullable String comment, @Nullable Document hint) {
this.allowDiskUse = allowDiskUse;
this.explain = explain;
this.cursor = Optional.ofNullable(cursor);
this.collation = Optional.ofNullable(collation);
this.comment = Optional.ofNullable(comment);
this.hint = Optional.ofNullable(hint);
}
/**
@@ -135,7 +112,7 @@ public class AggregationOptions {
}
/**
* Creates new {@link AggregationOptions} given {@link Document} containing aggregation options.
* Creates new {@link AggregationOptions} given {@link DBObject} containing aggregation options.
*
* @param document must not be {@literal null}.
* @return the {@link AggregationOptions}.
@@ -151,13 +128,8 @@ public class AggregationOptions {
Collation collation = document.containsKey(COLLATION) ? Collation.from(document.get(COLLATION, Document.class))
: null;
String comment = document.getString(COMMENT);
Document hint = document.get(HINT, Document.class);
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment, hint);
if (document.containsKey(MAX_TIME)) {
options.maxTime = Duration.ofMillis(document.getLong(MAX_TIME));
}
return options;
return new AggregationOptions(allowDiskUse, explain, cursor, collation, comment);
}
/**
@@ -234,33 +206,6 @@ public class AggregationOptions {
return comment;
}
/**
* Get the hint used to to fulfill the aggregation.
*
* @return never {@literal null}.
* @since 3.1
*/
public Optional<Document> getHint() {
return hint;
}
/**
* @return the time limit for processing. {@link Duration#ZERO} is used for the default unbounded behavior.
* @since 3.0
*/
public Duration getMaxTime() {
return maxTime;
}
/**
* @return {@literal true} to skip results when running an aggregation. Useful in combination with {@code $merge} or
* {@code $out}.
* @since 3.0.2
*/
public boolean isSkipResults() {
return ResultOptions.SKIP.equals(resultOptions);
}
/**
* Returns a new potentially adjusted copy for the given {@code aggregationCommandObject} with the configuration
* applied.
@@ -280,10 +225,6 @@ public class AggregationOptions {
result.put(EXPLAIN, explain);
}
if (result.containsKey(HINT)) {
hint.ifPresent(val -> result.append(HINT, val));
}
if (!result.containsKey(CURSOR)) {
cursor.ifPresent(val -> result.put(CURSOR, val));
}
@@ -292,10 +233,6 @@ public class AggregationOptions {
collation.map(Collation::toDocument).ifPresent(val -> result.append(COLLATION, val));
}
if (hasExecutionTimeLimit() && !result.containsKey(MAX_TIME)) {
result.append(MAX_TIME, maxTime.toMillis());
}
return result;
}
@@ -313,23 +250,10 @@ public class AggregationOptions {
cursor.ifPresent(val -> document.put(CURSOR, val));
collation.ifPresent(val -> document.append(COLLATION, val.toDocument()));
comment.ifPresent(val -> document.append(COMMENT, val));
hint.ifPresent(val -> document.append(HINT, val));
if (hasExecutionTimeLimit()) {
document.append(MAX_TIME, maxTime.toMillis());
}
return document;
}
/**
* @return {@literal true} if {@link #maxTime} is set to a positive value.
* @since 3.0
*/
public boolean hasExecutionTimeLimit() {
return !maxTime.isZero() && !maxTime.isNegative();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@@ -355,9 +279,6 @@ public class AggregationOptions {
private @Nullable Document cursor;
private @Nullable Collation collation;
private @Nullable String comment;
private @Nullable Document hint;
private @Nullable Duration maxTime;
private @Nullable ResultOptions resultOptions;
/**
* Defines whether to off-load intensive sort-operations to disk.
@@ -434,78 +355,13 @@ public class AggregationOptions {
return this;
}
/**
* Define a hint that is used by query optimizer to to fulfill the aggregation.
*
* @param hint can be {@literal null}.
* @return this.
* @since 3.1
*/
public Builder hint(@Nullable Document hint) {
this.hint = hint;
return this;
}
/**
* Set the time limit for processing.
*
* @param maxTime {@link Duration#ZERO} is used for the default unbounded behavior. {@link Duration#isNegative()
* Negative} values will be ignored.
* @return this.
* @since 3.0
*/
public Builder maxTime(@Nullable Duration maxTime) {
this.maxTime = maxTime;
return this;
}
/**
* Run the aggregation, but do NOT read the aggregation result from the store. <br />
* If the expected result of the aggregation is rather large, eg. when using an {@literal $out} operation, this
* option allows to execute the aggregation without having the cursor return the operation result.
*
* @return this.
* @since 3.0.2
*/
public Builder skipOutput() {
this.resultOptions = ResultOptions.SKIP;
return this;
}
/**
* Returns a new {@link AggregationOptions} instance with the given configuration.
*
* @return new instance of {@link AggregationOptions}.
*/
public AggregationOptions build() {
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment, hint);
if (maxTime != null) {
options.maxTime = maxTime;
}
if (resultOptions != null) {
options.resultOptions = resultOptions;
}
return options;
return new AggregationOptions(allowDiskUse, explain, cursor, collation, comment);
}
}
/**
* @since 3.0
*/
private enum ResultOptions {
/**
* Just do it!, and do not read the operation result.
*/
SKIP,
/**
* Read the aggregation result from the cursor.
*/
READ;
}
}

View File

@@ -1,162 +0,0 @@
/*
* Copyright 2020 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
*
* https://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.aggregation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import org.bson.Document;
import org.springframework.util.Assert;
/**
* The {@link AggregationPipeline} holds the collection of {@link AggregationOperation aggregation stages}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0.2
*/
public class AggregationPipeline {
private final List<AggregationOperation> pipeline;
/**
* Create an empty pipeline
*/
public AggregationPipeline() {
this(new ArrayList<>());
}
/**
* Create a new pipeline with given {@link AggregationOperation stages}.
*
* @param aggregationOperations must not be {@literal null}.
*/
public AggregationPipeline(List<AggregationOperation> aggregationOperations) {
Assert.notNull(aggregationOperations, "AggregationOperations must not be null!");
pipeline = new ArrayList<>(aggregationOperations);
}
/**
* Append the given {@link AggregationOperation stage} to the pipeline.
*
* @param aggregationOperation must not be {@literal null}.
* @return this.
*/
public AggregationPipeline add(AggregationOperation aggregationOperation) {
Assert.notNull(aggregationOperation, "AggregationOperation must not be null!");
pipeline.add(aggregationOperation);
return this;
}
/**
* Get the list of {@link AggregationOperation aggregation stages}.
*
* @return never {@literal null}.
*/
public List<AggregationOperation> getOperations() {
return Collections.unmodifiableList(pipeline);
}
List<Document> toDocuments(AggregationOperationContext context) {
verify();
return AggregationOperationRenderer.toDocument(pipeline, context);
}
/**
* @return {@literal true} if the last aggregation stage is either {@literal $out} or {@literal $merge}.
*/
public boolean isOutOrMerge() {
if (isEmpty()) {
return false;
}
AggregationOperation operation = pipeline.get(pipeline.size() - 1);
return isOut(operation) || isMerge(operation);
}
void verify() {
// check $out/$merge is the last operation if it exists
for (AggregationOperation operation : pipeline) {
if (isOut(operation) && !isLast(operation)) {
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
}
if (isMerge(operation) && !isLast(operation)) {
throw new IllegalArgumentException("The $merge operator must be the last stage in the pipeline.");
}
}
}
/**
* Return whether this aggregation pipeline defines a {@code $unionWith} stage that may contribute documents from
* other collections. Checking for presence of union stages is useful when attempting to determine the aggregation
* element type for mapping metadata computation.
*
* @return {@literal true} the aggregation pipeline makes use of {@code $unionWith}.
* @since 3.1
*/
public boolean containsUnionWith() {
return containsOperation(AggregationPipeline::isUnionWith);
}
/**
* @return {@literal true} if the pipeline does not contain any stages.
* @since 3.1
*/
public boolean isEmpty() {
return pipeline.isEmpty();
}
private boolean containsOperation(Predicate<AggregationOperation> predicate) {
if (isEmpty()) {
return false;
}
for (AggregationOperation element : pipeline) {
if (predicate.test(element)) {
return true;
}
}
return false;
}
private boolean isLast(AggregationOperation aggregationOperation) {
return pipeline.indexOf(aggregationOperation) == pipeline.size() - 1;
}
private static boolean isUnionWith(AggregationOperation operator) {
return operator instanceof UnionWithOperation || operator.getOperator().equals("$unionWith");
}
private static boolean isMerge(AggregationOperation operator) {
return operator instanceof MergeOperation || operator.getOperator().equals("$merge");
}
private static boolean isOut(AggregationOperation operator) {
return operator instanceof OutOperation || operator.getOperator().equals("$out");
}
}

View File

@@ -1,325 +0,0 @@
/*
* Copyright 2019-2020 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
*
* https://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.aggregation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Abstraction for {@code db.collection.update()} using an aggregation pipeline. Aggregation pipeline updates use a more
* expressive update statement expressing conditional updates based on current field values or updating one field using
* the value of another field(s).
*
* <pre class="code">
* AggregationUpdate update = AggregationUpdate.update().set("average")
* .toValue(ArithmeticOperators.valueOf("tests").avg()).set("grade")
* .toValue(ConditionalOperators
* .switchCases(CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(90)).then("A"),
* CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(80)).then("B"),
* CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(70)).then("C"),
* CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(60)).then("D"))
* .defaultTo("F"));
* </pre>
*
* The above sample is equivalent to the JSON update statement:
*
* <pre class="code">
* db.collection.update(
* { },
* [
* { $set: { average : { $avg: "$tests" } } },
* { $set: { grade: { $switch: {
* branches: [
* { case: { $gte: [ "$average", 90 ] }, then: "A" },
* { case: { $gte: [ "$average", 80 ] }, then: "B" },
* { case: { $gte: [ "$average", 70 ] }, then: "C" },
* { case: { $gte: [ "$average", 60 ] }, then: "D" }
* ],
* default: "F"
* } } } }
* ],
* { multi: true }
* )
* </pre>
*
* @author Christoph Strobl
* @author Mark Paluch
* @see <a href=
* "https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-aggregation-pipeline">MongoDB
* Reference Documentation</a>
* @since 3.0
*/
public class AggregationUpdate extends Aggregation implements UpdateDefinition {
private boolean isolated = false;
private final Set<String> keysTouched = new HashSet<>();
/**
* Create new {@link AggregationUpdate}.
*/
protected AggregationUpdate() {
this(new ArrayList<>());
}
/**
* Create new {@link AggregationUpdate} with the given aggregation pipeline to apply.
*
* @param pipeline must not be {@literal null}.
*/
protected AggregationUpdate(List<AggregationOperation> pipeline) {
super(pipeline);
for (AggregationOperation operation : pipeline) {
if (operation instanceof FieldsExposingAggregationOperation) {
((FieldsExposingAggregationOperation) operation).getFields().forEach(it -> {
keysTouched.add(it.getName());
});
}
}
}
/**
* Start defining the update pipeline to execute.
*
* @return new instance of {@link AggregationUpdate}.
*/
public static AggregationUpdate update() {
return new AggregationUpdate();
}
/**
* Create a new AggregationUpdate from the given {@link AggregationOperation}s.
*
* @return new instance of {@link AggregationUpdate}.
*/
public static AggregationUpdate from(List<AggregationOperation> pipeline) {
return new AggregationUpdate(pipeline);
}
/**
* Adds new fields to documents. {@code $set} outputs documents that contain all existing fields from the input
* documents and newly added fields.
*
* @param setOperation must not be {@literal null}.
* @return this.
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/set/">$set Aggregation Reference</a>
*/
public AggregationUpdate set(SetOperation setOperation) {
Assert.notNull(setOperation, "SetOperation must not be null!");
setOperation.getFields().forEach(it -> {
keysTouched.add(it.getName());
});
pipeline.add(setOperation);
return this;
}
/**
* {@code $unset} removes/excludes fields from documents.
*
* @param unsetOperation must not be {@literal null}.
* @return this.
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/unset/">$unset Aggregation
* Reference</a>
*/
public AggregationUpdate unset(UnsetOperation unsetOperation) {
Assert.notNull(unsetOperation, "UnsetOperation must not be null!");
pipeline.add(unsetOperation);
keysTouched.addAll(unsetOperation.removedFieldNames());
return this;
}
/**
* {@code $replaceWith} replaces the input document with the specified document. The operation replaces all existing
* fields in the input document, including the <strong>_id</strong> field.
*
* @param replaceWithOperation must not be {@literal null}.
* @return this.
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/">$replaceWith Aggregation
* Reference</a>
*/
public AggregationUpdate replaceWith(ReplaceWithOperation replaceWithOperation) {
Assert.notNull(replaceWithOperation, "ReplaceWithOperation must not be null!");
pipeline.add(replaceWithOperation);
return this;
}
/**
* {@code $replaceWith} replaces the input document with the value.
*
* @param value must not be {@literal null}.
* @return this.
*/
public AggregationUpdate replaceWith(Object value) {
Assert.notNull(value, "Value must not be null!");
return replaceWith(ReplaceWithOperation.replaceWithValue(value));
}
/**
* Fluent API variant for {@code $set} adding a single {@link SetOperation pipeline operation} every time. To update
* multiple fields within one {@link SetOperation} use {@link #set(SetOperation)}.
*
* @param key must not be {@literal null}.
* @return new instance of {@link SetValueAppender}.
* @see #set(SetOperation)
*/
public SetValueAppender set(String key) {
Assert.notNull(key, "Key must not be null!");
return new SetValueAppender() {
@Override
public AggregationUpdate toValue(@Nullable Object value) {
return set(SetOperation.builder().set(key).toValue(value));
}
@Override
public AggregationUpdate toValueOf(Object value) {
Assert.notNull(value, "Value must not be null!");
return set(SetOperation.builder().set(key).toValueOf(value));
}
};
}
/**
* Short for {@link #unset(UnsetOperation)}.
*
* @param keys the fields to remove.
* @return this.
*/
public AggregationUpdate unset(String... keys) {
Assert.notNull(keys, "Keys must not be null!");
Assert.noNullElements(keys, "Keys must not contain null elements.");
return unset(new UnsetOperation(Arrays.stream(keys).map(Fields::field).collect(Collectors.toList())));
}
/**
* Prevents a write operation that affects <strong>multiple</strong> documents from yielding to other reads or writes
* once the first document is written. <br />
* Use with {@link org.springframework.data.mongodb.core.MongoOperations#updateMulti(Query, UpdateDefinition, Class)}.
*
* @return never {@literal null}.
*/
public AggregationUpdate isolated() {
isolated = true;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#isIsolated()
*/
@Override
public Boolean isIsolated() {
return isolated;
}
/*
* Returns a update document containing the update pipeline.
* The resulting document needs to be unwrapped to be used with update operations.
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#getUpdateObject()
*/
@Override
public Document getUpdateObject() {
return new Document("", toPipeline(Aggregation.DEFAULT_CONTEXT));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#modifies(java.lang.String)
*/
@Override
public boolean modifies(String key) {
return keysTouched.contains(key);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#inc(java.lang.String)
*/
@Override
public void inc(String key) {
set(new SetOperation(key, ArithmeticOperators.valueOf(key).add(1)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#getArrayFilters()
*/
@Override
public List<ArrayFilter> getArrayFilters() {
return Collections.emptyList();
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(",\n", "[\n", "\n]");
toPipeline(Aggregation.DEFAULT_CONTEXT).stream().map(SerializationUtils::serializeToJsonSafely)
.forEach(joiner::add);
return joiner.toString();
}
/**
* Fluent API AggregationUpdate builder.
*
* @author Christoph Strobl
*/
public interface SetValueAppender {
/**
* Define the target value as is.
*
* @param value can be {@literal null}.
* @return never {@literal null}.
*/
AggregationUpdate toValue(@Nullable Object value);
/**
* Define the target value as value, an {@link AggregationExpression} or a {@link Field} reference.
*
* @param value can be {@literal null}.
* @return never {@literal null}.
*/
AggregationUpdate toValueOf(Object value);
}
}

View File

@@ -511,27 +511,6 @@ public class ArithmeticOperators {
: AccumulatorOperators.StdDevSamp.stdDevSampOf(expression);
}
/**
* Creates new {@link AggregationExpression} that rounds a number to a whole integer or to a specified decimal
* place.
*
* @return new instance of {@link Round}.
* @since 3.0
*/
public Round round() {
return usesFieldRef() ? Round.roundValueOf(fieldReference) : Round.roundValueOf(expression);
}
/**
* Creates new {@link AggregationExpression} that rounds a number to a specified decimal place.
*
* @return new instance of {@link Round}.
* @since 3.0
*/
public Round roundToPlace(int place) {
return round().place(place);
}
private boolean usesFieldRef() {
return fieldReference != null;
}
@@ -1569,100 +1548,4 @@ public class ArithmeticOperators {
return new Trunc(value);
}
}
/**
* {@link Round} rounds a number to a whole integer or to a specified decimal place.
* <ul>
* <li>If {@link Round#place(int)} resolves to a positive integer, {@code $round} rounds to the given decimal
* places.</li>
* <li>If {@link Round#place(int)} resolves to a negative integer, {@code $round} rounds to the left of the
* decimal.</li>
* <li>If {@link Round#place(int)} resolves to a zero, {@code $round} rounds using the first digit to the right of the
* decimal.</li>
* </ul>
*
* @since 3.0
*/
public static class Round extends AbstractAggregationExpression {
private Round(Object value) {
super(value);
}
/**
* Round the value of the field that resolves to an integer, double, decimal, or long.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public static Round roundValueOf(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Round(Collections.singletonList(Fields.field(fieldReference)));
}
/**
* Round the outcome of the given expression hat resolves to an integer, double, decimal, or long.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public static Round roundValueOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Round(Collections.singletonList(expression));
}
/**
* Round the given numeric (integer, double, decimal, or long) value.
*
* @param value must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public static Round round(Number value) {
Assert.notNull(value, "Value must not be null!");
return new Round(Collections.singletonList(value));
}
/**
* The place to round to. Can be between -20 and 100, exclusive.
*
* @param place value between -20 and 100, exclusive.
* @return new instance of {@link Round}.
*/
public Round place(int place) {
return new Round(append(place));
}
/**
* The place to round to defined by an expression that resolves to an integer between -20 and 100, exclusive.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public Round placeOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Round(append(expression));
}
/**
* The place to round to defined by via a field reference that resolves to an integer between -20 and 100,
* exclusive.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Round}.
*/
public Round placeOf(String fieldReference) {
Assert.notNull(fieldReference, "fieldReference must not be null!");
return new Round(append(Fields.field(fieldReference)));
}
@Override
protected String getMongoMethod() {
return "$round";
}
}
}

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