Red Hat

In Relation To Hibernate OGM

In Relation To Hibernate OGM

It's my great pleasure to announce the release of Hibernate OGM 4.1.0.Beta3. This release is focused on an improved experience when working with the Neo4j graph datastore and several improvements in the field of querying (the complete change log can be found here).

As always, you can either download a release bundle from SourceForge or retrieve the JARs from the JBoss Nexus repository server using Maven, Gradle etc. The GAV coordinates are:

  • org.hibernate.ogm:hibernate-ogm-core:4.1.0.Beta3 for the Hibernate OGM engine and
  • org.hibernate.ogm:hibernate-ogm-<datastore>:4.1.0.Beta3, depending on the backend you want to use.

Improved support for Neo4j

We now take advantage of labels - a new feature of Neo4J 2.x - to map entities in a more natural way. We use these labels to tag all the nodes belonging to one entity type. This does not only remove the need to put additional properties to the nodes but also will allow us to query for all the nodes of one entity type in an efficient manner.

We also support the execution of native Cypher queries now. You can either create them ad-hoc via EntityManager#createNativeQuery() or using the @NamedNativeQuery annotation on your entities:

@Entity
@NamedNativeQuery(
    name = "AnimalsBySpecies",
    query = "MATCH ( n: Animal { species: {species} } ) RETURN n",
    resultClass = Animal.class 
)
public class Animal { ... }
    
EntityManager em = ...
List<Animal> giraffes = em.createNamedQuery( "AnimalsBySpecies", Animal.class )
    .setParameter( "species", "Giraffe" )
    .getResultList();

As you can see in the example, native queries for Neo4j also support named parameters via the native Cypher parameter syntax.

The work on the Neo4j dialect is in full swing these days and you can expect to see further improvements in this field in the Beta4 release.

Query improvements

When working with queries you can now make use of setFirstResult() and setMaxResults() to page through a result set. This works for JP-QL as well as native MongoDB queries:

EntityManager em = ...;
List<Animal> giraffes = em.createQuery( "FROM Animal WHERE species = 'Giraffe'" )
    .setFirstResult( 51 )
    .setMaxResults( 100 )
    .getResultList();

Furthermore there is support for ORDER BY clauses in JP-QL queries now:

List<Animal> giraffes = em.createQuery( "FROM Animal WHERE species = 'Giraffe' ORDER BY name DESC" )
    .getResultList();

These two things together should be of great help when using Hibernate OGM to implement typical CRUD use cases (create, read, update, delete) on top of your NoSQL store.

What's next?

For Beta4 we plan to translate JP-QL queries into native Cypher queries for the Neo4j backend. We also got some ideas for performance improvements and will take a look into a more natural mapping of assocations to Neo4j relationships.

The MongoDB dialect should see some improvements around querying as well and we plan on investigating an error report and compensation API. This will offer the ability to collect errors on partially executed units of work on stores without full transactional semantics and the ability to react to these errors.

Needless to say, your feedback matters very much to us. So you're very welcome to raise your voice on the mailing list, ask questions in the forum or report any bugs or feature requests in the issue tracker.

Hibernate OGM 4.1 Beta2: WildFly and JBoss EAP modules

Posted by    |       |    Tagged as Hibernate OGM

Time for a new release of Hibernate OGM.

On the new feature side, we have added integration modules for both:

But the bulk of the work has been around polishing and fixing issues based on your feedback. In a nutshell, we polished MongoDB's support (security, native queries) and Neo4J. We fixed a few bugs around collections and query support. We kept improving the option framework which we use to define backend specific behaviors.

How to try it

You can take a look at the documentation or check how you can download Hibernate OGM 4.1.0.Beta2.

Many thanks to all the contributors that made this release possible whether it be via pull requests, bug reports or feedback on the forum.

Hibernate OGM 4.1.0.Beta1 is out

Posted by    |       |    Tagged as Hibernate OGM

It's my great pleasure to announce the release of Hibernate OGM 4.1.0.Beta1! This version shines with:

  • support for CouchDB
  • query execution via JPA
  • new Option API
  • and much more, including a version bump

But wait, hasn't the last released version been 4.0.0.Beta4? That's true indeed, and in case you were wondering, we did not forget to do a final release of the 4.0 line. The reason for the version bump is that Hibernate OGM is now compatible with JPA 2.1 and Hibernate ORM 4.3. To reflect this we thought it'd be a good idea to jump to a new minor version as well.

As usual, you can get the release from SourceForge (ZIP, TAR.GZ) or retrieve it from the JBoss Nexus repository server using your favorite dependency management tool. The GAV coordinates are:

  • org.hibernate.ogm:hibernate-ogm-core:4.1.0.Beta1 for the OGM engine and
  • org.hibernate.ogm:hibernate-ogm-<datastore>:4.1.0.Beta1, depending on the backend you want to use.

Note that we did move some packages around, so make sure to look at our migration guide.

Enough of the introductory words, let's have a closer look at some of the new features.

Support for CouchDB

Based on a huge contribution of community member Andrea Boriero (thanks again, Andrea!), Hibernate OGM comes now with support for Apache CouchDB. CouchDB is a scalable document datastore which persists data as JSON documents and offers an HTTP API, fully embracing REST principles.

The storage strategy of the new dialect resembles that used for MongoDB. In particular we aimed for a very natural mapping of entities to documents in the datastore. E.g. properties are mapped as document fields, embeddables are mapped as nested documents etc. We're also leveraging CouchDB's built-in optimistic locking mechanism to detect concurrent updates, just as you would expect it.

The listing belows shows an entity and how it is represented as JSON document in CouchDB:

@Entity
public class News {

    @Id
    private String id;

    @Version
    @Generated
    @Column(name="_rev")
    private String revision;

    private String title;

    @Column(name="desc")
    private String description;

    //getters, setters ...
}

# JSON document
{
    "_id": "News:id_:news-1_",
    "_rev": "1-d1cd3b00a677a2e31cd0480a796e8480",
    "$type": "entity",
    "$table": "News",
    "title": "On the merits of NoSQL",
    "desc": "This paper discuss why NoSQL will save the world for good"
}

As you can see, the @Id attribute is mapped to CouchDB's reserved _id field, the same applies for the revision attribute which is mapped to _rev. The @Version and @Generated annotations advice OGM to handle the revision property as version attribute and use it for optimistic concurrency control. The @Column annotation can be used to map a property to a document field with another name.

