MongoDB Persistence with Java and Morphia
Software Engineer, MongoDB
Justin Lee
AgendaIntroduction
Getting Started
Modeling
Querying
Updating
Indexing
Road Map
What is Morphia?• Object document mapper — https://github.com/mongodb/morphia
• Provides mapping to/from Java objects to mongodb
• Annotation-based
• Embedded objects/documents
• References (lazy or eager)
• Fluent query/update APIs
• Runtime validation
Why Morphia?• Offload object marshaling
• automatically keeps up as objects evolve: new, removed, renamed
• Higher abstraction
• Simplified management of indexes
• Unify code with schema management
• Object/Document Versioning
A Brief History
• Created by Scott Hernandez
• Hired by 10Gen —> started working on the kernel
• Morphia left alone for a couple of years
• Lots of uncertainty
• James Green forked morphia
• I was hired in June, 2013 in part to pick up Morphia
• 5 releases since with another pending
Getting Started
Adding morphia — Maven
<dependency>
<groupId>org.mongodb.morphia</groupId>
<artifactId>morphia</artifactId>
<version>0.105</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.11.3</version>
</dependency>
Adding morphia — Gradle
compile "org.mongodb.morphia:morphia:0.105"
compile "org.mongodb:mongo-java-driver:2.11.3"
!
!
Initializing morphia
Morphia morphia = new Morphia();
Datastore datastore = morphia.createDatastore(
new MongoClient(), “morphia-demo");
morphia.mapPackage("org.mongodb.morphia.demo");
datastore.ensureIndexes();
!
Entity Modeling
Basic Entity
@Entity
public class GithubUser {
private String userName;
private String fullName;
!
private Date memberSince;
}
Basic Entity
@Entity
public class GithubUser {
private String userName;
private String fullName;
!
private Date memberSince;
}
(value = “users")
Basic Entity
@Entity
public class GithubUser {
private String userName;
private String fullName;
!
private Date memberSince;
}
(value = "users", noClassnameStored = true)
Basic Entity
@Entity
public class GithubUser {
private String userName;
private String fullName;
!
private Date memberSince;
}
(value = "users", noClassnameStored = true)
@Id
Basic Entity
@Entity
public class GithubUser {
private String userName;
private String fullName;
!
private Date memberSince;
}
(value = "users", noClassnameStored = true)
@Id
@Property("since")
Basic Entity — Saving
GithubUser user = new GithubUser("evanchooly");
user.fullName = "Justin Lee";
user.memberSince = sdf.parse("6-15-1987");
datastore.save(user);
Basic Entity — Shell
repl0:PRIMARY> db.users.findOne() { "_id" : "evanchooly", "fullName" : "Justin Lee", "since" : ISODate("1987-06-15T04:00:00Z") }
Complex Entities
@Entity("orgs") public class Organization { @Id public String name; ! public Organization(final String name) { this.name = name; } }
Complex Entities
@Embedded public class Settings { public String defaultBranch = "master"; public Boolean allowWiki = false; public Boolean allowIssues = true; }
Complex Entities
@Entity("repos") public class Repository { @Id public String name; @Reference public Organization organization; @Reference public GithubUser owner; public Settings settings = new Settings(); }
Complex Entities
@Entity(value = "users", noClassnameStored = true) public class GithubUser { @Id public final String userName; public String fullName; @Property("since") public Date memberSince; @Reference(lazy = true) public List<Repository> repositories = new ArrayList<>(); }
Complex Entities
Organization org = new Organization("mongodb"); !GithubUser user = new GithubUser("evanchooly"); user.fullName = "Justin Lee"; user.memberSince = sdf.parse("6-15-1987"); !datastore.save(org); datastore.save(user); !datastore.save(new Repository(org, "morphia")); datastore.save(new Repository(user, "morphia"));
Complex Entities — Shell
repl0:PRIMARY> show collections orgs repos system.indexes users
Complex Entities — Shell
repl0:PRIMARY> db.orgs.findOne(); { "_id" : "mongodb", "className" : "org.mongodb.morphia.demo.Organization" }
Complex Entities — Shellrepl0:PRIMARY> db.repos.find().pretty(); { "_id" : "mongodb/morphia", "className" : "org.mongodb.morphia.demo.Repository", "organization" : DBRef("orgs", "mongodb"), "settings" : { "defaultBranch" : "master", "allowWiki" : false, "allowIssues" : true } } { "_id" : "evanchooly/morphia", "className" : "org.mongodb.morphia.demo.Repository", "owner" : DBRef("users", "evanchooly"), "settings" : { … } }
Complex Entities — Shell
repl0:PRIMARY> db.users.findOne(); { "_id" : "evanchooly", "fullName" : "Justin Lee", "since" : ISODate("1987-06-15T04:00:00Z"), "repositories" : [ DBRef("repos", "mongodb/morphia"), DBRef("repos", "evanchooly/morphia") ] }
Queries
Basic Query
Query<Repository> query = datastore.createQuery(Repository.class); Repository repository = query.get(); !runQuery called morphia-demo.repos {} !List<Repository> repositories = query.asList(); !runQuery called morphia-demo.repos {} !Iterable<Repository> fetch = query.fetch(); !… ?
Basic Query
Query<Repository> query = datastore.createQuery(Repository.class); query.field("owner").equal(evanchooly).get(); !runQuery called morphia-demo.repos { owner: { $ref: "users", $id: "evanchooly" } }
Complex Entities
@Entity("repos") public class Repository { @Id public String name; @Reference public Organization organization; @Reference public GithubUser owner; public Settings settings = new Settings(); }
Basic Query — @Property
@Entity(value = "users", noClassnameStored = true)
public class GithubUser {
@Id
private String userName;
private String fullName;
@Property("since")
private Date memberSince;
}
Basic Query — @Property
datastore.createQuery(GithubUser.class) .field("memberSince").equal(date).get(); !GithubUser{userName='evanchooly', fullName='Justin Lee', memberSince=Mon Jun 15 00:00:00 EDT 1987} !datastore.createQuery(GithubUser.class) .field("since").equal(date).get(); !GithubUser{userName='evanchooly', fullName='Justin Lee', memberSince=Mon Jun 15 00:00:00 EDT 1987}
Updates
Updating Entities
evanchooly.followers = 12; datastore.save(evanchooly);
Multiple Updates
UpdateOperations<GithubUser> update = datastore.createUpdateOperations( GithubUser.class) .inc("followers").set("following", 42); Query<GithubUser> query = datastore.createQuery(GithubUser.class) .field(“followers”) .equal(0); datastore.update(query, update); !update morphia-demo.users query: { followers: 0 } update: { $set: { following: 42 }, $inc: { followers: 1 } }
Versioned Updates
@Entity("orgs") public class Organization { @Id public String name; @Indexed public Date created; @Version(“v”) private long version; !… !}
Versioned Updates
Organization organization = datastore.createQuery(Organization.class).get(); Organization organization2 = datastore.createQuery(Organization.class).get(); !datastore.save(organization); // fine datastore.save(organization); // fine datastore.save(organization2); !java.util.ConcurrentModificationException: Entity of class org.mongodb.morphia.demo.Organization (id='mongodb',version='1') was concurrently updated.
Indexes
Indexes — Fields
@Entity("orgs") public class Organization { @Id public String name; @Indexed(value = IndexDirection.ASC, unique = false, name = "", dropDups = false, background = false, sparse = false, expireAfterSeconds = -1) public Date created; !… }
Indexes — Fields
repl0:PRIMARY> db.orgs.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "morphia-demo.orgs" }, { "v" : 1, "key" : { "created" : 1 }, "name" : "created_1", "ns" : "morphia-demo.orgs" } ]
Indexes — Classes
@Entity(value = "users", noClassnameStored = true) @Indexes({ @Index(value ="userName, -followers", name = "popular") }) public class GithubUser { @Id public String userName; public String fullName; @Property("since") public Date memberSince; public Date lastActive; @Reference(lazy = true) public List<Repository> repositories = new ArrayList<>(); public int followers = 0; public int following = 0; !…
What’s Next
The Road to 1.0
• It’s short
• Aggregation support
• Text Searching
• Improved geo support
• Handful of bug fixes
Resources
• Morphia Homepage https://github.com/mongodb/morphia
• Discussions Forum https://groups.google.com/forum/#!forum/morphia
• This presentation and code https://github.com/evanchooly/morphia-demo
Questions?
MongoDB Persistence with Java and Morphia
Software Engineer, MongoDB
Justin Lee