Building a sample application
The basic example is available in the master branch. The more advanced sample, with embedded MongoDB, is committed to the mongo branch. In case you would like to try running more advanced sample, you need to switch to that branch using git checkout mongo. Now, we need to perform some changes in the model class to enable object mapping to MongoDB. The model class has to be annotated with @Document and the primary key field with @Id. I also changed the ID field type from Long to String because MongoDB generates primary keys in UUID format, for example, 59d63385206b6d14b854a45c:
@Document(collection = "person")
public class Person {
@Id
private String id;
private String firstName;
private String lastName;
private int age;
private Gender gender;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// ...
}
The next step is to create a repository interface that extends MongoRepository. MongoRepository provides basic methods for searching and storing data, such as findAll, findOne, save, and delete. Spring Data has a very smart mechanism for performing queries using repository objects. We don't have to implement queries by ourselves, but only define an interface method with the right name. The method name should have the prefix findBy and then the searched field name. It may end with a standard search keyword suffix, such as GreaterThan, LassThan, Between, Like, and many more. A MongoDB query is automatically generated by Spring Data classes based on the full method name. The same keywords may be used in conjunction with delete…By or remove…By to create remove queries. In the PersonRepository interface, I decided to define two find methods. The first of them, findByLastName, selects all Person entities with the given lastName value. The second, findByAgeGreaterThan, is designed to retrieve all Person entities with an age greater than a given value:
public interface PersonRepository extends MongoRepository<Person, String> {
public List<Person> findByLastName(String lastName);
public List<Person> findByAgeGreaterThan(int age);
}
The repository should be injected into the REST controller class. Then, we can finally call all the required CRUD methods provided by PersonRepository:
@Autowired
private PersonRepository repository;
@Autowired
private PersonCounterService counterService;
@GetMapping
public List<Person> findAll() {
return repository.findAll();
}
@GetMapping("/{id}")
public Person findById(@RequestParam("id") String id) {
return repository.findOne(id);
}
@PostMapping
public Person add(@RequestBody Person p) {
p = repository.save(p);
counterService.countNewPersons();
return p;
}
@DeleteMapping("/{id}")
public void delete(@RequestParam("id") String id) {
repository.delete(id);
counterService.countDeletedPersons();
}
We have also added two API methods for custom find operations from the PersonRepository bean:
@GetMapping("/lastname/{lastName}")
public List<Person> findByLastName(@RequestParam("lastName") String lastName) {
return repository.findByLastName(lastName);
}
@GetMapping("/age/{age}")
public List<Person> findByAgeGreaterThan(@RequestParam("age") int age) {
return repository.findByAgeGreaterThan(age);
}
That's all that had to be done. Our microservice that exposes basic API methods implementing CRUD operations on an embedded Mongo database is ready to launch. You have probably noticed that it didn't require us to create a lot of source code. Implementation of any interaction with databases, whether relational or NoSQL, using Spring Data is fast and relatively easy. Anyway, there is still one more challenge facing us. An embedded database is a good choice, but only in development mode or for unit testing, not in production. If you have to run your microservice in production mode, you would probably launch one standalone instance or some instances of Mongo deployed as a sharded cluster, and connect the application to them. For our example purposes, I'll run a single instance of MongoDB using Docker.