To learn more about the CouchDB dialect and how to use it, refer to the reference guide. Note that the dialect is considered experimental at this time, so don't expect everything to work perfectly yet. Of course any feedback is highly welcome; maybe you even want to tackle one of the open issues) in this field?

Query improvements

We're very pleased to have progressed in the field of queries, addressing several long standing feature requests.

It's possible now to issue JP-QL queries via the EntityManager API which is great news for you if you're preferring JPA over using the native Hibernate API:

EntityManager em = ...;
List<Animal> giraffes = em.createQuery( "FROM Animal WHERE species = :species" )
    .setParameter( "species", "Giraffe" )
    .getResultList();

You also can work with named queries now:

@Entity
@NamedQuery(name = AnimalQueries.BY_SPECIES, query = "FROM Animal WHERE species = :species")
public class Animal { ... }

EntityManager em = ...;
List<Animal> giraffes = em.createNamedQuery( AnimalQueries.BY_SPECIES, Animal.class )
    .setParameter( "species", "Giraffe" )
    .getResultList();

Depending on the chosen backend, these queries will either be translated into Lucene Queries via Hibernate Search or into native queries of the backend (currently the case for MongoDB).

Option API

One big challenge Hibernate OGM has to deal with is how to expose store-specific functionality and configuration options in case the JPA semantics are not sufficient. Taking MongoDB as example, you might want to configure the write concern or a strategy for persisting association information.

Hibernate OGM now provides an extendible, type-safe and comprehensible mechanism for declaring such options. You can use annotations and a programmatic API. Dialect authors can plug in store-specific configuration options very easily, allowing users to specify these options in an intuitive and type-safe fashion. The following shows an example:

@Entity
@AssociationStorage(AssociationStorageType.ASSOCIATION_DOCUMENT)
public class Zoo {

    @OneToMany
    private Set<Animal> animals;

    @OneToMany
    private Set<Person> employees;

    @OneToMany
    @AssociationStorage(AssociationStorageType.IN_ENTITY)
    private Set<Person> visitors;

    //...
}

The @AssociationStorage annotation on the entity level expresses that all associations of the Zoo class should be persisted as separate association documents. Only the visitors association will be stored embedded within the corresponding Zoo document, as the local @AssociationStorage annotation takes precedence.

We try to share such configuration options between stores of one kind where feasible. E.g. the @AssocationStorage annotation is applicable to all document datastores (while e.g. an option such as @WriteConcern would be specific to MongoDB), simplifying the migration between stores. Neat, isn't it?

Now let's have a look at how you'd apply the same configuration using the programmatic API:

public class MyOptionConfigurator extends OptionConfigurator {

    @Override
    public void configure(Configurable configurable) {
        configurable.configureOptionsFor( MongoDB.class )
            .associationStorage( AssociationStorageType.ASSOCIATION_DOCUMENT )
            .entity( Zoo.class )
                .associationStorage( AssociationStorageType.IN_ENTITY )
                .property( "visitors", ElementType.FIELD )
                    .associationStorage( AssociationStorageType.ASSOCIATION_DOCUMENT )
                    .associationDocumentStorage( AssociationDocumentType.COLLECTION_PER_ASSOCIATION );
    }
}

All you need to do is to create an OptionConfigurator implementation, which provides access to the fluent configuration API. You can apply settings on a global level and then use entity() and property() to navigate to single entities and properties and apply options to the same. Again you can declare options specific to the store as well as options common to the family of store. Just don't forget to register your configurator when bootstrapping Hibernate OGM.

The work on options has been mainly behind the scenes in this release, with the shown options only being the first few examples for leveraging this new mechanism. You can expect to see more options specific to single stores or store families in future releases, providing you with all the flexibility you need to configure your datastore.

Streamlined configuration properties

The new option mechanism is an elegant way for specifying many options in a type-safe way. But plain properties in persistence.xml are just more appropriate for settings such as the host name of the store, user name etc. When working on the option API, it became apparent that several of our backend modules had the same or very similar configuration properties, but partly with different names and adhering to different naming conventions. So we took the chance to clean up the mess and re-organized the properties.

Properties common to all/most stores are named hibernate.ogm.datastore.* now, so e.g. there is hibernate.ogm.datastore.host, hibernate.ogm.datastore.username, hibernate.ogm.datastore.database etc. Properties specific to a single store are named hibernate.ogm.<datastore>.*, e.g. hibernate.ogm.mongodb.connection_timeout. Note that when programmatically bootstrapping a session factory or entity manager factory, you should refer to the properties using the constants declared on OgmProperties, InfinispanProperties etc.

While this change greatly increases consistency and removes redundancies, it requires existing applications to be adapted. So check out the reference guide to make sure you use the right names.

As always, your feedback around the new release is highly welcome. The complete change log can be found here. You can file bugs or feature requests in the issue tracker, ask questions in the forum or discuss ideas around the development of Hibernate OGM on our mailing list.

Hibernate OGM 4.0.0.Beta4 is out

Posted by    |       |    Tagged as Hibernate OGM

After one month from the previous release we are happy to announce the new Hibernate OGM 4.0.0.Beta4.

Initial embedded Neo4j integration

Hibernate OGM can now work with Neo4j, a fully transactional property graph database.

Graph databases represent the data as a system of interconnected nodes and work well when you need to store information like the relationship between people in social networks. In addition, in a property graph, you can also add properties to the elements of the graph.

Hibernate OGM maps an entity as a node where the attributes of the entity become properties of the node. At the moment we add some additional properties for the conversion of the node into the entity but we are planning to replace them using the label mechanism added in the latest Neo4j versions.

The relationship between two nodes represents an association between two entities. Currently a bidirectional association requires two relationships but we are going to change this since Neo4j can navigate a relationship in both directions.

The integration with Neo4j is experimental but we are planning on improving it soon. Please, let us know what you think or help us improving it.

Native query support for MongoDB

One missing feature in the previous releases was the ability to retrieve managed entities using the query language of the database of your choice. This is particularly useful if the query language supports specific features unavailable to JP-QL or currently non implemented.

We started to work on this feature and it is now possible to execute a MongoDB query using the org.hibernate.Session or the javax.persistence.EntityManager.

Let's look at an example using the session:

