Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of NoSQL data Modeling

2025-02-14 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)05/31 Report--

This article will explain in detail the example analysis of NoSQL data modeling for you. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.

NoSQL: a new way of thinking?

When developers talk about non-relational or NoSQL databases, the first thing they often mention is that they need to change their way of thinking. I think that really depends on your initial data modeling approach. If you are used to designing your application by modeling the database structure first (that is, determining the tables and their relationships first), modeling data using a schemaless data store, such as Bigtable, requires you to rethink the way you do things. However, if you start designing your application from the domain model, the schemaless structure of Bigtable will look more natural.

Non-relational data stores do not join tables or primary keys, or even the concept of foreign keys (although these two types of keys appear in a more loose form). Therefore, if you try to use relational modeling as the basis for data modeling in an NoSQL database, you may end up failing. Starting with the domain model will make things easier; in fact, I have found that the flexibility of the schemaless structure under the domain model is coming back to life.

The relative complexity of migrating from a relational data model to a schemaless data model depends on your approach: that is, do you start with relational or domain-based design. When you migrate to a database like CouchDB or Bigtable, you do lose the smoothness of a mature persistent storage platform like Hibernate (at least for now). On the other hand, you have the "greenfield effect" that you can build it yourself. In the process, you will learn more about schemaless data storage.

Entity and relationship

Schemaless data storage gives you the flexibility to first use objects to design domain models (this flexibility is automatically supported by newer frameworks such as Grails). Your next step is to map your domain to the underlying data store, which couldn't be easier when using Google App Engine.

In the article "Java Development 2.0: Gaelyk for Google App Engine," I introduced Gaelyk, a Groovy-based framework that facilitates the underlying data storage using Google. The main part of that article focuses on how to take advantage of Google's Entity object. The following example (from that article) shows how object entities work in Gaelyk.

Listing 1. Object persistence using Entity

Def ticket = new Entity ("ticket") ticket.officer = params.officer ticket.license = params.plate ticket.issuseDate = offensedate ticket.location = params.location ticket.notes = params.notes ticket.offense = params.offense

This method of object persistence is effective, but it's easy to see that if you use bill entities frequently-for example, if you're creating (or looking for) them in various servlet, it becomes boring. Using a public servlet (or Groovlet) to handle these tasks for you will eliminate some of these burdens. A more natural choice-- I'll show you later-- would be to model a Ticket object.

Return to the game

I won't repeat the ticket example in the introduction to Gaelyk. Instead, to keep it fresh, I'll use a race theme in this article and build an application to demonstrate the technology to be discussed.

As shown in the many-to-many chart in figure 1, a Race has multiple Runner, and a Runner can belong to multiple Race.

Figure 1. Competition and contestants

If I were to use a relational table structure to design this relationship, I would need at least three tables: table 3 would be a join table that links a many-to-many relationship. Fortunately, I don't have to confine myself to the relational data model. Instead, I'll use Gaelyk (and Groovy code) to map this many-to-many relationship to Google's Bigtable abstraction for Google App Engine. In fact, Gaelyk allows you to think of Entity as a Map, which makes the mapping process quite simple.

One of the benefits of schemaless data storage is that you don't have to know everything in advance, that is, you can adapt to change more easily than using a relational database architecture. (note that I'm not implying that the architecture cannot be changed; I'm just saying that it's easier to adapt to change. I'm not going to define properties on my domain objects-I defer it to the dynamic nature of Groovy (in fact, this feature allows the creation of domain object proxies for Google's Entity objects). Instead, I will spend my time determining how to find objects and deal with relationships. This is NoSQL and the various frameworks that take advantage of schemaless data storage do not have built-in functionality.

Model base class

I'll start by creating a base class that holds an instance of the Entity object. Then, I will allow some subclasses to have dynamic properties that will be added to the corresponding Entity instance through Groovy's convenient setProperty method. SetProperty is called by the setter for any property that does not actually exist in the object. If this sounds sensational, don't worry, you'll understand when you see it actually running. )

Listing 2 shows the first stab of a Model instance in my sample application:

Listing 2. A simple Model base class

