NoSQL databases with MongoDB

Một phần của tài liệu Manning spring roo in action (Trang 152 - 157)

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.

Một phần của tài liệu Manning spring roo in action (Trang 152 - 157)

Tải bản đầy đủ (PDF)

(406 trang)