List<OscarWildePoem> result = session
    .createSQLQuery( "{ $query : { author : 'Oscar Wilde' }, $orderby : { name : 1 } }" )
    .addEntity( OscarWildePoem.TABLE_NAME, OscarWildePoem.class )
    .list();

and one using the entity manager:

List<OscarWildePoem> results = entityManager
    .createNativeQuery( "{ $query : { author : 'Oscar Wilde' }, $orderby : { name : 1 } }", OscarWildePoem.class )
    .getResultList();

How to try it

You can take a look at the documentation or check how you can download Hibernate OGM 4.0.0.Beta4.

Many thanks to all the contributors that made this release possible whether it be via pull requests, bug reports or feedback on the forum.

Hibernate OGM 4 beta 3 is out

Posted by    |       |    Tagged as Hibernate OGM

We have been very quiet on the announcement front for Hibernate OGM, but don't be fooled. Davide and Gunnar have been very busy bees and did bring significant foundation work for features that you will see in the next few months.

Improved query support around projection and operators

Based on the foundation of our JP-QL parser, we have added support for more JP-QL queries:

Supported operators

  • operators =, <, <=, >, =, IN, LIKE, BETWEEN
  • Supported boolean combinations: AND, OR, NOT
  • Support for simple property projections and embedded objects

In practice, that means that you can write the following JP-QL query and have it run for example on Infinispan.

select u.firstname, u.lastname from User u where (u.age BETWEEN 12 AND 18) OR u.supervision = 'free'

As a reminder, we now support for following query features:

  • operators =, <, <=, >, =>, IN, LIKE, BETWEEN
  • boolean combinations: AND, OR, NOT
  • simple proprerty projections
  • embedded objects
  • parametric queries

JP-QL query support for MongoDB

We now convert JP-QL queries into MongoQL queries and run them to retrieve entities. This is only the beginning and our support is comparable to the Lucene support.

This is a significant step forward for us as we proved our tool chain can handle what we are planning.

Discriminator based hierarchy

OGM now supports the single table per class strategy for the mapping of hierachies. In this case a column will be added to differentiate between entity types in a hierarchy. Previously, you had to store subclasses in different tables.

JBoss AS 7 integration module

JBoss AS comes with a module system offering better packages and isolated libraries. We have added Hibernate OGM as a module to simplify JBoss AS users.

Support for Wildfly is coming soon.

Support for Hibernate Search mass indexing

When using Hibernate Search in conjunction with Hibernate OGM, you could not index entities using the MassIndexer.

This is now fixed and we are back to an efficient indexing.

Next

Expect more regular releases from us from now on. We forgot our release early, release often mantra but that has to stop!

As an example, expect support for Neo4J as well as the ability to pass native queries (for example to MongoDB) and retrieve managed objects.

How to try it

You can take a look at the documentation or check how you can download Hibernate OGM 4.0.0.Beta3.

Many thanks to all the contributors that made this release possible whether it be via pull requests, bug reports or feedback on the forum.

Past week I returned from my trip to Bengaluru, where we had one of our great developers conferences.

JUDCon India 2013

As always at these events the best part was the people attending: a good mix of new users and experts, but all having in common a very healthy curiosity and not intimidated at all, so proposing a terrific amount of questions, discussions and for my long trip home a lot of things to think about.

Presentations

I had the honor to present several topics:

  • Hibernate Search: queries for Hibernate and Infinispan
  • Infinispan in 50 minutes
  • Cross data center replication with Infinispan
  • Measuring performance and capacity planning for Data Grids
  • Participating on the JBoss experts panel

The talk about Hibernate Search was a last minute addition: by shuffling the agenda a bit we could insert the additional subject and given the amount of nice feedback I'm happy we did.

The big denormalization problem

An expert Hibernate Search user asked me what would happen when having a domain model connecting User types to Addresses, when you have many Users and the city name changes. He actually knew what would happen, but was looking for alternatives to compensate for the problem; since Lucene requires denormalization, all User instances in the Lucene index need to be updated, triggering a reload of all Users living in the particular city. Yes that might be a problem! But that is not something happening frequently in model schemas right? I stated that in this example, it would take a city to change name! Well that caused a good amount of laugher as Bangalore just changed it's official name to the old traditional Bengaluru.. so since they where using Hibernate Search and this was an unexpected behaviour when the city changed name - having more than 8 million inhabitants - the public registry had some servers working very hard!

Obviously this needed specific testing and possibly better warnings from out part. Such problems are a natural consequence of denormalization and need to be addressed with ad-hoc solutions; in this case I'd suggest using a synonym and register the two names as same in the context of searching by configuring the Synonym support in the used Analyzer: the city name would need a single record change in the database and no reindexing would be needed.

Hibernate OGM

While I'm part of the OGM team, I had no need to talk about OGM as well because there where other speakers on the subject already. I greatly enjoyed listening to the other presentors, Ramya Subash and Shekhar Gulati: they where extremely well prepared and even with the most complex questions there was no need for me to help out.

To all attending and especially all those I've been talking to, thank you so much it was very interesting and I very much appreciate all the feedback. As always feel free to get more questions flowing on our Hibernate forums or Infinispan forum, and you're all welcome to participate more by sending tests or patches.

We have been quiet for too long on Hibernate OGM. It is time for us to speak up because we have a lot to show. And to celebrate, we are releasing its first beta.

What is Hibernate OGM

Hibernate OGM is for NoSQL datastores what Hibernate ORM is for relational databases. A way to persist your objects into a native datastore. The good thing is that Hibernate OGM aims at supporting all of JPA features (and the Hibernate native APIs):

  • all the mapping annotations, not a selected few
  • the APIs
  • the semantic (cascade etc)
  • and JP-QL

Work on the NoSQL side

We initially started with Infinispan as single backend and we worked our way to abstract it to support more:

Infinispan

We did not stand still on Infinispan. We greatly improved the backend in particular by using the FineGrainedAtomicMap class to store data and benefit from its increased scalability. See more in the performance section later on.

Ehcache

Ehcache was the first to jump on the wagon and propose an alternative backend implementation. Kudos to Alex Snaps and Greg Luck for taking this bet and help us improve our abstraction API.

MongoDB

MongoDB was the first non key-value based NoSQL solution we supported and we worked very hard to make sure the mapping was akin to what a developer would have designed without Hibernate OGM. For example, identifiers are mapped to the _id property and association information is embedded in the entity by default.


                
    {
        "_id" : "owner0001",
        "name": "Emmanuel",
        "twitter": "emmanuelbernard",
        "bankAccounts" : [
            { "bankAccounts_id" : "accountXYZ" }
        ]
    }

                
                
                

