Skip to content

Commit

Permalink
spring-projects#361 - Add example for MongoDB 4.0 transactions.
Browse files Browse the repository at this point in the history
  • Loading branch information
christophstrobl authored and mp911de committed May 17, 2018
1 parent 9059adc commit 9025621
Show file tree
Hide file tree
Showing 14 changed files with 1,010 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ We have separate folders for the samples of individual modules:
* `reactive` - Example project to show reactive template and repository support.
* `security` - Example project showing usage of Spring Security with MongoDB.
* `text-search` - Example project showing usage of MongoDB text search feature.
* `transactions` - Example project for synchronous and reactive MongoDB 4.0 transaction support.

## Spring Data REST

Expand Down
1 change: 1 addition & 0 deletions mongodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<module>reactive</module>
<module>security</module>
<module>text-search</module>
<module>transactions</module>
</modules>

<dependencies>
Expand Down
79 changes: 79 additions & 0 deletions mongodb/transactions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Spring Data MongoDB - Transaction Sample

This project contains samples for upcoming MongoDB 4.0 transactions.

## Running the Sample

The sample uses multiple embedded MongoDB processes in a [MongoDB replica set](https://docs.mongodb.com/manual/replication/).
It contains test for both the synchronous and the reactive transaction support in the `sync` / `reactive` packages.

You may run the examples directly from your IDE or use maven on the command line.

**INFO:** The operations to download the required MongoDB binaries and spin up the cluster can take some time. Please
be patient.

## Sync Transactions

`MongoTransactionManager` is the gateway to the well known Spring transaction support. It lets applications use
[the managed transaction features of Spring](http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/transaction.html).
The `MongoTransactionManager` binds a `ClientSession` to the thread. `MongoTemplate` detects the session and operates
on these resources which are associated with the transaction accordingly. `MongoTemplate` can also participate in
other, ongoing transactions.

```java
@Configuration
static class Config extends AbstractMongoConfiguration {

@Bean
MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}

// ...
}

@Component
public class TransitionService {

@Transactional
public void run(Integer id) {

Process process = lookup(id);

if (!State.CREATED.equals(process.getState())) {
return;
}

start(process);
verify(process);
finish(process);
}
}
```

## Reactive transactions

`ReactiveMongoTemplate` offers dedicated methods for operating within a transaction without having to worry about the
commit/abort actions depending on the operations outcome. There's currently no session or transaction integration
with reactive repositories - we apologize for that!

**NOTE:** Please note that you cannot preform meta operations, like collection creation within a transaction.

```java
@Component
public class RactiveTransitionService {

public Mono<Integer> run(Integer id) {

return template.inTransaction().execute(action -> {

return lookup(id) //
.filter(State.CREATED::equals)
.flatMap(process -> start(action, process))
.flatMap(this::verify)
.flatMap(process -> finish(action, process));

}).next().map(Process::getId);
}
}
```
72 changes: 72 additions & 0 deletions mongodb/transactions/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-mongodb-examples</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>

<artifactId>spring-data-mongodb-transactions</artifactId>
<name>Spring Data MongoDB - Transactions</name>

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
<exclusions>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.1.0.BUILD-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.1.0.BUILD-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.8.0-beta2</version>
</dependency>

<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-async</artifactId>
<version>3.8.0-beta2</version>
</dependency>

<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>1.9.0-beta1</version>
</dependency>

<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.7.RELEASE</version>
</dependency>

<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.1.7.RELEASE</version>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb;

import lombok.AllArgsConstructor;
import lombok.Data;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

/**
* @author Christoph Strobl
* @currentRead The Core - Peter V. Brett
*/
@Data
@AllArgsConstructor
@Document("processes")
public class Process {

@Id Integer id;
State state;
int transitionCount;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb;

/**
* @author Christoph Strobl
* @currentRead The Core - Peter V. Brett
*/
public enum State {

CREATED, ACTIVE, DONE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.reactive;

import example.springdata.mongodb.Process;

import org.springframework.data.repository.reactive.ReactiveCrudRepository;

/**
* @author Christoph Strobl
* @currentRead The Core - Peter V. Brett
*/
public interface ReactiveProcessRepository extends ReactiveCrudRepository<Process, Integer> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.reactive;

import example.springdata.mongodb.Process;
import example.springdata.mongodb.State;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Mono;

import java.util.concurrent.atomic.AtomicInteger;

import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

/**
* @author Christoph Strobl
* @currentRead The Core - Peter V. Brett
*/
@Service
@RequiredArgsConstructor
public class ReactiveTransitionService {

final ReactiveProcessRepository repository;
final ReactiveMongoTemplate template;

final AtomicInteger counter = new AtomicInteger(0);

public Mono<Process> newProcess() {
return repository.save(new Process(counter.incrementAndGet(), State.CREATED, 0));
}

public Mono<Integer> run(Integer id) {

return template.inTransaction().execute(action -> {

return lookup(id) //
.flatMap(process -> start(action, process)) //
.flatMap(this::verify) //
.flatMap(process -> finish(action, process));

}).next().map(Process::getId);
}

private Mono<Process> finish(ReactiveMongoOperations operations, Process process) {

return operations.update(Process.class).matching(Query.query(Criteria.where("id").is(process.getId())))
.apply(Update.update("state", State.DONE).inc("transitionCount", 1)).first() //
.then(Mono.just(process));
}

Mono<Process> start(ReactiveMongoOperations operations, Process process) {

return operations.update(Process.class).matching(Query.query(Criteria.where("id").is(process.getId())))
.apply(Update.update("state", State.ACTIVE).inc("transitionCount", 1)).first() //
.then(Mono.just(process));
}

Mono<Process> lookup(Integer id) {
return repository.findById(id);
}

Mono<Process> verify(Process process) {

Assert.state(process.getId() % 3 != 0, "We're sorry but we needed to drop that one");
return Mono.just(process);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.sync;

import example.springdata.mongodb.Process;

import org.springframework.data.repository.CrudRepository;

/**
* @author Christoph Strobl
* @currentRead The Core - Peter V. Brett
*/
interface ProcessRepository extends CrudRepository<Process, Integer> {

}
Loading

0 comments on commit 9025621

Please sign in to comment.