Added in Roo 1.2 is support for NoSQL databases using the NoSQL Spring Data APIs.
NoSQL databases1 attempt to address a number of challenges inherent in using a rela
tional database, such as these:
Normalization kills query performance —Because relational data is normalized, or factored in to the least duplicated form, it can become hard to query across a number of entities. Consider the performance issues with querying an overly normalized database that leads to a crazy 10 table joins.
Rigid database structure—Relational databases require structure to perform properly. Each column must be defined with a specific type, and the database engine optimizes indexing and performance around this structure.
Difficulty querying large volumes of data quickly —Although some database vendors have rolled out text searching capabilities, and other developers are using tools such as Lucene to index their data, databases themselves can’t quickly search through large volumes of loosely structured text without scanning vast amounts of data. This makes them less than ideal for searching through content stored in XML or JSON format.
1 The term NoSQL was coined by Carlo Strozzi in 1998 as a name for his nonrelational, RDB derivative database.
This project is ongoing, and can be visited at http://www.strozzi.it.
For these and other reasons, the NoSQL movement was born. A number of types of NoSQL databases have been developed, but they generally fall into several types, as outlined in table 4.4.
Table 4.4 Several NoSQL database variants
Type Description Examples
Document store
Graph
Column store
Key/Value store
Each entry in a document store is a single document. Stored in a format such as JSON, XML, or even binary JSON (BSON), these data
bases are geared toward quickly searching content within the document stores and stor
ing internally structured data.
Data is related in a node-to-node graph struc
ture, and can be quickly navigated across these nonfixed relationships. Great for storing associations of data elements where the net
work of data can be reorganized quickly. A favorite of some semantic web developers.
Places focus primarily on the column, rather than collected rows of data. Powers huge web- sites such as Facebook, Twitter, Digg, and oth
ers. Google pioneered column-based storage with BigTable when indexing the internet through linear means proved too difficult.a If the world is a hashmap, this is your data
base. The objects can be structured in any way possible, such as primitive values or serialized objects, and are fetched by unique keys.
MongoDB, CouchDB, SimpleDB
Neo4J
Cassandra, HBase, BigTable
Redis, MemcacheDB
a. Facebook created Cassandra, a column store NoSQL database, and open sourced an implementation. See http://
mng.bz/5321.
We won’t get into the religious debates about which NoSQL variant or engine to choose in this book. But as Roo supports at least one (at the time of publication) NoSQL database, we will show you how to use it as a database store.
4.7.1 Persistence with MongoDB
As discussed earlier, document store NoSQL databases treat each element as a search
able document. Roo supports accessing these databases using the Spring Data MongoDB API (http://mng.bz/rLm9), which is part of the larger Spring Data proj
ect. To use this data store, you’ll need to install a MongoDB engine, configure it, and then use a variant of the entity command to create your Mongo-based entities.
Native MongoDB data is stored in a JSON data format, and can be accessed using a number of APIs. The Mongo default client uses JavaScript. Here’s a sample code snip
pet from this client that creates and then retrieves a document from the data store:
121 NoSQL databases with MongoDB
> db.people.save( { ... "name" : "Ken", ... "age" : 42,
... "city" : "Philadelphia"
... })
> db.people.find({ "name" : "Ken"})
{ "_id" : ObjectId("4e7df1f0a3c4b6fc99496731"),
"name" : "Ken", "age" : 42, "city" : "Philadelphia" }
The first block of code creates a new element in the newly defined db.people object.
The data held within it is defined as a JSON string. Later, in the second statement, we use a fragment of that string, { "name" : "Ken" }, to find all of the people with the name of Ken. We didn’t have to predefine the object, or field definitions, or even the database. MongoDB just took our data and stored it.
4.7.2 Setting up MongoDB
Let’s get started and set up MongoDB. Like other servers, you’ll install the database engine and client by downloading it and configuring it as a standalone service.
To install MongoDB, visit the project website at http://mongodb.org and down
load the server for your operating system. We’ve used the OS X utility, brew,2 to install it on our machines with a minimum of fuss. You’ll also have to configure an empty database directory, /data/db by default. We recommend reading and using the “Get
ting Started” guide and the basic tutorial to get a sense of how to start up the server, mongod, and how to access the client, mongo. After you’ve created several documents and experimented with the client, you should be ready to build your first MongoDB
based application.
Our tutorial assumes that you’ve configured a working MongoDB database and that your mongod daemon process is running when you work with MongoDB and Roo.
4.7.3 MongoDB and Roo
Obviously, Java programmers need a bit more structure than that. First of all, they deal mostly with classes in the real world, so one way of approaching a MongoDB imple
mentation would be to serialize and deserialize JSON using a Java POJO.
That’s where Roo’s support for the Spring Data MongoDB API comes in. Instead of using a relational data store and the @Entity pattern, Roo uses a new annotation, @Roo- MongoEntity, which is configured when using the entity mongo shell command. To build your course with Mongo support, you’d first set up your MongoDB database layer:
roo> mongo setup
This configures support for MongoDB, assuming that the engine is running on the same machine, on the default port. It also assumes that security is not enabled. You can use parameters to adjust your MongoDB settings, or edit them later in database .properties.
2 brew installs MongoDB. That’s it. Really.
USING CLOUD FOUNDRY? If you’re using Cloud Foundry, use the --cloud- Foundry true option to configure support for the Cloud Foundry MongoDB instance.
After your configuration is updated, you’ll see a new configuration file, application
Context-mongo.xml, which contains the Spring Data Mongo API configuration ele
ments. Now you’re ready to create MongoDB-based entities.
4.7.4 A MongoDB Course entity
Let’s build a Course object using MongoDB. You’ll start by defining the entity with the new entity mongo shell command:
entity mongo --class ~.model.Course --testAutomatically
Roo responds by building your MongoDB entity:
package org.rooinaction.coursemanager.model;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.layers.➥
repository.mongo.RooMongoEntity;
import org.springframework.roo.addon.tostring.RooToString;
@RooJavaBean
@RooToString
@RooMongoEntity public class Course { }
Comparing this entity with the others, the major difference is the @RooMongoEntity annotation. Beyond this, you can treat it the same way as your other entities. Let’s add some fields and a relationship to a simplified Offer object. First, the Course fields:
private String name;
private String description;
private BigDecimal listPrice;
private Integer maximumCapacity;
private CourseTypeEnum courseType;
private Set<Offering> offerings = new HashSet<Offering>();
Next, you’ll define your Offering POJO, for embedding within your Course:
package org.rooinaction.coursemanager.model;
@RooJavaBean
@RooToString
public class Offering { private Date offerDate;
}
Note, this is just a simple Java POJO which you’ll embed into your database.
NoSQL databases with MongoDB 123
4.7.5 Generating a Course MongoDB repository
To enable access to your Roo MongoDB entity, you need to build a repository. You can do this via the repository mongo command:
roo> repository mongo --entity ~.model.Course ➥
--interface ~.repositories.CourseRepository
This command will build a CourseRepository interface and build an ITD, CourseRepository_Roo_Mongo_Repository.aj, to back it. The repository definition:
@RooRepositoryMongo(domainType = Course.class) public interface CourseRepository {
List<org.rooinaction.coursemanager.model.Course> findAll();
}
The other methods, such as find(BigInteger), save(Course), and update(Course) are provided by the ITD, which uses Spring Data MongoDB calls to perform the persis
tence activities. These calls are enabled by the annotation @RooRepositoryMongo.
They’re similar in feature to the JPA repository CRUD methods. But they act on a NoSQL database, so the method signatures aren’t an exact match. You should thor
oughly research MongoDB before writing an application that uses it for persistence.
You can then execute code against the repository, as in this test, which uses a method, createCourseWithOffering(), to create the Course and a single offering:
@Autowired
private CourseRepository courseRepository;
...
@Test
public void testPersistCourseUsingRepository() { Course course = createCourseWithOffering();
courseRepository.save(course);
Assert.assertTrue(courseRepository.count() > 0);
Course course2 = courseRepository.findOne(course.getId());
Assert.assertEquals( course.getId(), course2.getId());
Assert.assertEquals(1, course.getOfferings().size());
}
4.7.6 Creating a service for your MongoDB repository
Creating a service is the same procedure, whether you are using a JPA or MongoDB repository. Simply issue the service command:
roo> service --interface ~.service.CourseService ➥
--entity ~.model.Course
If you aimed your tests against the service, your methods would look similar to the SQL-based service wrapper methods. Roo tries to normalize the method names a bit.
Here’s the same test, but focused on a service instead:
@Autowired
private CourseService courseService;
@Test
public void testPersistCourseWithOffering() { Course course = createCourseWithOffering();
courseAndOfferingService.saveCourse(course);
Assert.assertTrue (courseAndOfferingService.countAllCourses() > 0);
Course course2 = courseAndOfferingService.findCourse(course.getId());
Assert.assertEquals( course.getId(), course2.getId());
Assert.assertEquals(1, course.getOfferings().size());
}
The major differences between NoSQL and regular database services are
NoSQL databases don’t participate in transactions. The @Transactional anno
tation is ignored for methods called by Spring Data NoSQL stores.
Tests don’t clean up after themselves. You’ll need to use JUnit annotations such as @Before and @After to remove or reset data.
Relationships will behave differently and may need special supporting code.
The example above just nests an Offering POJO within each Course, and doesn’t attempt to create a true two-way relationship similar to JPA.
This feature is quite new, and may change in the future. But SpringSource is commit
ted to the Spring Data API and to supporting both SQL and non-SQL data stores. For more information, please refer to the Spring Data project and the various subprojects for databases of interest.