You can override that setting with various other mapping strategies.

We have more ideas we want to implement to optimize the Hibernate OGM - MongoDB integration and we will be working with folks like Jeff Yemin from 10gen to make sure we do the most natural mapping for MongoDB and that our access layer is as efficient as possible.

Note that we have decided to enable the MongoDB safe mode by default in Hibernate OGM as we thought it made more sense for models persisted with Hibernate OGM and because we don't want to be responsible for killing kittens. You can switch this back off of course.

Other NoSQL engines

Expect the list of supported NoSQL engines to grow as we continuously strive to integrate more and more engines to cover the full bandwidth of NoSQL solutions. Come and talk to us if you want to help out! You can either contribute to any ongoing effort or even start a new one. It's not that hard.

Performance considerations

We worked a lot around performance. The project started as an experiment but we did not manage to make it fail ;) So this time around we worked on performance optimizations.

By side effect, we also improved performance of Hibernate ORM - we reuse the same core engine. We also entered a chase with the Infinispan team to see which of Hibernate OGM or Infinispan was consuming the most time in the call stack. We went back and forth a few times and both projects reduced their overhead by a lot!

At the moment, the profiler does not show much overhead on using Hibernate OGM's stack compared to directly using the underling NoSQL datastore.

Query support

While you won't unfortunately see much in this release, we have been hard at work on the JP-QL query support. Sanne wrote a JP-QL query parser that will be flexible enough for our needs. In fact this is also likely going to be the foundation for the next-gen parser of Hibernate ORM.

He has also worked on the first steps to convert a JP-QL query into an Hibernate Search query. This will let you use Hibernate Search as an internal index and query system besides your NoSQL store. This can either replace or complement the query capability of the NoSQL solution.

Today, this solution can answer simple queries already (make sure to use the Hibernate Session API).

We also have been working on integrating Teiid's query engine which will be our way towards complex queries not natively supported by the underlying NoSQL engine. The plan is to combine the JP-QL parser, the Teiid query engine and write native support for the various NoSQL query options.

We have a great plan but that would take a lot of words to explain so let me save that for a future post.

In the mean time, you can use Hibernate OGM and do queries in two ways:

  1. use Hibernate Search to index your entities and write Hibernate Search queries directly. What is nice about this model is that it's a natural extension to the Hibernate or JPA APIs (managed entities are returned etc)
  2. use the native query capability of your underlying NoSQL store. Because Hibernate OGM tries to map data in a natural way, it's easy to simply query the NoSQL store directly

We hope to show you JP-QL support very soon as the foundations are now built. If you are interested in how we plan on doing queries, come talk to us or go read the architecture chapter in our reference documentation

Community, documentation and download

We worked on many more stuff like better JPA support, better ease of use, less redundant data structures, etc but this post is too long already. Check out the changelog if you are interested.

This project would be nowhere without the help from our community and contributors. Special thanks to:

  • Guillaume Scheibel who literally made MongoDB's support happen
  • Alan Fitton and Oliver Carr for their work on MongoDB associations support
  • Nicolas Helleringer - he is always around somewhere
  • Khanh Maudoux for his experimentation around Cassandra
  • Davide D'Alto for his experimentation on Neo4J and a few other things
  • Seiya Kawashima for his experimentation around Redis and Voldemort

If you are interested in contributing, come talk to us, we have many plans and a humongous todo list.

And guys we took great pain - literally - to write a very good reference guide, so go read it and go download Hibernate OGM.

Enjoy

Hibernate OGM 实战

Posted by    |       |    Tagged as Hibernate OGM

本文是上文的延续.

我想先介绍一下JBoss Developer Framework, 这是JBoss 社区推出的一个全新的项目, 旨在帮助开发者更好的理解和使用JBoss的相关技术, 它提供了大量的实例教程(50+), 视频, 文章的内容, 迁移向导(从Java EE 5到EE 6, 从Spring到Java EE)等, 教你一点点的学会Java EE 6相关的各种技术, 并且涵盖了 REST, HTML5 等新的热点技术.

本文即以kitchensink, 一个 JBoss Developer Framework 提供的实例为基础展开.

首先, 先让我介绍一下kitchensink吧, 最新的源代码可以在这里找到.

这个实例主要演示了如下几种技术:

  • Bean Validation 1.0
  • EJB 3.1
  • JAX-RS
  • JPA 2.0
  • JSF 2.0
  • CDI 1.0
  • Arquillian

想要运行这个实例的话, 你需要 JDK 6/7, Maven 3 和 JBoss AS 7

注意, 有一些依赖是只存在于JBoss 的maven 仓库中的, 所以, 可能需要对maven 的settings.xml文件做些配置, 添加JBoss maven仓库, 具体请参考这里这里

具体配置信息就不多说了, 上面给出的链接很详细, 这些也不是本文的重点, 接下来就让我们看看代码

首先是persistence.xml, 位于main/resources/META-INF/persistence.xml, 都是标准的位置

    <persistence version="2.0"
       xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
            http://java.sun.com/xml/ns/persistence
            http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
       <persistence-unit name="primary">
          <jta-data-source>java:jboss/datasources/KitchensinkQuickstartDS</jta-data-source>
          <properties>
             <property name="hibernate.hbm2ddl.auto" value="create-drop" />
             <property name="hibernate.show_sql" value="false" />
          </properties>
       </persistence-unit>
    </persistence>

可以看到, 这个文件定义的很简单, 就是定义了一个数据源和两个属性, 注意, hibernate.hbm2ddl.auto=creat-drop, 意思是在创建session factory的时候自动创建表结构, 关闭session factory的时候会自动drop掉表.

同时, 还可以看到main/resources目录中有import.sql这个文件, 当使用hibernate创建表结构的时候, 创建完成之后, hibernate会自动的导入import.sql文件, 这样可以添加一些初始数据.

另外, 引用的数据源是定义在main/webapp/WEB-INF/kitchensink-quickstart-ds.xml文件中的.

