Deep Dive into Spring Data and MongoDB - Percona · 2019-06-03 · Map Java Objects and Documents?...
Transcript of Deep Dive into Spring Data and MongoDB - Percona · 2019-06-03 · Map Java Objects and Documents?...
Deep Dive into
Spring Data and
MongoDBFabiano Guizellini Modos
Software Architect at HBSIS@fmodos
I Java andMongoDB
Why do I love Java?
• Learned Object OrientedProgramming
• Design Patterns, Clean Code, Unit Test
• Saved me from Programming in Delphi
Why do I love MongoDB?
• Save complex Objects in an easy way
• No more relational databasecommands: Create table, Alter Table, etc..
• Open Source
It is not only a Story
...run this in Production in 3 critical systems
Order
Invoice
SupermarketOrder
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?
• Complex documents and events
• High Concurrency System
Why did we use MongoDB?
• Complex documents and events
• High Concurrency System
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 of Polyglot Persistence
“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 of Polyglot Persistence
Case Study – Food Recipe System
Class Diagram Table Diagram (SQL) Document Diagram (NoSQL)
• Users should be able to create Dish with a name, description and list of ingredientes• Users should be able to add Comments to the Dish.
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: ‘[email protected]’
}]
})
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: ‘[email protected]’
}
]
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 theFramework to
Map Java Objectsand Documents?
Spring Data
Spring Data
“This is an umbrella project which contains many subprojects that are specific to a given database”
• MongoDB
• Neo4j
• Cassandra
• JDBC Extensions
• Redis
• Etc
How do we get started?
• Add this maven configuration to the pom.xml file
• Add the MongoDB config properties to the config file
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId><version>1.10.6.RELEASE</version></dependency>
spring.data.mongodb.host=localhostspring.data.mongodb.port=27017spring.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?
@Servicepublic class DishService {
@AutowiredDishMongoRepository dishRepository;
public void insert(Dish dish) {dishRepository.insert(dish);
}
public interface DishMongoRepository extends MongoRepository<Dish, String> {
}
Where is the insertmethod?
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
• Handle messages without defining them in the receiving class
• Build dynamic queries using the 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)
@AutowiredMongoTemplate mongoTemplate;
• Upsert - Update or insert a new Document combining the Query and the Update values
• Autowire MongoTemplate
• FindAndModify – Query the Document, update it and return the Updated one
Dish dish = mongoTemplate.findAndModify(newQuery(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 totransition between the use of the base MongoDB driver andMongoOperations”
How to combine Repository and MongoTemplate?
interfaceDishMongoRepository
interfaceMongoRepository
interfaceIDishMongoTemplateRepository
classDishMongoTemplateRepository
extends
extends
extends
DishController DishService DishMongoRepository
Application Layers
High Coupling Between DishService Businesse Rules andMongoDB
Application Layers
DishController DishService IDishRepository
MongoDishRepository
JpaRepository
<extends>
<extends>
SQLDishRepository
<extends>
Database Resilience – Circuit Breaker
IDishRepository
MongoDishRepository
SQLDishRepository
HystrixRepository
<extends>
DemoLessons Learned
Application
Query without IndexQuery without Projection
Cool features I never usedin Production
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
• Document Oriented Modelling
• Native CRUD operations in MongoDB
• Application Layers
• SpringData Repositories
• SpringData Query
• MongoTemplate Operations
• Lessons Learned in Production
• FullTextQuery
• GeoSpatialQuery
Thank you!@fmodos