Package com.b50.nosql import com.google.appengine.api.datastore.DatastoreServiceFactory import com.google.appengine.api.datastore.Entity abstract class Model {def entity static def datastore = DatastoreServiceFactory.datastoreService public Model () {super ()} public Model (params) {this.@entity = new Entity (this.getClass () .simpleName) params.each {key, val-> this.setProperty key Val} def getProperty (String name) {if (name.equals ("id")) {return entity.key.id} else {return entity. "${name}"}} void setProperty (String name, value) {entity. "${name}" = value} def save () {this.entity.save ()}}

Notice how the abstract class defines a constructor that takes a Map of the property-- I can always add more constructors later, as I'll do later. This setting is handy for Web frameworks, which typically take parameters submitted from the form. Gaelyk and Grails subtly encapsulate such parameters into an object called params. This constructor iterates over the Map and calls the setProperty method for each key / value pair.

Check the setProperty method and you will find that the "key" is set to the property name of the underlying entity, and the corresponding "value" is the value of the entity.

Groovy skills

As mentioned earlier, the dynamic nature of Groovy allows me to capture method calls to properties that do not exist through the get and set Property methods. In this way, the subclasses of Model in listing 2 do not have to define their own properties-they simply delegate all calls to a property to the underlying entity object.

The code in listing 2 performs some Groovy-specific operations, which is worth mentioning. First, you can bypass the accessor method of an attribute by appending an @ to it. I must do this on the entity object reference in the constructor, or I will call the setProperty method. Obviously, calling setProperty at this juncture will break this pattern, because the entity variable in the setProperty method will be null.

Second, the call this.getClass (). SimpleName in the constructor sets the "category" of the entity-- the simpleName property generates a subclass name without a package prefix (note that simpleName is indeed a call to getSimpleName, but Groovy allows me to try to access a property without the corresponding JavaBeans-style method call).

Finally, if you make a call to the id property (that is, the key of the object), the getProperty method is smart enough to ask the underlying key to get its id. In Google App Engine, the key property of entities is automatically generated.

Race subclass

Defining the Race subclass is simple, as shown in listing 3:

Listing 3. A Race subclass

Package com.b50.nosql class Race extends Model {public Race (params) {super (params)}}

When a subclass is instantiated with a list of parameters (that is, a Map containing multiple key / value pairs), a corresponding entity is created in memory. To persist it, simply call the save method.

Listing 4. Create an instance of Race and save it to GAE's data store

Import com.b50.nosql.Runner def iparams = [:] def formatter = new SimpleDateFormat ("MM/dd/yyyy") def rdate = formatter.parse ("04 iparams 17 date 2010") iparams ["name"] = "Charlottesville Marathon" iparams ["date"] = rdate iparams ["distance"] = 26.2 as double def race = new Race (iparams) race.save ()

Listing 4 is a Groovlet where a Map (called iparams) is created with three attributes-- the name, date, and distance of a match. Note that in Groovy, a blank Map is created with [:]. A new instance of Race is created and then stored in the underlying data store through the save method

You can view the underlying data store through the Google App Engine console to make sure my data is there, as shown in figure 2:

Figure 2. View the newly created Race

Find the program method to generate persistent entities

Now that I have stored an Entity, it would be helpful to have the ability to find it. Next, I can add a "finder" method. In this case, I will create this "finder" method as a class method (static) and allow these Race to be found by name (that is, search based on the name attribute). Later, you can always add additional finders through other properties.

I also intend to adopt a convention for my finder, which specifies that any finder without the word all in its name attempts to find an instance. A finder with the word all in its name, such as findAllByName, can return an instance of Collection or List. Listing 5 shows the findByName finder:

Listing 5. A simple search program based on Entity name search

Static def findByName (name) {def query = new Query (Race.class.simpleName) query.addFilter ("name", Query.FilterOperator.EQUAL, name) def preparedQuery = this.datastore.prepare (query) if (preparedQuery.countEntities () > 1) {return new Race (preparedQuery.asList (withLimit (1)) [0])} else {return new Race (preparedQuery.asSingleEntity ())}

This simple finder uses Google App Engine's Query and PreparedQuery types to find an entity of type "Race" whose name is (exactly) equivalent to the name passed in. If more than one Race meets this criterion, the finder returns the first item of a list, as specified by the paging limit 1 (withLimit (1)).

The corresponding findAllByName is similar to the method above, but adds a parameter that specifies the number of entities you want, as shown in listing 6:

Listing 6. Find all entities by name

Static def findAllByName (name, pagination=10) {def query = new Query (Race.class.getSimpleName ()) query.addFilter ("name", Query.FilterOperator.EQUAL, name) def preparedQuery = this.datastore.prepare (query) def entities = preparedQuery.asList (withLimit (pagination as int)) return entities.collect {new Race (it as Entity)}}

Similar to the finder defined earlier, findAllByName finds the Race instance by name, but it returns all Race. By the way, Groovy's collect method is very flexible: it allows you to delete the corresponding loop that creates the Race instance. Note that Groovy also supports default values for method parameters; this way, if I don't pass in the second value, pagination will have a value of 10.

Listing 7. Find the actual operation of the program

Def nrace = Race.findByName ("Charlottesville Marathon") assert nrace.distance = 26.2def races = Race.findAllByName ("Charlottesville Marathon") assert races.class = = ArrayList.class

The finder in listing 7 works the way it is intended: findByName returns an instance, while findAllByName returns a Collection (assuming multiple "Charlottesville Marathon").

The objects of "contestants" are not much different.

Now that I can create and find an instance of Race, I can create a quick Runner object. This process is as simple as creating an initial Race instance by extending Model as shown in listing 8:

Listing 8. It is easy to create a contestant

Package com.b50.nosql class Runner extends Model {public Runner (params) {super (params)}}

Looking at listing 8, I feel like I'm almost done. However, I also need to create a link between the contestant and the competition. Of course, I will model it as a many-to-many relationship, because I want my contestants to participate in multiple competitions.

Domain modeling without architecture

Google App Engine's abstraction on top of Bigtable is not an object-oriented abstraction; that is, I can't store relationships as is, but I can share keys. Therefore, to model the relationship between multiple Race and multiple Runner, I will store a column of runner keys in each Race instance and a column of Race keys in each Runner instance.

I have to add a little logic to my key sharing mechanism, but because I want the generated API to be natural-I don't want to ask a Race to get a list of runner keys, so I want a list of Runner. Fortunately, this is not difficult to achieve.

In listing 9, I have added two methods to the Race instance. But when an instance of Runner is passed to the addRunner method, its corresponding id is added to the Collection of the id that resides in the runners property of the underlying entity. If there is a ready-made collection for runners, a new Runner instance key will be added to it; otherwise, a new Collection will be created and the key of this Runner (the id attribute on the entity) will be added to it.

Listing 9. Add and retrieve contestants

Def addRunner (runner) {if (this.@entity.runners) {this.@entity.runners

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Database

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report