Slide 1

Slide 1 text

RigaDevDay Painfree Object- Document Mapping for MongoDB Philipp Krenn̴̴̴̴@xeraa

Slide 2

Slide 2 text

Vienna

Slide 3

Slide 3 text

Vienna

Slide 4

Slide 4 text

Vienna

Slide 5

Slide 5 text

ViennaDB Papers We Love Vienna

Slide 6

Slide 6 text

Electronic Data Interchange (EDI)

Slide 7

Slide 7 text

Who uses JPA?

Slide 8

Slide 8 text

Who uses MongoDB?

Slide 9

Slide 9 text

Who has heard of Morphia?

Slide 10

Slide 10 text

Like JPA for MongoDB ...but better

Slide 11

Slide 11 text

@OneToMany(mappedBy = "destCustomerId") @ManyToMany @Fetch(FetchMode.SUBSELECT) @JoinTable(name = "customer_dealer_map", joinColumns = { @JoinColumn(name = "customer_id", referencedColumnName = "id")}, inverseJoinColumns = { @JoinColumn(name = "dealer_id", referencedColumnName = "id")}) private Collection dealers;

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Relations vs Objects

Slide 14

Slide 14 text

Ted Neward: ORM is "The Vietnam of Computer Science" http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx

Slide 15

Slide 15 text

MongoDB

Slide 16

Slide 16 text

Table = Collection Schemaless Row = Document JSON

Slide 17

Slide 17 text

{ "name": "Philipp", "isAlive": true, "age": 30, "height_cm": 181.5, "address": { "city": "Vienna", "postalCode": "1190" }, "phoneNumbers": [ { "type": "mobile", "number": "+43 123 4567890" } ] }

Slide 18

Slide 18 text

MongoDB Java driver

Slide 19

Slide 19 text

List phoneNumbers = new ArrayList<>(); phoneNumbers.add( new BasicDBObject("type", "mobile") .append("number", "+43 123 4567890")); BasicDBObject document = new BasicDBObject("name", "Philipp") .append("isAlive", true) .append("age", 30) .append("height_cm", 181.5f) .append("address", new BasicDBObject("city", "Vienna") .append("postalCode", "1190")) .append("phoneNumbers", phoneNumbers);

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Morphia

Slide 23

Slide 23 text

Object-Document Mapping POJO + Annotations Object-Relational Mapping

Slide 24

Slide 24 text

Features Lightweight Type safe & preserving

Slide 25

Slide 25 text

Required libraries https://github.com/mongodb/ mongo-java-driver (3.2.2) + https://github.com/mongodb/ morphia (1.1.1)

Slide 26

Slide 26 text

Show me some code https://github.com/xeraa/ morphia-demo

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

$ gradle test Standalone or embedded MongoDB

Slide 29

Slide 29 text

Annotations

Slide 30

Slide 30 text

Collections

Slide 31

Slide 31 text