然后再来看看本文关注的另外一个方面, 实体定义:

    @Entity
    @XmlRootElement
    @Table(uniqueConstraints = @UniqueConstraint(columnNames = "email"))
    public class Member implements Serializable {
       /** Default value included to remove warning. Remove or modify at will. **/
       private static final long serialVersionUID = 1L;
    
       @Id
       @GeneratedValue
       private Long id;
    
       @NotNull
       @Size(min = 1, max = 25)
       @Pattern(regexp = "[A-Za-z ]*", message = "must contain only letters and spaces")
       private String name;
    
       @NotNull
       @NotEmpty
       @Email
       private String email;
    
       @NotNull
       @Size(min = 10, max = 12)
       @Digits(fraction = 0, integer = 12)
       @Column(name = "phone_number")
       private String phoneNumber;    
       // getters / setters
    } 

很简单的一个entity mapping, 需要注意的是javax.xml.bind.annotation.XmlRootElement 是JAXB里面的一个annotation, 在这里可以把这个实体对象转化成xml表示

还有就是这个entity里面定义了一些BV的annotation, 具体可以参考Hibernate Validator的文档.

Okay, 本实例中用到的其它技术暂时不做介绍了, 下面终于该进入正题了

上面介绍过了, 这个实例使用的是JBoss AS7中的数据源(java:jboss/datasources/KitchensinkQuickstartDS), 那么, 我们接下来就是看看如何做很少的更改, 让这个实例使用insinispan做为存储替换掉数据源中使用H2

首先, 是添加依赖, 因为我们这里是想要通过Hibernate OGM, 把实体对象保存进Infinispan当中, 所以只需要加入下面的依赖项即可:

        <dependency>
            <groupId>org.hibernate.ogm</groupId>
            <artifactId>hibernate-ogm-core</artifactId>
            <version>4.0.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>        
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.6</version>
        </dependency>
        <dependency>
            <artifactId>infinispan-core</artifactId>
            <groupId>org.infinispan</groupId>
            <version>5.1.5.FINAL</version>
            <scope>provided</scope>
        </dependency>

接下来就是修改persistence.xml了, 在上文曾经提到过, Hibernate OGM本身也是一个JPA的实现, 但是由于JBoss AS7默认集成的是Hibernate ORM 4, 所以我们需要在persistence.xml中显示的声明我们希望使用Hibernate OGM作为JPA的实现.

     <persistence version="2.0"
       xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
            http://java.sun.com/xml/ns/persistence
            http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
       <persistence-unit name="primary">
           <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
           <properties>
               <property name="hibernate.ogm.datastore.provider"
                         value="org.hibernate.ogm.datastore.infinispan.impl.InfinispanDatastoreProvider"/>
               <property name="hibernate.ogm.infinispan.configuration_resourcename" value="infinispan-ogm-config.xml"/>
           </properties>
           <class>org.jboss.as.quickstarts.kitchensink.model.Member</class>
       </persistence-unit>
    </persistence>

在这个更新过的文件中我们可以看到如下的变化:

  • 使用 org.hibernate.ogm.jpa.HibernateOgmPersistence 作为JPA的实现
  • 去掉了datasource的引用, Hibernate OGM不使用RMDBS
  • 通过 hibernate.ogm.datastore.provider指定使用infinispan作为data store
  • 通过 hibernate.ogm.infinispan.configuration_resourcename 属性指定infinispan的配置文件

src/main/webapp/WEB-INF/kitchensink-quickstart-ds.xml 这个文件已经没有用了,可以删掉.

至此, 配置方面就需要这么多的改动, 很简单吧

现在, 我们已经通过使用Hibernate OGM, 把底层的存储从RMDBS切换成了Infinispan, 但是, 由于RMDBS和NO-SQL 本质的不同, 我们还需要做一些修改.

使用uuid作为主键

在使用Hibernate ORM的时候, 我们通常会使用@GeneratedValue来得到数据库自动生成的id, 并且, 通常我们建议把id设置成long类型的以得到更好的性能.

可是, 如果使用的是NO-SQL的话, 如果是K-V类型的NO-SQL的话,他们是没有一个主键的概念的 (mongodb等文档型数据库是会提供自动生成的id的), 所以为了统一, 并且保证全局唯一, 在Hibernate OGM中我们建议使用UUID作为主键生成策略, 并且, 在Hibernate ORM中早已提供了此种策略, 我们在这里可以直接使用.

org.jboss.as.quickstarts.kitchensink.model.Member#id 需要修改成如下的样子.

   @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    private String id;

注意, 改变了id的类型之后, 我们还需要修改 org.jboss.as.quickstarts.kitchensink.rest.MemberResourceRESTService#lookupMemberById

        @GET
    	@Path("/{id:[0-9][0-9,\\-]*}")
        @Produces(MediaType.APPLICATION_JSON)
        public Member lookupMemberById(@PathParam("id") String id) {
            Member member = repository.findById(id);
            if (member == null) {
                throw new WebApplicationException(Response.Status.NOT_FOUND);
            }
            return member;
        }

这个方法提供了一个通过REST接口来查找Member的功能, 但是因为我们已经把id的类型改成了String, 所以我们需要对@Path做一些修改, 让它能够接受字符 -- @Path("/{id:[0-9,a-z][0-9,a-z,\\-]*}")

查询

因为Hibernate OGM还是一个很年轻的项目, 有一些功能还没有完全的实现, 例如, 我们在JPA/Hibernate中经常使用的Criteria 查询, 但是幸好, Hibernate OGM和Hibernate Search有很好的集成, 我们可以使用Hibernate Search来完成这部分工作.

org.jboss.as.quickstarts.kitchensink.data.MemberRepository#findByEmail 方法是通过email来查询一个Member, 内部实现是通过Criteria来查询的.

org.jboss.as.quickstarts.kitchensink.data.MemberRepository#findAllOrderedByName 方法是查询所有的Member并且按照name排序, 同样是使用的Criteria.

这两个方法的实现很简单, 相信大家都能看懂.

那么, 我们现在就需要使用Hibernate Search替换掉这两个方法.

首先, 把Hibernate Search相关的依赖添加进pom当中

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-search-orm</artifactId>
            <version>4.2.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-search-engine</artifactId>
            <version>4.2.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-search-analyzers</artifactId>
            <version>4.2.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-search-infinispan</artifactId>
            <version>4.2.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-lucene-directory</artifactId>
            <version>5.1.5.FINAL</version>
        </dependency>

这里所依赖的org.hibernate:hibernate-search-infinispanorg.infinispan:infinispan-lucene-directory提供了一个Lucene的Directory的实现, 可以把Lucene的index保存在infinispan当中, 从而实现比保存在文件系统当中更好的性能和可扩展性(因为Infinispan是一个分布式的数据网格系统).

