Deep Dive into Spring Data and MongoDB
Fabiano Guizellini Modos Software Architect at HBSIS @fmodos
Deep Dive into Spring Data and MongoDB Fabiano Guizellini Modos - - PowerPoint PPT Presentation
Deep Dive into Spring Data and MongoDB Fabiano Guizellini Modos Software Architect at HBSIS @fmodos I Java and MongoDB Why do I love Java? Learned Object Oriented Programming Design Patterns, Clean Code, Unit Test
Fabiano Guizellini Modos Software Architect at HBSIS @fmodos
Why do I love Java?
Programming
Delphi
Why do I love MongoDB?
commands: Create table, Alter Table, etc..
Order Invoice
Supermarket
Order Invoice
Supermarket
Deliver ~5k Suppliers Invoice Daily ~4M Events Daily >10M DB Ops/Daily >100k Sales Invoices Daily >500k Events Daily >10M DB Ops/Daily ~35k Transport Invoice Daily ~300k Events Daily >1M DB Ops/Daily Validate Suppliers Invoice Sales Invoice Transport Invoice
Why did we use MongoDB?
Why did we use MongoDB?
Relational Database? SQL?
SQL NOSQL
“Different databases are designed to solve different problems. Using a single database engine for all of the requirements usually leads to non- performant solutions” ― Pramod J. Sadalage, NoSQL Distilled: A Brief Guide to the Emerging World
“Complex applications combine different types of problems, so picking the right language for each job may be more productive than trying to fit all aspects into a single language.” ― Pramod J. Sadalage, NoSQL Distilled: A Brief Guide to the Emerging World
Case Study – Food Recipe System
Class Diagram Table Diagram (SQL) Document Diagram (NoSQL)
Create
SQL NoSQL INSERT INTO dish (name) VALUES (“White Rice”) INSERT INTO ingredient (name, dish_id) VALUES (“rice”, 1) INSERT INTO ingredient (name) VALUES (“garlic”, 1) INSERT INTO comment (text, user_id, dish_id) VALUES (“This rice is really good”, 1, 1) db.getCollection(‘Dish’).save({name: “White Rice” ingredients : [“rice”, “garlic”], comments : Array[ { text : ‘This rice is really good’ user : { nickname : ‘FoodLover’, email: ‘fabiano@mail.com’ } ] })
Read
SQL NoSQL SELECT d.name, d.description FROM dish d WHERE id=1 SELECT i.name FROM ingredient i WHERE i.dish_id=1 SELECT c.text, u.nickname, u.name FROM comment c, user u WHERE c.user_id = u.id and c.dish_id = 1 db.getCollection(‘Dish’).find({_id : 1})
Update
SQL NoSQL UPDATE user SET nickname = ‘Bryan’ WHERE id=1 db.getCollection(‘User’).update({_id : 1}, {$set : {nickname : ‘Bryan’}})
{name: “White Rice” ingredients : [“rice”, “garlic”], comments : Array[ { text : ‘This rice is really good’ user : { nickname : ‘FoodLover’, email: ‘fabiano@mail.com’ } ]
Data Inconsistency
Delete
SQL NoSQL DELETE FROM ingredient WHERE dish_id=1 DELETE FROM comment WHERE dish_id=1 DELETE FROM dish WHERE id=1 db.getCollection(‘Dish’).remove({_id : 1})
Java MongoDB Driver
Where is the Framework to Map Java Objects and Documents?
Spring Data
Spring Data
“This is an umbrella project which contains many subprojects that are specific to a given database”
How do we get started?
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data- mongodb</artifactId> <version>1.10.6.RELEASE</version> </dependency> spring.data.mongodb.host=localhost spring.data.mongodb.port=27017 spring.data.mongodb.database=dish-db
Application Layers
DishController DishService DishMongoRepository
Application Layers
How to create MongoDB Repository?
package com.fmodos.dish.infraestructure.mongodb; import org.springframework.data.mongodb.repository.MongoRepository; import com.fmodos.dish.domain.Dish; public interface DishMongoRepository extends MongoRepository<Dish, String> { }
How to use the MongoDB Repository?
@Service public class DishService { @Autowired DishMongoRepository dishRepository; public void insert(Dish dish) { dishRepository.insert(dish); } public interface DishMongoRepository extends MongoRepository<Dish, String> { } Where is the insert method?
SpringData Repositories
MongoRepository PagingAndSortingRepository CrudRepository Repository DishMongoRepository <extends>
SpringData Repositories
<S extends T> S save(S entity); <S extends T> Iterable<S> save(Iterable<S> entities); long count(); void delete(ID id); void delete(T entity); void delete(Iterable<? extends T> entities); void deleteAll();
dishMongoRepository.delete("id"); List<Dish> listDish = new ArrayList<Dish>(); List<Dish> savedList = dishMongoRepository.save(listDish); long count = dishMongoRepository.count();
SpringData Repositories
T findOne(ID id); boolean exists(ID id); Iterable<T> findAll(); Iterable<T> findAll(Iterable<ID> ids); Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable);
List<Dish> listDish = dishRepository.findAll() Dish dish = dishRepository.findOne(id) Page<Dish> pageDish = dishMongoRepository.findAll( new PageRequest(1, 10));
What about Query Filter?
public interface DishMongoRepository extends MongoRepository<Dish, String> { public List<Dish> findByName(String name); public List<Dish> findByNameAndDateCreatedGreaterThan(String name Date dateCreated); } “Methods should have verb or verb phrase names” Clean Code
What is the magic? DSL – Dynamic Reception
them in the receiving class
method declaration
Spring Data Query
Keyword Sample Logical result After findByBirthdateAfter(Date date) {"birthdate" : {"$gt" : date}} GreaterThan findByAgeGreaterThan(int age) {"age" : {"$gt" : age}} GreaterThanEqual findByAgeGreaterThanEqual(int age) {"age" : {"$gte" : age}} Before findByBirthdateBefore(Date date) {"birthdate" : {"$lt" : date}} LessThan findByAgeLessThan(int age) {"age" : {"$lt" : age}} LessThanEqual findByAgeLessThanEqual(int age) {"age" : {"$lte" : age}} Between findByAgeBetween(int from, int to) {"age" : {"$gt" : from, "$lt" : to}} In findByAgeIn(Collection ages) {"age" : {"$in" : [ages…]}} NotIn findByAgeNotIn(Collection ages) {"age" : {"$nin" : [ages…]}} IsNotNull, NotNull findByFirstnameNotNull() {"firstname" : {"$ne" : null}} IsNull, Null findByFirstnameNull() {"firstname" : null} Like, StartingWith, EndingWith findByFirstnameLike(String name) {"firstname" : name} (name as regex) NotLike, IsNotLike findByFirstnameNotLike(String name) {"firstname" : { "$not" : name }} (name as regex)
Spring Data Query
Containing on String findByFirstnameContaining(String name) {"firstname" : name} (name as regex) NotContaining on String findByFirstnameNotContaining(String name) {"firstname" : { "$not" : name}} (name as regex) Containing on Collection findByAddressesContaining(Address address) {"addresses" : { "$in" : address}} NotContaining on Collection findByAddressesNotContaining(Addr ess address) {"addresses" : { "$not" : { "$in" : address}}} Regex findByFirstnameRegex(String firstname) {"firstname" : {"$regex" : firstname }} (No keyword) findByFirstname(String name) {"firstname" : name} Not findByFirstnameNot(String name) {"firstname" : {"$ne" : name}} IsTrue, True findByActiveIsTrue() {"active" : true} IsFalse, False findByActiveIsFalse() {"active" : false} Exists findByLocationExists(boolean exists) {"location" : {"$exists" : exists }}
MongoDB JSON Based Query
public interface DishMongoRepository extends MongoRepository<Dish, String> { @Query(value="{ 'ingredients' : {$in : ?0 }", fields="{ 'name' : 1}") public List<Dish> findNameByIngredients(List<String> ingredients); }
MongoTemplate Operations
mongoTemplate.upsert(new Query(Criteria.where("name").is("Brazil Food")), Update.update("description", "Really Good Food"), Dish.class) @Autowired MongoTemplate mongoTemplate;
Dish dish = mongoTemplate.findAndModify(new Query(Criteria.where("name").is("Brazil Food")), Update.update("description", "Really Good Food"), Dish.class); “The design goal was to make it as easy as possible to transition between the use of the base MongoDB driver and MongoOperations”
How to combine Repository and MongoTemplate?
interface DishMongoRepository interface MongoRepository interface IDishMongoTemplateRepository class DishMongoTemplateRepository extends extends extends
DishController DishService DishMongoRepository
Application Layers
High Coupling Between Dish Service Businesse Rules and MongoDB
Application Layers
DishController DishService IDishRepository MongoDishRepository JpaRepository <extends> <extends> SQLDishRepository <extends>
Database Resilience – Circuit Breaker
IDishRepository MongoDishRepository SQLDishRepository HystrixRepository <extends>
Application Query without Index Query without Projection
Full Text Query
Query query = TextQuery.queryText(new TextCriteria().matching("rice")); mongoTemplate.find(query, Dish.class);
Query in the Dish Fields annotated with @TextIndexed(weight=1)
Similar behavior would be to query using OR operator
GeoSpatial – Query Player within Box
Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)); List<Player> venues = template.find(new Query(Criteria.where("location").withinBox(box)), Player.class); class Player { private double[] location; }
GeoSpatial – Query by distance
Point location = new Point(-73.99171, 40.738868); NearQuery query = NearQuery.near(location).maxDistance(new Distance(10, Metrics.MILES)); GeoResults<SinglePerson> = operations.geoNear(query, SinglePerson.class);
Summary