CRUD with Terrastore
Mats Henricson
Crisp AB
Why NoSQL?
NoSQL is interesting if you value:
- Scalability
- Robustness
- Performance
- Agile methods
Agile methods?
Who enjoys writing DAO classes?
Is it possible to not having to write tedious DAO classes with a NoSQL database?
The first NoSQL database I looked at was Cassandra.
How do you do CRUD with Cassandra?
Java API:s for Cassandra
- Hector
- Pelops
- HelenaORM (old)
- OCM (old)
- Datanucleus-Cassandra (old)
- Jassandra (old)
For Scala:
- Scromium
- Cascal
- Cassandra4o
- Cassie
Real example from the Scromium docs
ks.insert("its", "a" -> "stack" -> "of", "fuckshit")
Well... perhaps I should pick another API?
Pelops, Cassandra's beautiful son
There's almost no documentation...
How beautiful is that?
Hector
Pretty good documentation, but unfortunately very
low level.
To persist your POJO:s you'll have to write your own DAO classes,
by hand, much like you did with JDBC.
How agile is that? Answer: Not at all!
Perhaps a document database?
From NoSQL conference in London in April:
"Which NoSQL database should you choose?
Use a document database if you can get away with it."
NoSQL document databases
- CouchDB (written in Erlang)
- MongoDB (written in C++)
- RavenDB (written in C#)
- Terrastore (written in Java)
It turns out Terrastore has a very elegant Java API,
written by a Swede, Sven Johansson!
Terrastore!
- Built on top of Terracotta!
- HTTP and Java API
- Supports single-cluster and multi-cluster deployments
- Elastic: You can add and remove nodes dynamically
- Scalable: Automatic and transparent re-balancing
- Schemaless
- Easy to install and configure
Other features
- Custom data partitioning
- Event processing
- Range queries
- Server-side update functions
- Per-document consistency
Future releases
- Enhanced failure detection and handling
- Map/Reduce, maybe by integrating Hadoop
- Web console
So, what is a document?
For our purposes
- a POJO, or
- a smallish cluster of POJO:s
Terrastore guarantees to always give you the latest version of a single document!
Example
public class Teacher {
private String name;
// ...
}
public class Student {
private String name;
// ...
}
public class Course {
private String name;
private Teacher teacher;
private List<Student> students;
// ...
}
Example problem
public class Student {
private String name;
private List<Course> courses; // Would not work !!
// ...
}
public class Course {
private String name;
private Teacher teacher;
private List<Student> students;
// ...
}
A
Jackson bug prevents us from having cyclic references,
so beware!
Smallish clusters of POJO:s?
You store your domain objects into different buckets. Exemples:
- Customer data
- Item data
- Orders
If you need to link objects in different buckets,
then you have to use domain unique IDs.
Annotations? Base classes?
Nope. The POJO:s are plain vanilla vanilla POJO:s.
Buckets?
Yes, buckets.
One bucket for customer data.
One bucket for items.
One bucket for orders.
Buckets are identified by string names,
such as "customers", "items", "orders".
Buckets are comparable to RDBMS tables,
but with no schema!
Enough! I want an example, now!
First we need a terrastore client
TerrastoreClient client =
new TerrastoreClient("http://localhost:8080",
new HTTPConnectionFactory());
That localhost URL points to a terrastore
server.
If that server goes down, then your client is toast.
Future versions of the API will implement fallbacks.
Now lets create the POJO:s
Student plato = new Student("Plato");
Student camus = new Student("Camus");
Teacher socrates = new Teacher("Socrates");
Course philosophy = new Course("Philosophy");
// Set relationships
philosophy.setTeacher(socrates);
philosophy.addStudent(plato);
philosophy.addStudent(camus);
Create, Read and Delete
BucketOperation courses = client.bucket("courses");
// Create:
courses.key("philosophy").put(philosophy);
// Read:
Course p = courses.key("philosophy").get(Course.class);
// Important: p != philosophy
// Delete:
courses.key("philosophy").remove();
You can update a document with another put()
So, are we done?
No, there is a lot more cool stuff!
- It is safe to remove() non-existing documents
- Get for non-existing keys will throw KeyNotFoundException
- You can import and export whole buckets to file for backup
- You can get all documents of a specific type in a bucket
- You can get n documents of a specific type in a bucket
- You can get all documents of a specific type within a key range
XPath!
String xpath = "jxpath:/teacher/name[.='Socrates']";
Map<String, Course> philosophyCourses =
coursesBucket.predicate(xpath).get(Course.class);
assertTrue(philosophyCourses.containsValue(philosophy));
Internally using JXPath.