在persistence.xml当中,我们需要添加上两个Hibernate Search的属性 这里所依赖的org.hibernate:hibernate-search-infinispanorg.infinispan:infinispan-lucene-directory提供了一个Lucene的Directory的实现, 可以把Lucene的index保存在infinispan当中, 从而实现比保存在文件系统当中更好的性能和可扩展性(因为Infinispan是一个分布式的数据网格系统).

在persistence.xml当中,我们需要添加上两个Hibernate Search的属性

<property name="hibernate.search.default.directory_provider" value="infinispan"/>
    <property name="hibernate.search.infinispan.configuration_resourcename" value="infinispan.xml"/>
第一个hibernate.search.default.directory_provider告诉Hibernate Search使用Infinispan作为Lucene Index Directory, 第二个指定了Hibernate Search所使用的Infinispan的配置文件.

接下来, 我们需要对org.jboss.as.quickstarts.kitchensink.model.Member做一些改动, 添加上Hibernate Search所需要的Annotation.

   @Entity
    @XmlRootElement
    @Indexed
    @Table(uniqueConstraints = @UniqueConstraint(columnNames = "email"))
    @Proxy(lazy = false)
    public class Member implements Serializable {
       /** Default value included to remove warning. Remove or modify at will. **/
       private static final long serialVersionUID = 1L;
    
       @Id
       @GeneratedValue(generator = "uuid")
       @GenericGenerator(name = "uuid", strategy = "uuid2")
       private String id;
    
       @NotNull
       @Size(min = 1, max = 25)
       @Pattern(regexp = "[A-Za-z ]*", message = "must contain only letters and spaces")
       @Fields({
    		   @Field(analyze = Analyze.NO, norms = Norms.NO, store = Store.YES, name = "sortableStoredName"),
    		   @Field(analyze = Analyze.YES, norms = Norms.YES)
       })
       private String name;
    
       @NotNull
       @NotEmpty
       @Email
       @Field(analyze = Analyze.NO)
       private String email;
    
       @NotNull
       @Size(min = 10, max = 12)
       @Digits(fraction = 0, integer = 12)
       @Column(name = "phone_number")
       @Field(analyze = Analyze.NO)
       private String phoneNumber;

第一个是@Indexed, 它告诉Hibernate Search, 这个实体类是需要被索引的, 还有@Field (在name和phoneNumber属性上)告诉Hibernate Search这两个属性是需要被索引的 ,具体信息请参考Hibernate Search 文档

想要在保存一个实体对象的时候,让Hibernate Search自动索引的话, 我们需要使用org.hibernate.search.jpa.FullTextEntityManager来替换javax.persistence.EntityManager.

org.jboss.as.quickstarts.kitchensink.service.MemberRegistration#register这个方法调用了EntityManager#persist, 那么我们需要做的就是把这个类中的@Inject private EntityManager em;换成 @Inject private FullTextEntityManager em; 这里, 之前的EntityManager和现在的FullTextEntityManager都是由CDI负责自动注入的, 可是, CDI并不知道如何创建Hibernate Search所特有的FullTextEntityManager, 所以, 为了让自动注入工作, 我们需要一个Producer.

而这个Producer, 在我们的实例当中就是org.jboss.as.quickstarts.kitchensink.util.Resources, 它已经存在了, 用来提供EntityManager的注入和Logger的注入(org.jboss.as.quickstarts.kitchensink.util.Resources#produceLog)

我们只需要添加Hibernate Search的内容 (CDI相关内容可以参考Weld文档)

	@Produces
	public FullTextEntityManager getFullTextEntityManager() {
		return Search.getFullTextEntityManager( em );
	}

	@Produces
	@ApplicationScoped
	public SearchFactory getSearchFactory() {
		return getFullTextEntityManager().getSearchFactory();
	}

	@Produces
	@ApplicationScoped
	public QueryBuilder getMemberQueryBuilder() {
		return getSearchFactory().buildQueryBuilder().forEntity( Member.class ).get();
	}

Okay, 现在万事俱备, 我们可以使用Hibernate Search来替换org.jboss.as.quickstarts.kitchensink.data.MemberRepository中那两个使用了Criteria的方法了

注意, org.jboss.as.quickstarts.kitchensink.data.CriteriaMemberRepository#findById 是需要把参数的类型从long改成String的, 除此之外, Hibernate OGM是支持直接使用id进行查询的, 所以不需要修改.

org.jboss.as.quickstarts.kitchensink.data.MemberRepository#findByEmail 方法是通过email来查询一个Member, 内部实现是通过Criteria来查询的.

org.jboss.as.quickstarts.kitchensink.data.MemberRepository#findAllOrderedByName 方法是查询所有的Member并且按照name排序, 同样是使用的Criteria.

@Inject
	private QueryBuilder queryBuilder;

	@Inject
	private FullTextEntityManager em;

	@Override
	public Member findById(String id) {
		return em.find( Member.class, id );
	}

	@Override
	public Member findByEmail(String email) {
		Query luceneQuery = queryBuilder
				.keyword()
				.onField( "email" )
				.matching( email )
				.createQuery();
		List resultList = em.createFullTextQuery( luceneQuery )
				.initializeObjectsWith( ObjectLookupMethod.SKIP, DatabaseRetrievalMethod.FIND_BY_ID )
				.getResultList();
		if ( resultList.size() > 0 ) {
			return (Member) resultList.get( 0 );
		}
		else {
			return null;
		}
	}

	@Override
	public List<Member> findAllOrderedByName() {
		Query luceneQuery = queryBuilder
				.all()
				.createQuery();
		List resultList = em.createFullTextQuery( luceneQuery )
				.initializeObjectsWith( ObjectLookupMethod.SKIP, DatabaseRetrievalMethod.FIND_BY_ID )
				.setSort( new Sort( new SortField( "sortableStoredName", SortField.STRING_VAL ) ) )
				.getResultList();
		return resultList;
	}

可以看到, 在这个新的类中, 我们首先使用CDI自动注入了Hibernate Search的QueryBuilder和FullTextEntityManager (参见上面修改后的org.jboss.as.quickstarts.kitchensink.util.Resources 工厂类)

在findById方法中, FullTextEntityManager#find实际上是代理给Hibernate OGM来处理的.

而在其余两个方法中, 则完全是使用Hibernate Search的Query API创建了查询条件, 然后交过Lucene来搜索的, 还记得我们上面修改了Member类, 在它被保存的时候创建Lucene索引的吧 (另, 上面提到过, 这个索引也是保存在infinispan当中的)

部属到JBoss AS 7

方便的是, 我的同事Hardy已经准备好了这样一个修改过的项目, 你可以直接从前面的链接中下载到本文中所介绍到的项目的源代码.

这是一个maven项目, 所以你需要先安装好maven, Hardy还很贴心的在pom里面使用了cargo插件, 所以你不需要下载JBoss AS 7了(尽管我还是推荐你下载一个看看, 很值得的), cargo会自动帮你下载, 并且完成部属等事情.

  • 编译 $ mvn clean package
  • 运行 $ mvn cargo:run
  • 测试(基于Arquillian) $mvn test

跑起来之后你可以访问http://127.0.0.1:8080/ogm-kitchensink 来看看具体的效果.

另外, 如果你尝试输入一个不合法的名字, 电话号码或者email地址的话, 你会看到错误提示, 这就是Bean Validation所自动帮你提供的输入校验, 来看看代码, 还记得 org.jboss.as.quickstarts.kitchensink.model.Member 类中的属性上定义的Bean Validation Annotations么?(下面代码中我只保留了这部分的annotation)

        @NotNull
	@Size(min = 1, max = 50)
	@Pattern(regexp = "[A-Za-z ]*", message = "must contain only letters and spaces")
	private String name;
	
	@NotNull
	@NotEmpty
	@Email
	private String email;

        @NotNull
	@Size(min = 10, max = 12)
	@Digits(fraction = 0, integer = 12)
	private String phoneNumber;

Okay, 现在你已经有了一个使用Hibernate OGM + NO-SQL的程序跑在 JBoss AS7上面了

另外, JBoss AS 7的启动速度很快, 非常快!

23:27:10,050 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: 
JBoss AS 7.1.1.Final "Brontes" started in 2009ms - Started 133 of 208 services (74 services are passive or on-demand)

上面的日志是在我的机器上启动的速度, 应该和Tomcat也差不多了吧, 但是JBoss AS7 可是一个完整的通过Java EE6认证的应用服务器, 而Tomcat只是一个servlet container.

Hardy还非常贴心的提供了一个小工具来帮助我们自动的插入数据, 这就是在项目根目录下的member-generator.rb

要使用这个工具的话, 你首先需要安装(gem install)如下的gem:

  • gem install httparty
  • gem install nokogiri
  • gem install choice

然后就可以通过执行下面的命令来创建一些测试数据了.

ruby member-generator.rb -a http://localhost:8080/ogm-kitchensink -c 20

部属到 Openshift

openshift是Redhat所提供了一个Paas服务, 它背后的技术已经开源, 可以在这里找到其源代码和众多的实例项目.

openshift同时提供免费的服务和付费的服务, 对于我们简单的玩玩来讲, 免费的账户已经足够了, 但是如果你的项目是一个正式上线的项目, 还是推荐使用收费的带有支持的服务的.

openshift所支持的平台包括:

  • Java
  • Ruby
  • Node.js
  • Python
  • PHP
  • Perl

并且它还提供了开箱即用的数据库支持, 包括RMDBS和NO-SQL

  • Mysql
  • PostgreSQL
  • Mongodb

例如我们可以在常见部属一个自己的wordpress或者drupal程序, 很方便.

对于我们Java程序员来讲, openshift最重要的是它内置了JBoss AS7 (和其商业版本JBoss EAP6)的支持, 所以, 对于想要尝试Java EE6的童鞋来说就有福了, 你可以直接使用此服务.

首先, 第一步没得说, 先到这里来创建一个免费的账号.

然后从这里下载客户端, Linux / Mac / Windows都有对应的客户端供下载.

接着, 你需要创建一个domain, 命令为 rhc domain create -n ${my domain name}, 之后, 你所有部属到openshift上的应用都会是http://${app name}-${domain name}.rhcloud.com的格式(免费账户可以创建3个程序, 并且你可以使用自己的域名).

然后该创建应用了, 我们想要把这个程序部属到JBoss AS7上, 使用下面的命令rhc-create-app -a ${your app name} -t jbossas-7 --nogit, 命令完成之后, 你会从输出中看到有一个git repo的地址, 现在, 你可以把这个地址作为一个remote添加到之前clone出来的ogm-kitchensink项目当中

git remote add openshift ${repo-url}

然后, 把ogm-kitchensink 推送到openshift提供的git repo当中

git push -f openshift master

最后, 你就可以访问你的应用了, 地址如同之前所说的, 是

http://${your app name}-${your domain name}.rhcloud.com

我创建了一个demo, 可以访问http://ogm-stliu.rhcloud.com.

Standardizing JPA for NoSQL: are we there yet?

Posted by    |       |    Tagged as Hibernate OGM

Every now and then, I am asked if a JPA for NoSQL JSR should be started. Let me share my opinion on the subject.

In short, probably at some point but certainly not today.

I do think there is something to offering an Object X Mapper for NoSQL engines and Hibernate OGM is proof of my belief but jumping on the standardization wagon is a whole different thing.

Trying to standardize something not mature enough or without practical experience on the subject very often go wrong and for good reasons. Cemeteries are filled with dead cold standards that hurt developers. I am a strong believer in standards and Mark our CTO too. But look at what we did for JPA and Bean Validation: take solid and useful techs and standardize them to improve integration with the rest of the ecosystem. That's the same model we use for the data grid JSR.

In Hibernate OGM, we are barely exploring how to properly map data and discovering interesting possibilities such a layer offers. Now would be a bad time to try and set in store rules. Heck, we might even arrive to the conclusion that such tool turns out not being useful after all.

I am of course open to discuss and exchange on the subject and more than happy to change my mind :) You know where I stand, what's your position on the subject?

Hibernate OGM 带你进入NO-SQL的世界, 用你熟悉的方式

Posted by    |       |    Tagged as Hibernate OGM

Sorry, this is an Chinese only post :D

现在 NO-SQL 的概念被互联网界炒的很火, 貌似哪个网站要是不用上这个, 不推出自己的 K-V 存储都不好意思出来混了. 现在网上有很多介绍各种各样 NO-SQL 的内容, 所以本文不注重与此, 但是, 作者想要强调的是, 做技术有个很重要的原则是不跟风, 选择合适自己的技术最重要. NO-SQL 不是银弹, 它并不能(起码目前)完全取代关系型数据库, 可参考这篇文章.

作为Java程序员的我们, 已经(应该)很熟悉RMDBS了, 并且, 有一套很完善(有人管这叫笨重, 但是取决于你的项目类型, 如果是一个startup, 那么自然笨重, 可是如果是给铁道部开发的系统, 再怎么谨慎也不为过)的开发方法论了.

我们所熟悉的 Hibernate 现在已经改名叫做 Hibernate ORM 了, 因为现在Hibernate team出品的项目并不仅仅是一个O/R Mapping的框架了, 我们还有提供了完善的搜索支持的Hibernate Search和用于校验的Hibernate Validator, 以及本文介绍的Hibernate OGM, 所以, 需要一个更准确的命名.

对于一个Java项目, 一般先创建好领域模型, 然后定义实体对象以及他们之间的关系, 剩下的事情Hiberate ORM会帮你全都打理好, 很简单也很熟悉.

那么, 如果想要用NO-SQL呢?

什么是Hibernate OGM

OGM == Object/(Data) Grid Mapping

ORM的概念大家都很熟悉了, 那么OGM的意思是对象--数据网格 映射, Hibernate OGM同样是一个JPA的实现, 所以你可以用熟悉的JPA API (当然, 或者Hibernate API)来把实体模型存储进NO-SQL中, 和执行查询等操作.

通过使用Hibernate OGM, 我们可以把现有的, 基于JPA/Hibernate ORM的项目不加改动的从RMDBS切换到NO-SQL之上.

Hibenrate OGM并不是从头搭建的全新项目, 事实上, 它跟牛顿一样, 也站在了巨人的肩膀上.

通过使用Hibernate ORM的核心, 提供了完整的JPA支持, 以及实体对象生命周期管理等功能, 确保了稳定性.

通过Hibernate Search提供了完善的全文检索功能, 这也正是NO-SQL相比RMDBS所缺少的部分.

Infinispan, 一个高性能, 分布式的网格存储引擎, 则是Hibernate OGM所支持的标准NO-SQL实现(当然不仅限于Infinispan), 同时, infinispan还可以作为Hibernate Search所需要是lucene index的存储.

当程序和存储(无论RMDBS 还是 NO-SQL)打交道的时候, 核心问题实际上就是两个: 存进去, 取出来.

领域模型如何被持久化进NO-SQL store

抛开各种NO-SQL的实现方式, 例如K-V, Documents, Graph等, 我们可以把NO-SQL简化成我们容易理解的 HashMap .

那么, 现在问题是, 如果你有一个简单的实体类, 你该如何把它存储进一个HashMap呢?

对于RMDBS来说, 这不是个问题, 每个属性对应表中的一个column就好了.

可是如果是NO-SQL的话, 情况就复杂一些了, 最简单的方式是用这个实体类的类名和其id作为key(注意, 这里的HashMap是个概念, 指代NO-SQL store, 所以其是全局的), 把这个实体对象(经过序列化)作为value来存储.

可是, 这个方案会有些问题:

1. NO-SQL中存储的内容和你的领域模型紧密耦合, 如果后面领域模型有改动, 那么在反序列化之前存储的内容的时候会出问题. 2. 别的系统可能会无法使用该NO-SQL中的数据, 例如, 你还有一个ruby的系统, 那么两个系统之间可能会无法共享一个NO-SQL, 因为ruby没办法反序列化Java Object. 3. 序列化的时候, 可能会把整个对象图 (因为对象之间相互关联) 都保存起来

RMDBS在此则有一些比较好的原则:

实体对象和存储内容解耦 (所以才有O/R Mapping) 使用基本类型, 例如varchar, integer等 主键确保唯一性 外键确保一致性

Hibernate OGM在把实体对象持久化的时候, key是这样构造的 -- 表名,主键列名,主键值, 而value呢, 是存储成一个基本数据类型组成的元组的, 可以相像成Map<String,Object>, 这个map的key是属性名, 而值则是属性值的基本类型表示, 例如 URL 会表示成一个 String. 我们相信使用基本类型更有助于数据的可移植性.

实体之间的关联关系, 则是另外一个让人头痛的地方 -- 如果自己处理实体的存储的话.

但是, 得益于JPA/Hibernate早已定义的很完善的关联关系处理方案, 这个在Hibernate OGM中也很简单, 也是存放在元组当中的, 其中key是由 -- 关联表名,外键列名,外键值 组成的. 这样的结构保证了, 关联关系可以由实体中保存的信息获取到.

如下图所示:

上图中, 如果想找到 user id为1的人的名字和地址的话, 那么就可以:

1. 通过key tbluser,userIdpk,1 得到 {userId_pk=1, name=Emmanuel} 2. 通过key tbluseraddress,userIdfk,1 得到 { { userId_fk=1, addressId_fk=3}, { userId_fk=1, addressId_fk=5} } 得到与此user所关联的两个address的id分别为3和5. 3. 再分别构造两个key tbladdress,addressIdpk,3 和 tbladdress, addressIdpk,5得到此人的地址.

所有上面介绍的这些都是发生在Hibernate OGM内部的, 用户所需要的仅仅是:

entitymanager.persist(user);
entitymanager.persist(address);
entitymanager.find(User.class, 1);

查询

目前, 绝大多数应用对于存储的需求都是读大于写的, 而传统的关系型数据库, 由于其有完善的关系数学模型保证, 所以能够提供完善的查询, 甚至多表级联查询(join), 而另一方面, NO-SQL因为起schema free / K-V的本质, 基本上只能通过key来查找.

如上面例子所示, 对于id已知的情况, 那么NO-SQL表现的很好, 可是, 如果想根据地址来搜索的话, 那就比较麻烦了.

而Hibernate Search, 正是在此起的作用, 通过在实体对象添加一些annotation, 定义要索引的字段, 当你通过Hibernate ORM/OGM (Hibernate Search和这两个都集成的很好) 保存或更新实体对象的时候, Hibernate Search会自动的对此对象创建lucene索引, 之后, 就可以通过Hibernate Search Query API 或者 Lucene Query API对保存的实体对象进行全文索引了.

继续阅读Hibernate OGM实战

back to top