@Entity( value = "company" ) public class CompanyEntity { @Id protected ObjectId id; public CompanyEntity() { }

Slide 32

Slide 32 text

Do not use dashes in the collection name

Slide 33

Slide 33 text

https://www.destroyallsoftware.com/talks/wat

Slide 34

Slide 34 text

Do not copy paste the value attribute

Slide 35

Slide 35 text

Do not use @Id as String (without reason)

Slide 36

Slide 36 text

Polymorphism

Slide 37

Slide 37 text

public abstract class EmployeeEntity { protected String name; } public class ManagerEntity extends EmployeeEntity { protected Boolean approveFunds; } public class WorkerEntity extends EmployeeEntity { protected Integer yearsExperience; }

Slide 38

Slide 38 text

RDBMS 1. Union table with (many) NULL values

Slide 39

Slide 39 text

RDBMS 2. Concrete instances without common queries

Slide 40

Slide 40 text

RDBMS 3. Base table joined with all subtables

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

@Entity(value = "employee", noClassnameStored = false) public abstract class EmployeeEntity { @Id protected ObjectId id; protected String name; } public class ManagerEntity extends EmployeeEntity { protected Boolean approveFunds; } public class WorkerEntity extends EmployeeEntity { protected Integer yearsExperience; }

Slide 43

Slide 43 text

{ "_id": ObjectId("5461c8bf9e2acf32ed50c079"), "className": "net.xeraa.morphia_demo.entities.ManagerEntity", "name": "Peter", "approveFunds": true } { "_id": ObjectId("524d9fe7e4b0f8bd3031f84e"), "className": "net.xeraa.morphia_demo.entities.WorkerEntity", "name": "Philipp", "yearsExperience": 10 }

Slide 44

Slide 44 text

className and refactoring?

Slide 45

Slide 45 text

Properties

Slide 46

Slide 46 text

protected String firstname; @AlsoLoad("lastname") protected String surname; protected Boolean approveFunds; @Property("hire") protected boolean managerCanApproveHires; public EmployeeEntity setFirstname(String firstname) { this.firstname = firstname; return this; }

Slide 47

Slide 47 text

Do trim property names (MMAPv1)

Slide 48

Slide 48 text

Do use object data types

Slide 49

Slide 49 text

Do provide chainable setters

Slide 50

Slide 50 text

Do use Lombok for more !

Slide 51

Slide 51 text

Indexes

Slide 52

Slide 52 text

@Entity(value = "employee", noClassnameStored = false) @Indexes(@Index(name = "name", fields = { @Field(value = "surname"), @Field(value = "firstname") })) public class EmployeeEntity { protected String firstname; protected String surname; @Indexed(unique = true, sparse = false) protected String email;

Slide 53

Slide 53 text

Optimistic locking vs pessimistic locking in RDBMS

Slide 54

Slide 54 text

@Version private long version; { ... "version": NumberLong("1") }

Slide 55

Slide 55 text

Anti JOIN

Slide 56

Slide 56 text

public class EmployeeEntity { @Reference protected CompanyEntity company; @Embedded protected BankConnectionEntity bankConnection;

Slide 57

Slide 57 text

Queries

Slide 58

Slide 58 text

Save or upsert

Slide 59

Slide 59 text

public ObjectId persistCompanyEntity(CompanyEntity company) { mongoDatastore.save(company); return company.getId(); } public ObjectId persistManagerEntity(ManagerEntity manager) { mongoDatastore.save(manager); return manager.getId(); } public ObjectId persistWorkerEntity(WorkerEntity worker) { mongoDatastore.save(worker); return worker.getId(); }

Slide 60

Slide 60 text

Get data

Slide 61

Slide 61 text

public EmployeeEntity findByEmail(final String email) { return mongoDatastore.find(EmployeeEntity.class) .field("email").equal(email).get(); } public List getAllEmployees() { return mongoDatastore.find(EmployeeEntity.class).asList(); } public List getAllManagers() { return mongoDatastore.find(ManagerEntity.class) .disableValidation() .field("className").equal(ManagerEntity.class.getName()) .asList(); }

Slide 62

Slide 62 text

Watch out .equal() != .equals() Trust your compiler

Slide 63

Slide 63 text

Performance Normalize fields email.toLowerCase() before saving

Slide 64

Slide 64 text

More queries Regular expressions .exists(), .doesNotExist() .greaterThan(), .hasAnyOf(),... .sort(), .skip(), .limit()

Slide 65

Slide 65 text

More features Capped collections Full-text search Aggregation framework Geo locations

Slide 66

Slide 66 text

Patterns

Slide 67

Slide 67 text

Base Class

Slide 68

Slide 68 text

public abstract class BaseEntity { @Id protected ObjectId id; protected Date creationDate; protected Date lastChange; @Version private long version;

Slide 69

Slide 69 text

public BaseEntity() { super(); } // No setters public ObjectId getId() { return id; } public Date getCreationDate() { return creationDate; } public Date getLastChange() { return lastChange; }

Slide 70

Slide 70 text

@PrePersist public void prePersist() { this.creationDate = (creationDate == null) ? new Date() : creationDate; this.lastChange = (lastChange == null) ? creationDate : new Date(); } public abstract String toString(); }

Slide 71

Slide 71 text

public ObjectId persist(E entity) { mongoDatastore.save(entity); return entity.getId(); } public long count(Class clazz) { return mongoDatastore.find(clazz).countAll(); } public E get(Class clazz, final ObjectId id) { return mongoDatastore.find(clazz).field("id").equal(id).get(); }

Slide 72

Slide 72 text

Converters

Slide 73

Slide 73 text

Option 1 Not readable or searchable in the database @Serialized protected BigDecimal bonus;

Slide 74

Slide 74 text

Option 2 Fugly @Transient protected BigDecimal salary; protected String salaryString;

Slide 75

Slide 75 text

@PrePersist public void prePersist() { super.prePersist(); if (salary != null) { this.salaryString = this.salary.toString(); } } @PostLoad public void postLoad() { if (salaryString != null) { this.salary = new BigDecimal(salaryString); } else { this.salary = null; } }

Slide 76

Slide 76 text

Option 3 Yes! @Converters({BigDecimalConverter.class}) public class WorkerEntity extends EmployeeEntity { protected BigDecimal dailyAllowance;

Slide 77

Slide 77 text

public class BigDecimalConverter extends TypeConverter implements SimpleValueConverter { @Override public Object encode(Object value, MappedField optionalExtraInfo) { if (value == null) { return null; } return value.toString(); } @Override public Object decode(Class targetClass, Object fromDBObject, MappedField optionalExtraInfo) throws MappingException { if (fromDBObject == null) { return null; } return new BigDecimal(fromDBObject.toString()); } }

Slide 78

Slide 78 text

More http://projects.spring.io/spring-data-mongodb/ https://github.com/fakemongo/fongo https://github.com/evanchooly/critter

Slide 79

Slide 79 text

Critter Query query = ds.createQuery(Query.class); query.and( query.criteria("bookmark").equal(bookmark), query.criteria("database").equal(database) ); QueryCriteria criteria = new QueryCriteria(datastore); criteria.and( criteria.bookmark(bookmark), criteria.database(database) ); Query query = criteria.query().get();

Slide 80

Slide 80 text

But is it fast?

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

Conclusion

Slide 83

Slide 83 text

Things you won't get Transactions JOINs

Slide 84

Slide 84 text

Things you will get Rapid development Replication and sharding Impedence match with rich documents

Slide 85

Slide 85 text

To sum up Developers ❤ Morphia

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

PS: Elastic

Slide 88

Slide 88 text

Thanks! Questions? @xeraa

Slide 89

Slide 89 text

Image Credit • Schnitzel https://flic.kr/p/9m27wm • Architecture https://flic.kr/p/6dwCAe • Conchita https://flic.kr/p/nBqSHT • Paper: http://www.freeimages.com/photo/432276