NoSQL Endgame
Otávio Santana
Platform.sh
Thodoris Bais
ABN Amro & Utrecht JUG
Werner Keil
CATMedia
21-23 December 2020 #Virtual
Werner Keil Thodoris Bais
Jakarta EE Specification Committee Member
Let’s meet
@thodorisbais@wernerkeil
Jakarta EE Ambassador, EG Member JSR-385
Otavio Santana
Developer Relations Engineer, Platform.sh
Let’s meet
@otaviojava
4
5 trends in 2020
New digital trends create new technical
challenges
@thodorisbais@wernerkeil @otaviojava
• Develop with agility
• Operate at any scale
• Deliver the performance and
availability required to meet the
demands of Digital Economy
businesses
What are the technical challenges?
7
• Handle large volumes of data at high-speed with a scale-out architecture
• Store unstructured, semi-structured, or structured data
• Enable easy updates to schemas and fields
• Developer-friendly
• Take full advantage of the cloud to deliver zero downtime
Advantages of NoSQL
Why this talk
Tons of persistence frameworks
Which one performs best for your case?
JVM can cope with heavy loads
Would normally take ages to
compare
NoSQL
01 Database
02 Doesn't use structure
03 Not Transaction
04 Base
Four different types05
Key-Value stores
AmazonS3
Apollo
Ares
Aphrodite
Sun
War
Love Beauty
AmazonDynamo
Hazelcast
Redis
Column-Oriented
Apollo
Aphrodite
Ares
Kratos
Duty
Duty
Duty
Dead Gods
Love, happy
Sun
War
13
Color
weapon
Sword
Row-key ColumnsHBase
Scylla
SimpleDB
Cassandra
DynamoDB
Clouddata
Document stores
{
"name":"Diana",
"duty":[
"Hunt",
"Moon",
"Nature"
],
"siblings":{
"Apollo":"brother"
}
}
ApacheCouchDB
MongoDB
Couchbase
Graph databases
Apollo Ares
Kratos
was killed by was killed by
killed killed
Neo4j
InfoGrid
Sones
HyperGraphDB
Multi-Model
01
02
03
04
OrientDB (graph, document)
Couchbase (key value, document)
Elasticsearch (document, graph)
ArangoDB (document, graph, key-value)
SQL vs NoSQL
SQL KEY-VALUE COLUMN DOCUMENTS GRAPH
Table Bucket Column family Collection
Row Key/value pair Column Documents Vertex
Column Key/value pair Key/value pair Vertex and
Edge property
Relationship Link Edge
BASE vs ACID
• Basically Available
• Soft state
• Eventual consistency
• Atomicity
• Consistency
• Isolation
• Durability
CAP
Scalability vs Complexity
Scalability
Complexity
key-value
Column
Document
Graph
Relational Application NoSQL Application
Logic Tier Logic Tier
DAO DAO
JPAJPAJPAJPA
JDBC JDBCJDBCJDBC
Data Tier
APIAPI API
Data Tier
Our comparison model
JPA problem for NoSQL
01
02
03
04
05
06
Saves Async
Async Callback
Time to Live (TTL)
Consistency Level
SQL based
Diversity in NoSQL
Annotated Entities
01
02
03
Mapped Superclass
Entity
Column
@Entity("god")
public class God {
@Column
private String name;
@Column
private long age;
@Column
private Set<String> powers;
}
Template
God artemis = ...;
DocumentTemplate template = …
template.insert(artemis);
template.update(artemis);
DocumentQuery query = ...
List<God> gods = template.select(query);
Repository
interface GodRepository extends MongoRepository<God, String> {
List<God> findByNameAndAge (String name, Integer age);
}
Repository
public interface MovieRepository extends
ReactiveNeo4jRepository<MovieEntity, String> {
Mono<MovieEntity> findOneByTitle(String title);
}
Entities
@Node("Person")
public class PersonEntity {
@Id
private final String name;
private final Integer born;
}
@Node("Movie")
public class MovieEntity {
@Id
private final String title;
@Property("tagline")
private final String description;
@Relationship(type = "ACTED_IN", direction = INCOMING)
private Map<PersonEntity, Roles> actorsAndRoles =
new HashMap<>();
@Relationship(type = "DIRECTED", direction = INCOMING)
private List<PersonEntity> directors = new ArrayList<>();
}
Relationships
@RelationshipProperties
public class Roles {
private final List<String> roles;
public Roles(List<String> roles) {
this.roles = roles;
}
}
@Document(collection = "gods")
public class God { … }
interface GodRepository extends
MongoRepository<God, String> { … }
What about the Controller?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring.data.mongodb.database=mythology
spring.data.mongodb.port=27017
Logistics Domain
Our example
MovieEntity
MovieRepository
PersonEntity
Roles
…
Other reactive dependencies
…
<dependency>
<groupId>org.neo4j.springframework.data</groupId>
<artifactId>spring-data-neo4j-rx-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
org.neo4j.driver.uri=bolt://localhost:7687
org.neo4j.driver.authentication.username=neo4j
org.neo4j.driver.authentication.password=secret
Logistics Domain
Demo Time
@thodorisbais@wernerkeil @otaviojava
Repository
@Inject
@Database(DatabaseType.COLUMN)
private GodRepository godRepository;
@Inject
@Database(DatabaseType.KEY_VALUE)
private GodRepository godRepository;
Repository with Queries
interface PersonRepository extends Repository<Person, Long> {
@Query("select * from Person")
Optional<Person> findByQuery();
@Query("select * from Person where id = @id")
Optional<Person> findByQuery(@Param("id") String id);
}
Diversity
@Entity("god")
public class God {
@Column
private String name;
@UDT("weapon")
@Column
private Weapon weapon;
}
interface GodRepository extends
CassandraRepository<God, String> {
@CQL("select * from God where name = ?")
List<God> findByName(String name);
}
List<Movie> movies = documentTemplate.query("select * from Movie
where year > 2012");
List<Person> people = columnTemplate.query("select * from Person
where age = 25");
Optional<God> god = keyValueTemplate.query("get 'Ullr'");
List<City> cities = graphTemplate.query("g.V().hasLabel('City')");
Query by Text
Micronaut Data
NoSQL?
Repository
public class AnimalRepository {
List<Animal> findByType(String type) {…}
public Animal insert(Animal animal) {…}
}
Entity
public class City {
private String name;
public City(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Query by Text
public void save(City city) {
try (Session s = driver.session()) {
String statement = "MERGE (c:City {id:$name.id}) ON CREATE SET c+=$name";
s.writeTransaction(tx -> tx.run(statement, singletonMap("name" ,city.asMap())));
}
}
public Stream<City> findByName(String name) {
try (Session s = driver.session()) {
String statement = "MATCH (c:City) WHERE c.name contains $name RETURN c";
return s.readTransaction(tx -> tx.run(statement, singletonMap("name", name)))
.list(record -> new City(record.get("c").asMap())).stream();
}
}
GORM
NoSQL?
Entity
@Entity
class User {
ObjectId id
String emailAddress
String password
String fullname
Date dateCreated
Date lastUpdated
static constraints = {
emailAddress email: true
password nullable: true
fullname blank: false
}
}}
What happened to
?
Entity
@Entity(”Editor")
public class Editor {
@Id
private String editor;
private String editorName;
@OneToMany(mappedBy = “editor”, cascade = PERSIST)
private Set<Author> assignedAuthors = new HashSet<>();
// constructors, getters, setters…
}
Operations
Warning
“In most cases, multi-document transaction incurs a greater
performance cost over single document writes, and the availability of
multi-document transactions should not be a replacement for
effective schema design. For many scenarios, the denormalized data
model (embedded documents and arrays) will continue to be optimal
for your data and use cases. That is, for many scenarios, modeling
your data appropriately will minimize the need for multi-document
transactions.”
Documentation
Entity
@Entity(name = "book")
@Indexed
@Analyzer(impl = StandardAnalyzer.class)
public class Book {
@Id
@DocumentId
private Long isbn;
@Column
@Field(analyze = Analyze.NO)
private String name;
@Column
@Field
private String author;
@Column
@Field
private String category;
}
Operations
EntityManagerFactory managerFactory = Persistence.createEntityManagerFactory("hibernate");
EntityManager manager = managerFactory.createEntityManager();
manager.getTransaction().begin();
Book cleanCode = getBook(1L, "Clean Code", "Robert Cecil Martin");
manager.merge(cleanCode);
manager.getTransaction().commit();
Book book = manager.find(Book.class, 1L);
Warning
“Cassandra does not use RDBMS ACID transactions with
rollback or locking mechanisms...
As a non-relational database, Cassandra does not support joins
or foreign keys, and consequently does not offer consistency in
the ACID sense.”
DataStax Documentation
Conclusions
@thodorisbais@wernerkeil @otaviojava
Conclusions
Still in Progress ❌
Supports a huge number of NoSQL
database systems
✅
Loosely coupled code makes switching between
different NoSQL vendors just a matter of configuration
✅
Removes boiler-plate code and
provides a cleaner, more readable DAO
✅
Conclusions
Switching between different NoSQL
vendors not so easy
❌
Micronaut Data does not support NoSQL
databases yet
❌
Still in Progress ❌
Often faster with a lower footprint and polyglot
language support for Java, Kotlin and Groovy
✅
Conclusions
Provides a cleaner and more readable DAO
implementation
✅
Works with Grails, Micronaut or Spring Boot, polyglot
language support for Java and Groovy
✅
Removes a lot of boiler-plate code ✅
Only 3 to 4 of the most popular NoSQL databases are supported
out of the box, others require more effort or won’t work yet ❌
Conclusions
Provides a cleaner and more readable DAO
implementation
✅
Loosely coupled code makes switching between
different NoSQL vendors just a matter of configuration
✅
Removes a lot of boiler-plate code ✅
Only half a dozen popular NoSQL databases are supported out of
the box, others require more effort or won’t work yet ❌
Conclusions
Using a mapper for all scenarios is not a
recommended approach
❌
Some JPA concepts are not easily mapped to
the NoSQL world (e.g. transactions)
❌
No error-safe way to run the query language and hydrate DTOs
from the result ❌
Outdated, no release since 2018 ❌
▪ GitHub repositories
▪ https://github.com/JNOSQL/nosql-endgame
▪ Project page
▪ http://jnosql.org
@thodorisbais@wernerkeil
Links
5
6
• Flaticon.com
• Michael Simons
• Jean Tessier
Credits
@thodorisbais@wernerkeil
Thank You !
@thodorisbais@wernerkeil @otaviojava

NoSQL Endgame - Java2Days 2020 Virtual