Hibernate 和 MongoDB 高级教程(一)

原文:Pro Hibernate and MongoDB

协议:CC BY-NC-SA 4.0

零、简介

这本书涵盖了开发 Hibernate OGM-MongoDB 应用的所有重要方面。它为充分利用 Hibernate OGM-MongoDB duo 提供了清晰的说明,并提供了许多通过 Hibernate Native API 和 Java Persistence API 集成 Hibernate OGM 的例子。您将学习如何为最流行的 web 和企业服务器开发桌面、web 和企业应用,例如 Tomcat、JBoss AS 和 Glassfish AS。您将看到如何利用 Hibernate OGM-MongoDB 以及许多常见技术,如 JSF、Spring、Seam、EJB 等等。最后,您将了解如何迁移到云—MongoHQ、MongoLab 和 OpenShift。

这本书是给谁的

这本书是为有经验的 Java 开发人员编写的,他们对探索 NoSQL 数据库的 Hibernate 解决方案感兴趣。对于开篇章节(第一章–第三章),熟悉 ORM 范式、Hibernate 原生 API 和 JPA 的主要方面就足够了。这本书提供了这些概念的简要概述。从第四章的开始,你应该对开发部署在 Tomcat、JBoss AS 或 GlassFish AS 服务器下的 web 应用(使用 NetBeans 或 Eclipse)有所了解。此外,您需要熟悉 web 应用中常用的 Java 技术和框架,比如 servlets、EJB、JSF、JSP、Seam、Spring 等等。

这本书的结构

以下是每章的主要重点:

第一章【Hibernate OGM 入门

本章简要介绍了 Hibernate OGM 世界。在这一章的第一部分,我讨论了 Hibernate OGM 架构,它当前的特性,以及我们对未来支持的期望。然后,我提供了几种下载、安装和配置 Hibernate OGM 和 MongoDB 的方法。

第二章 : Hibernate OGM 和 MongoDB1

在这一章中,我通过关注 Hibernate OGM 如何与 MongoDB 一起工作来更清楚地定义 Hibernate OGM 和 MongoDB 之间的关系。您将学习如何存储数据,如何映射主键和关联,以及如何处理事务和查询。

第三章:引导 Hibernate OGM

本章展示了如何通过 Hibernate 本地 API 和 JPA 来引导 Hibernate OGM。

第四章:Hibernate 工作中的 OGM

这是最重要的章节之一。您将学习如何在部署在不同服务器上的最常见的 web 和企业 Java 应用中集成 Hibernate OGM 和 MongoDB。以下是应用的完整列表:

  • Java SE 和 Mongo DB——一个“Hello world”示例
  • 在非 JTA 环境(JDBC 事务,Tomcat 7)中 Hibernate OGM(通过 Hibernate Native API)
  • 在独立的 JTA 环境(JBoss JTA,Tomcat 7)中 Hibernate OGM(通过 Hibernate Native API)
  • 在内置的 JTA 环境(没有 EJB,GlassFish 3)中 Hibernate OGM(通过 Hibernate Native API)
  • 在内置的 JTA 环境(EJB/BMT,GlassFish 3)中 Hibernate OGM(通过 Hibernate Native API)
  • 在内置的 JTA 环境(EJB/CMT,GlassFish 3)中 Hibernate OGM(通过 Hibernate Native API)
  • 在内置的 JTA 环境(GlassFish AS 3)中 Hibernate OGM(通过 JPA)
  • 在内置的 JTA 环境(JBoss AS 7)中 Hibernate OGM(通过 JPA)
  • 在内置的 JTA 环境(JBoss AS 7 和 Seam 应用)中 Hibernate OGM(通过 JPA)
  • 在内置的 JTA 环境(GlassFish 和 Spring 应用)中 Hibernate OGM(通过 JPA)
  • 在独立的 JTA 环境中 Hibernate OGM(通过 JPA)JPA/JTA(Tomcat)
  • 在非 JTA 环境中 Hibernate OGM(RESOURCE _ LOCAL,Apache Tomcat 7)

第五章 : Hibernate OGM 和 JPA 2.0 注解

在 Hibernate OGM 中映射 Java 实体可以分为支持的和不支持的注释。在这一章中,我展示了支持的注释,以及每个注释的支持程度。

第六章 : Hibernate OGM 查询 MongoDB

本章探索 Hibernate OGM 的查询功能。我从 MongoDB 原生查询开始,然后学习用 Hibernate Search 和 Apache Lucene 编写的复杂查询。

第七章 : MongoDB 电子商务数据库模型

至此,您已经掌握了足够的专业知识来开发一个包含 Hibernate OGM 和 MongoDB 的真正的应用。电子商务网站是一个良好的开端,也是一个有趣的研究案例,因此在本章中,我将一个经典的 SQL 数据库模型改编为 Hibernate OGM 和 MongoDB 风格。我还研究了电子商务数据库架构的各个方面。

第八章 : MongoDB 电子商务数据库查询

在开发了 MongoDB 电子商务数据库模型之后,是时候绘制和实现主要的特定于电子商务的查询了。在这一章中,我使用 Hibernate Search 和 Apache Lucene 来编写这样的查询。结果是一个名为 RafaEShop 的完整电子商务应用。

第九章:将 MongoDB 数据库迁移到云端

在本章中,您将学习如何将在第七章中开发的 MongoDB 电子商务数据库迁移到两个云中:MongoHQ 和 MongoLab。

第十章:在 OpenShift 上迁移 RafaEShop 应用

这最后一章是将电子商务 RafaEShop 应用迁移到两台企业服务器上的 OpenShift cloud 的详细指南:JBoss AS 和 GlassFish AS。

下载代码

本书中所示示例的代码可在 Apress 网站www.apress.com上获得。您可以在该书的信息页面上的源代码/下载选项卡下找到链接。该选项卡位于页面相关标题部分的下方。

联系作者

如果您有任何问题或意见——或者发现您认为我应该知道的错误——您可以通过leoprivacy@yahoo.com联系我。

一、Hibernate OGM 入门

您可能熟悉 Hibernate ORM,这是一个强大、健壮的工具,用于在关系数据库(RDBMS)和面向对象编程语言之间转换数据。作为一个对象关系映射(ORM)框架,Hibernate ORM 使用 SQL 存储。然而,近年来,开发人员对 NoSQL 数据库产生了兴趣,这种数据库为存储和检索大量数据进行了优化。NoSQL 数据库往往是非关系的、开源的、可水平伸缩的、分布式的和无模式的。

描述 NoSQL 商店有多种方式,但它们通常按数据模型分类,尤其是以下方式:

  • 文档存储(Mongo DB、RavenDB、CouchDB 等等)
  • 宽列商店(Hypertable、Cassandra、HBase 等)
  • key value/元组 stores (DynamoDB、LevelDB、Redis、Ryak 等)
  • 图形数据库(Neo4J、GraphBase、InfoGrid 等等)

这些也很常见:

  • 多模式数据库(OrientDB、ArangoDB 等)
  • 对象数据库(db4o、Versant 等等)
  • 网格和云数据库(GigaSpaces、Infinispan 等等)
  • XML 数据库(eXist、Sedna 等等)

显然,NoSQL 的商店非常复杂多样。一些网站拥有庞大的用户群,而另一些则鲜为人知。每一种都有自己的优点和缺点。你甚至可以说,NoSQL 是一个非常有争议的话题,程序员谈论它的时间比他们实际使用它的时间还要多。

然而,随着最近发布的 Hibernate OGM(对象网格映射器)项目,这种情况可能会改变,该项目提供了一个完整的 Java 持久性 API (JPA)引擎,用于在 NoSQL 商店中存储数据。这个项目给了寻求利用 NoSQL 商店的 Java 开发人员一个真正的推动,因为它提供了一个公共接口——众所周知的 JPA 编程模型——作为各种 NoSQL 方法的前端。Hibernate OGM 基于 Hibernate ORM 核心引擎,重用 Java 持久性查询语言(JP-QL)作为查询存储数据的接口,并且已经提供了对三个 NoSQL 商店的支持:MongoDB、Ehcache 和 Infinispan,Apache Cassandra 将来应该会得到支持。尽管这个项目还很年轻,Hibernate OGM 团队的目标保证了它在未来有巨大的潜力——以及大量的工作要完成。

特点和期望

在撰写本书时,最新的 Hibernate OGM 发行版是 4.0.0 Beta2,它已经成功地为不同的 NoSQL 方法提供了一个通用的接口;快速扩大或缩小数据存储;独立于底层存储技术;和 Hibernate 搜索。以下是 Hibernate OGM 目前支持的内容:

  • 在文档存储中存储数据(MongoDB)
  • 将数据存储在键/值存储中(Infinispan 的数据网格和 Ehcache)
  • JPA 实体的创建、读取、更新和删除(CRUD)操作
  • 多态实体(支持超类、子类等等)
  • 可嵌入的对象(例如,可嵌入的类,在 JPA 中用@Embeddable注释;可嵌入类的实例集合,在 JPA 中用@ElementCollection注释
  • 基本类型(如数字、StringURLDate、枚举)
  • 联想(@ManyToOne@OneToOne@OneToMany@ManyToMany)
  • 双向关联
  • 集合(SetListMap等)
  • Hibernate 搜索的全文查询
  • JPA 和原生 Hibernate ORM API (Hibernate OGM 可以通过 JPA 或 Hibernate Session 引导,我将在第三章向您展示。)

将来,Hibernate OGM 将支持:

  • 其他键/值对系统
  • 其他 NoSQL 发动机
  • 声明性反规格化
  • 复杂的 JP-QL 查询,包括一对多连接和聚合
  • 前置现有的 JPA 应用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意反规格化是一种加速读取过程的数据库技术。其思想是尽可能减少查询中的连接数量;连接降低了读取性能,因为必须从多个表中提取数据,而不能破坏它们之间的关联。虽然规范化促进了将相关数据拆分到多个关联的表中,但是反规范化鼓励添加少量冗余来限制连接。即使一些数据被复制,性能通常也会提高。

Hibernate OGM 体系结构〔??〕

因为 Hibernate OGM 尽可能地使用现有的 Hibernate ORM 模块,所以 OGM 架构本质上是通过插入和拔出不同的组件来扩展 ORM 架构。Hibernate ORM 使用一组接口和类在关系数据库和面向对象编程语言之间转换和保存数据。其中包括 JDBC 层,用于连接数据库和发送查询, Persister s 和 Loader s 接口,负责持久化和加载实体和集合,如图图 1-1 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-1 。Hibernate ORM 架构

Hibernate OGM 旨在实现相同的目标,但是使用 NoSQL 商店。因此,Hibernate OGM 不再需要 JDBC 层,取而代之的是两个新元素:一个数据存储提供者和一个数据存储方言,如图图 1-2 所示。这两者都充当 Hibernate OGM Core 和 NoSQL 商店之间的适配器。(一个数据存储库是一个适配器,它将核心映射引擎与特定的 NoSQL 技术连接起来。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-2 。Hibernate OGM 数据存储提供者和数据存储方言

数据存储提供者负责管理到 NoSQL 存储的连接,而数据存储方言管理与 NoSQL 存储引擎的通信。实际上,这些概念体现在两个接口中,org.hibernate.ogm.datastore.spi.DatastoreProviderorg.hibernate.ogm.dialect.GridDialectDatastoreProvider接口负责启动、维护和停止商店连接,而GridDialect接口处理 NoSQL 商店中的数据持久性。此外,PersistersLoaders界面被重新编写以支持 NoSQL 商店的功能。

目前有四个DatastoreProvider的实现:

  • EhcacheDatastoreProvider(对于 NoSQL Encache)
  • InfinispanDatastoreProvider(适用于 NoSQL Infinispan)
  • MongoDBDatastoreProvider(适用于 NoSQL MongoDB)
  • MapDatastoreProvider(用于测试目的)

从特定的网格实现中抽象 Hibernate OGM 有五种实现方式:

  • EhcacheDialect(用于 EhCache)
  • InfinispanDialect(对于 Infinispan)
  • MongoDBDialect(用于 MongoDB)
  • HashMapDialect(用于测试)
  • GridDialectLogger(用于记录在真实方言上执行的呼叫)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意如果你决定写一个新的数据存储,你必须实现一个DatastoreProvider和一个GridDialect。在https://community.jboss.org/wiki/HowToWriteADatastoreInHibernateOGM可以找到更多细节。

持久数据

通过修改后的Loader s 和 s 接口,Hibernate OGM 能够将数据保存到 NoSQL 商店。但是,在这样做之前,OGM 需要在内部表示和存储数据。为此,Hibernate OGM 尽可能多地保留了关系数据库的概念,并根据自己的需要修改这些概念。一些概念,比如存储实体,完全遵循关系模型,而另一些概念,比如存储关联,部分遵循关系模型。因此,数据存储为基本类型(实体存储为元组);仍然使用主键外键 的概念;应用数据模型和商店数据模型的关系是通过像这样的概念来抽象维护的。

OGM 使用元组 来表示数据的基本单位。元组意味着在概念上将实体存储为一个Map<String, Object>。关键字是列名(实体属性/字段或@Column注释值),值是作为原始类型的列值(见图 1-3 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-3 。Hibernate OGM 元组

每个元组代表一个实体实例,存储在一个特定的键中。实体实例通过由表名、主键列名和主键列值组成的特定键查找来标识。参见图 1-4 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-4 。存储实体实例的 Hibernate OGM

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意 Java 集合被表示为一个元组列表。特定键由包含集合的表名、代表外键的列名和列值组成。

图 1-5 显示了多对多关联的关系数据库模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-5 。多对多关联的关系数据库模型

相比之下,Hibernate OGM 中的关联存储为类型为Map<String, Object>的元组集。例如,对于多对多关联,每个元组存储一对外键。Hibernate OGM 将从一个实体导航到其关联所必需的信息存储在一个特定的键中,该键由表名、列名和值组成,这些值表示我们所来自的实体的外键。这个@ManyToMany关联由 Hibernate OGM 内部存储,如图 图 1-6 所示。(您可以看到从第 8 行开始的关联元组。)这种方法通过键查找来促进可达数据,但是它有缺点:数据可能是冗余的,因为必须为关联双方存储信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-6 。多对多关系的 Hibernate OGM 数据网格

Hibernate OGM 将 JPA 实体存储为元组,而不是可序列化的 blobs。这更接近于关系模型。序列化实体有几个缺点:

  • 与其他实体相关联的实体也必须被存储,这很可能导致一个大图。
  • 很难保证重复对象之间的对象同一性甚至一致性。
  • 很难添加或删除一个属性或包含一个超类并处理反序列化问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意 Hibernate OGM 将种子(当标识符需要种子时)存储在值中,该值的键由表示段的表名、列名和列值组成。

显然,这种表现并不是所有 NoSQL 商场都有的。例如,对于面向文档的商店 MongoDB 来说,情况就不同了。在这种情况下,使用GridDialect,它的主要任务是将这个表示转换成 NoSQL 商店的预期表示。对于 MongoDB,MongoDBDialect将其转换成 MongoDB 文档。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意由于 NoSQL 商店不知道模式的概念,Hibernate OGM 元组不绑定到模式。

查询数据

当然,Hibernate OGM 需要提供一个强大的查询数据引擎,在撰写本文时,根据查询的性质和 NoSQL 查询支持,这可以通过多种不同的方式实现。

CRUD 操作是 Hibernate ORM 引擎的责任,它们遵循一个简单的过程。独立于 JPA 或 Hibernate 原生 API,Hibernate ORM 将持久性和加载查询委托给 OGM 引擎,OGM 引擎将 CRUD 操作委托给DatastoreProvider / GridDialect,后者与 NoSQL 存储交互。图 1-7 描述了这个过程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-7 。Hibernate OGM 和 CRUD 操作

因为 Hibernate OGM 想要提供整个 JPA,所以它需要支持 JP-QL 查询。这意味着复杂的查询引擎(QE)应该对特定的 NoSQL 商店查询能力和 JP-QL 查询的复杂性敏感。最乐观的例子是具有查询功能和简单的 JP-QL 查询的 NoSQL。在这种情况下,查询被委托给特定于 NoSQL 的查询翻译器,结果由 Hibernate OGM 管理,以组成特定的对象(参见图 1-8 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-8 。Hibernate OGM 和 JP-QL 简单查询(支持查询的 NoSQL)

当 NoSQL 商店不支持当前查询时,情况就不那么乐观了。在这种情况下,JBoss Teiid 数据虚拟化系统介入,将 JP-QL 查询拆分成可以由数据存储执行的简单查询。(更多信息见www.jboss.org/teiid)。Teiid 也对结果进行处理,得到最终的查询结果,如图图 1-9 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-9 。Hibernate OGM 和 JP-QL 复杂查询

最糟糕的情况是 NoSQL 商店很少或没有查询支持。由于这是一个棘手的案例,它需要重炮,就像 Hibernate Search,一个基于 Hibernate Core 和 Apache Lucene 的企业全文搜索工具。基本上,Hibernate 搜索索引引擎从 Hibernate ORM 核心接收事件,并保持实体索引过程最新,而 JP-QL 查询解析器将查询翻译委托给 Hibernate 搜索查询引擎(对于简单查询)或 Teiid(对于中级到复杂查询),并使用 Lucene 索引执行它们(参见图 1-10 )。此外,Hibernate Search 还提供了集群支持和面向对象的抽象,其中包括查询领域特定语言(DSL)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-10 。Hibernate OGM 和 JP-QL 查询(很少或没有 NoSQL 支持)

获取 Hibernate OGM 发行版

在撰写本文时,Hibernate OGM 发行版是 4.0.0.Beta2。获得完整文档、源代码和依赖项的最佳方式是访问www.hibernate.org/subprojects/ogm.html并下载相应的 ZIP/TGZ 归档文件。

不幸的是,这并不像看起来那么简单。由于本书的重点是 Hibernate OGM 和 MongoDB,所以您需要找到专门用于“连接”OGM 和 MongoDB 的 jar:hibernate-ogm-mongodb-``x``.jarmongo-java-driver- x .jar。(MongoDB 对大多数编程语言都有客户端支持;这是 MongoDB 团队开发的 MongoDB Java 驱动,Hibernate OGM 用来和 MongoDB 交互)。在 Hibernate OGM 版本 4.0.0.Beta1 中,你会在\hibernate-ogm-4.0.0.Beta1\dist\lib\mongodb文件夹中找到这些 jar:hibernate-ogm-mongodb-4.0.0.Beta1.jarmongo-java-driver-2.8.0.jar。在 Hibernate OGM 版本 4.0.0.Beta2 中,缺少了\mongodb文件夹,所以新的 jar 没有被打包。

这意味着您仍然可以在 Hibernate OGM 4.0.0.Beta2 中使用hibernate-ogm-mongodb-4.0.0.Beta1.jarmongo-java-driver-2.8.0.jar,或者您可以编译 Hibernate OGM 4.0.0.Beta2 的源代码来获得最新的快照。要编译代码,请访问www.sourceforge.net/projects/hibernate/files/hibernate-ogm/4.0.0.Beta2/。我已经编译好代码,获得了 MongoDB JAR,命名为hibernate-ogm-mongodb-4.0.0-SNAPSHOT

如果你看一下图 1-11 中显示的 Hibernate OGM 变更日志,你会看到 Hibernate OGM 4.0.0.Beta2 已经升级到支持 MongoDB Java Driver 2.9。x。这意味着如果您决定编译代码并使用 MongoDB 概要文件的结果快照,您也可以添加一个 2.9。x MongoDB Java 驱动,而不是 2.8。x

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-11 。Hibernate OGM 更改日志

对于这本书,我选择使用 Hibernate OGM 4.0.0.Beta2 和 Hibernate OGM for MongoDB 4 . 0 . 0 . beta 1。

从 Maven 中央存储库获取 Hibernate OGM

也可以从 Maven 中央存储库 ( www.search.maven.org/)下载 Hibernate OGM。搜索“hibernate ogm”,将会返回您在图 1-12 中看到的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-12 。Maven 中央存储库中列出的 Hibernate OGM 发行版

如您所见,下载 Hibernate OGM 核心和概要文件非常容易,包括 MongoDB 概要文件。您可以下载 jar 或 POMs(项目对象模型)文件。

从 Maven 命令行获取 Hibernate OGM

Hibernate OGM 也可以从 Apache Maven 命令行 获得。显然,Maven 必须在您的计算机上安装和配置。首先,您必须修改您的settings.xml文档,它存储在 Maven 本地存储库.m2文件夹中(默认位置)。对于 Unix/Mac OS X 用户,这个文件夹应该是∼/.m2;对于 Windows 用户来说是C:\Documents and Settings\{your username}\.m2 or C:\Users\{your username}\.m2。如果settings.xml文件不存在,你应该在这个文件夹中创建它,如清单 1-1 所示。(如果您已经有了这个文件,只需相应地修改它的内容。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意如果创建或修改settings.xml因为太冗长而显得太复杂,你可以简单地在你的pom.xml中使用<repository><dependency>标签。

***清单 1-1。***settings . XML

<?xml version="1.0" encoding="UTF-8"?>

<settings FontName2">http://maven.apache.org/SETTINGS/1.0.0 " xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
          xsi:schemaLocation=" http://maven.apache.org/SETTINGS/1.0.0
                                             http://maven.apache.org/xsd/settings-1.0.0.xsd ">
 <!-- jboss.org config start -->
 <profiles>
    <profile>
      <id>jboss-public-repository</id>
      <repositories>
        <repository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url> https://repository.jboss.org/nexus/content/groups/public-jboss/</url >
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url> https://repository.jboss.org/nexus/content/groups/public-jboss/</url >
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
    <profile>
      <id>jboss-deprecated-repository</id>
      <repositories>
        <repository>
          <id>jboss-deprecated-repository</id>
          <name>JBoss Deprecated Maven Repository</name>
          <url> https://repository.jboss.org/nexus/content/repositories/deprecated/</url >
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>false</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </repository>
      </repositories>
    </profile>
    <!-- jboss.org config end -->
  </profiles>

  <!-- jboss.org config start -->
  <activeProfiles>
    <activeProfile>jboss-public-repository</activeProfile>
  </activeProfiles>
  <!-- jboss.org config end -->
</settings>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意你可以通过在settings.xml中添加标签localRepository来修改 Maven 本地库的默认位置,比如:<localRepository> new_repository_path </localRepository>

接下来,你需要创建一个pom.xml文件。显然,这个文件的内容取决于您想从 Hibernate OGM 存储库中获得什么。例如,清单 1-2 中的pom.xml将下载 Hibernate OGM 核心发行版(包括依赖项)并将其本地存储在D:/Hibernate_OGM中(您也可以使用默认的./m2文件夹,但这样会更清晰、更容易导航)。

***清单 1-2。***POM . XML

<project FontName2">http://maven.apache.org/POM/4.0.0 "
             xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
             xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd ">
    <modelVersion>4.0.0</modelVersion>
    <groupId>maven.hibernate.ogm</groupId>
    <artifactId>Maven_HOGM</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Maven_HOGM</name>
    <dependencies>
        <dependency>
            <groupId>org.hibernate.ogm</groupId>
            <artifactId>hibernate-ogm-core</artifactId>
            <version>4.0.0.Beta2</version>
        </dependency>
    </dependencies>
    <build>
       <directory>D:/Hibernate_OGM</directory>
       <defaultGoal>dependency:copy-dependencies</defaultGoal>
    </build>
</project>

最后一步是执行 Maven mvn命令。为此,打开命令提示符,导航到包含pom.xml文件的文件夹,运行mvn命令(参见图 1-13 )。几秒钟后,您应该在pom.xml文件指定的路径中找到 Hibernate OGM 二进制文件(包括依赖项)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-13 。运行 mvn 命令

添加 MongoDB 工件

现在您知道了如何获得 Hibernate OGM 4.0.0.Beta2 核心(和依赖项),但是没有任何 NoSQL 数据存储构件。目前,您可以为以下 NoSQL 商店添加工件: Ehcache、Infinispan 和 MongoDB。因为我们的重点是 Hibernate OGM 和 MongoDB,所以您需要通过将以下依赖项放入pom.xml文件来添加 MongoDB 工件:

...
<dependency>
   <groupId>org.hibernate.ogm</groupId>
   <artifactId>hibernate-ogm-mongodb</artifactId>
   <version>4.0.0.Beta1</version>
 </dependency>
...

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意对于 Infinispan,只需用hibernate-ogm-infinispan替换工件 id,对于 Ehcache 用hibernate-ogm-ehcache

现在,再次运行mvn命令将增加两个罐子,hibernate-ogm-mongodb-4.0.0.Beta1.jarmongo-java-driver-2.8.0.jar,如图图 1-14 所示。MongoDB 驱动程序也可以作为 jar 在www.mongodb.org/display/DOCS/Drivers地址下载。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-14 。添加 MongoDB 工件后运行 mvn 命令

使用 NetBeans IDE 获得 Hibernate OGM 发行版

如果您是 NetBeans 的粉丝,从 NetBeans Maven 项目中使用 Maven 会简单得多。本节描述了创建这样一个项目的主要步骤,以获得作为 NetBeans 库的 Hibernate OGM 发行版,以便在其他项目中使用。启动 NetBeans(我在 NetBeans 7.2.1 上进行了测试),并遵循以下步骤:

  1. From the File menu, select the New Project option. In the New Project wizard, select Maven in the Categories list and POM Project in the Projects list, as shown in Figure 1-15.

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 1-15 。使用 NetBeans 7 创建 POM 项目

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意如果您的 NetBeans 发行版中没有 Maven,您可以按照http://wiki.netbeans.org/InstallingAPlugin中关于第三方插件安装的教程来安装它。

  2. Type the project name (Maven_HOGM), select the project location (D:\Apress\apps\NetBeans), type the group id (maven.hibernate.ogm) and the version (1.0-SNAPSHOT) and click Finish as shown in Figure 1-16. (Note that I’ve used example names and locations here. Feel free to choose your own.) The empty project will be created and listed under the Projects panel.

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 1-16 。设置项目名称和位置

  3. Expand the Maven_HOGM | Project Files node and locate pom.xml and settings.xml. If settings.xml isn’t listed, right-click on the Project Files node, select Create settings.xml (as shown in Figure 1-17), and fill the file with the appropriate content.

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 1-17 。从 NetBeans 7 创建 settings.xml 文件

  4. 根据需要编辑pom.xml。此时,两个文件都应该准备好被 Maven 处理了。

  5. 右键单击Maven-HOGM节点并选择Clean and Build。等到任务成功结束,然后展开Maven_OGM | Dependencies节点查看下载的 jar。

  6. Now you can create a NetBeans library. (I recommend that you create this library because the applications developed with NetBeans, in later chapters, refer to it.) From the NetBeans main menu, select Tools | Ant Libraries. In the Ant Library Manager, click the New Library button, provide a name for the library, such as Hibernate OGM Core and MongoDB, and click OK. Next, click on the Add JAR/Folderbutton and navigate to the JARs (if you followed my example path, you’ll find them inD:\Hibernate_OGM`dependency, as shown in Figure 1-18). Select all of the JARs and add them to this library. Click OK to finish creating the library.

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 1-18 。为 Hibernate OGM 和 MongoDB 创建用户库`

`现在,通过将 Hibernate OGM Core/Hibernate OGM Core 和 MongoDB 库添加到您的项目库中,您可以轻松地将 Hibernate OGM/MongoDB 发行版集成到您的任何 NetBeans 项目中。

完整的应用可以在 Apress 库中找到。这是一个名为 Maven_HOGM 的 NetBeans 项目。

使用 Eclipse IDE 获得 Hibernate OGM 发行版

如果你是一个 Eclipse 粉丝,从 Eclipse Maven 项目中使用 Maven 要简单得多。本节描述了创建这样一个项目的主要步骤,以获得作为 Eclipse 库的 Hibernate OGM 发行版,以便在其他项目中使用。因此,启动 Eclipse(我们在 Eclipse JUNO 上进行了测试),并遵循以下步骤:

  1. From the File menu, select New | Other. In the New wizard, expand the Maven node and select Maven Projectas shown in Figure 1-19. Click Next.

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 1-19 。用 Eclipse JUNO 创建一个新的 Maven 项目

    如果您的 Eclipse 发行版中没有 Maven,您可以下载一个独立的 Maven 发行版并从Window | Preferences | Maven | Installations安装它,或者您可以从Eclipse Marketplace安装 Maven for Eclipse,您可以在Help菜单中找到它。一旦你在市场中找到 Maven,按照向导完成安装(参见图 1-20 )。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 1-20 。用 Eclipse JUNO 创建一个新的 Maven 项目

  2. 勾选标有Create a simple project (skip archetype selection)的方框。可以选择默认工作区,点击Next

  3. 键入组 id ( maven.hibernate.ogm)和工件 id ( Maven_HOGM)。点击Finish按钮,等待项目成功创建并在Package Explorer面板中列出。

  4. 在 maven 本地存储库中手动更新或创建settings.xml文件。

  5. Maven_HOGM项目中找到pom.xml并双击它。

  6. Next, in the editor, switch to the pom.xml tab where you’ll see a pom.xml skeleton. Add to it the missing parts from your pom.xml and save the project (see Figure 1-21).

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 1-21 。在 Eclipse JUNO 中编辑 pom.xml 文件

  7. Package Explorer面板中,右击项目名称并选择Run As | Maven build。当流程成功结束时,您应该会在由pom.xml中的<directory>标签定义的路径下看到 Hibernate OGM 发行版(包括依赖项)。

  8. Window菜单中选择Preferences。在左边的树中,展开Java | Build Path节点,选择User Libraries

  9. 点击New按钮创建一个新的库。为新库键入一个名称,比如 Hibernate OGM Core 和 MongoDB,然后单击OK

  10. 单击Add External JARs按钮,导航到下载 Hibernate OGM 发行版的文件夹。选择所有的 jar 并将它们添加到库中。点击OK

现在,通过将 Hibernate OGM Core/Hibernate OGM Core 和 MongoDB 库添加到您的项目构建路径中,您可以轻松地将 Hibernate OGM/MongoDB 发行版集成到您的任何 Eclipse 项目中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意如果您更喜欢用 Maven 创建整个项目,只需相应地添加 Hibernate OGM 依赖项。你所要做的就是添加相应的<repository><dependency>标签。

完整的应用可以在 Apress 库中找到。这是一个名为 Maven_HOGM 的 Eclipse 项目。

获取 MongoDB 发行版

在写这本书的时候,推荐的 MongoDB 发行版是版本 2.2.2(我选择这个版本是因为它是 Hibernate OGM 和 OpenShift 的“首选”)。您可以在官方网站http://www.mongodb.org/轻松下载。您将在http://docs.mongodb.org/manual/installation/找到安装步骤。本书中的示例是在 64 位版本的 Windows 7 和 8 下开发和测试的,安装非常简单。

下载并安装 MongoDB 发行版后,您就可以看到 MongoDB 服务器是否启动并响应命令了。打开命令提示符,导航到{MONGODB_HOME}/bin文件夹并键入mongod --dbpath ../命令来启动服务器(--dbpath选项指示您按照安装指南在{MONGODB_HOME}文件夹中手动创建的/data/db文件夹的位置)。如果没有错误,打开另一个命令提示符,导航到同一个文件夹,然后键入mongo。如果你看到类似于图 1-22 所示的东西,那么 MongoDB 已经成功安装了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-22 。检查 MongoDB 服务器可用性

要进行更彻底的测试,请尝试http://docs.mongodb.org/manual/tutorial/getting-started/的入门教程中的命令。按下CTRL-C就可以轻松关闭 MongoDB 服务器。

摘要

在这一介绍性章节中,我们迈出了理解和使用 Hibernate OGM 的第一步。我们研究了 Hibernate OGM 的概念、特性和目标,并简要概述了 Hibernate OGM 的架构。(如果你想理解下一章,知道事情是如何在内部管理是很重要的)。

然后,您看到了如何获得作为 ZIP/TGZ、命令行 Maven 项目和基于 NetBeans/Eclipse Maven 的项目的 Hibernate OGM 发行版。最后,您学习了如何安装 MongoDB 发行版,以及如何将相应的 jar 添加到 Hibernate OGM 发行版中。`

二、Hibernate OGM 和 MongoDB

到目前为止,您应该对 Hibernate OGM 的一般范围和架构有了一些了解。在第一章的中,我讨论了 Hibernate OGM 如何与一般的 NoSQL 存储一起工作,我谈到了它的一般焦点以及如何表示、持久化和查询数据。此外,您学习了如何获得 Hibernate OGM 发行版,并且安装了一个 MongoDB NoSQL 存储,执行了一个简单的命令行测试来验证 MongoDB 服务器是否正确响应。

在这一章中,我将更清楚地定义 Hibernate OGM 和 MongoDB 之间的关系。我将重点介绍 Hibernate OGM 如何与 MongoDB store 一起工作,而不是一般的可能性,您将看到 Hibernate OGM 可以“吞噬”多少 MongoDB,以及迫使 Hibernate OGM 加班管理它们的一些 MongoDB 缺点。

配置 MongoDB-Hibernate OGM 属性

当您提供一组配置属性时,Hibernate OGM 会意识到 MongoDB。如果您以前使用过 Hibernate ORM,那么您应该已经熟悉了这些类型的属性。具体来说,有三种设置这些属性的方法,您将在下一章中看到:

  • 声明性的,通过hibernate.cfg.xml配置文件
  • 以编程方式,通过 Hibernate 本机 API
  • 声明性的,通过 JPA 上下文中的persistence.xml配置文件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意记住,我们使用的是 Hibernate OGM 4 . 0 . 0 beta 2 和用于 MongoDB 4 . 0 . 0 . 0 . beta 1 的 Hibernate OGM 以及用于 MongoDB 2.8.0 的 Java 驱动程序。

让我们看看使 Hibernate OGM 能够与 MongoDB 一起工作的属性。

hibernate.ogm.datastore.provider

正如你从第一章中了解到的,Hibernate OGM 目前支持几个 NoSQL 商店,包括 MongoDB。这个属性值是让 Hibernate OGM 知道您想要使用哪个 NoSQL 存储的方式。对于 MongoDB,该属性的值必须设置为mongodb

hibernate.ogm.mongodb.host

接下来,Hibernate OGM 需要定位 MongoDB 服务器实例。首先,它必须定位主机名,主机名由托管 MongoDB 实例的机器的 IP 地址表示。默认情况下,该属性的值为127.0.0.1,相当于 localhost,也可以通过 MongoDB 驱动程序设置:

Mongo mongo = new Mongo("127.0.0.1");
Mongo mongo = new Mongo(new ServerAddress( "127.0.0.1"));

hibernate.ogm.mongodb.port

没有端口的主机名是什么?默认情况下,MongoDB 实例运行在端口号27017,上,但是您可以使用任何其他 MongoDB 端口,只要您将它指定为该属性的值。如果您直接使用 MongoDB 驱动程序,端口通常是这样设置的:

Mongo mongo = new Mongo("127.0.0.1", 27017);
Mongo mongo = new Mongo( new ServerAddress("127.0.0.1", 27017));

hibernate.ogm.mongodb.database

现在 Hibernate OGM 可以通过它的主机和端口定位 MongoDB。您还必须指定要连接的数据库。如果您指定了一个不存在的数据库名称,将自动创建一个具有该名称的新数据库(该属性没有默认值)。您还可以使用 MongoDB 驱动程序进行连接,如下所示:

DB db = mongo.getDB(" *database_name*");
Mongo db = new Mongo( new DBAddress( "127.0.0.1", 27017, " *database_name*" ));

hibernate.ogm.mongodb.username
hibernate.ogm.mongodb.password

这两个属性代表认证凭证。它们没有缺省值,通常一起出现在 MongoDB 服务器上对用户进行身份验证(尽管如果设置了密码而没有设置用户名,Hibernate OGM 会忽略the hibernate.ogm.mongodb.password属性)。您还可以使用 MongoDB 驱动程序来设置身份验证凭证,如下所示:

boolean auth = db.authenticate(" *username* ", " *password* ".toCharArray());

hibernate.ogm.mongodb.safe

注意,这个属性有点棘手。MongoDB 不擅长事务;它不执行回滚,也不能保证插入的数据确实在数据库中,因为驱动程序在返回之前不会等待应用写操作。在巨大的速度优势背后——由驱动程序执行对 MongoDB 服务器的写操作这一事实导致——隐藏着一个可能丢失数据的危险陷阱。

MongoDB 团队知道这个缺点,所以开发了一个名为写关注点的新特性来告诉 MongoDB 一段数据有多重要。这也用来表示数据的初始状态,默认写,(WriteConcern.NORMAL )。

MongoDB 定义了几个级别的数据重要性,但是 Hibernate OGM 允许您在默认的写入和安全写入之间切换。

使用写安全,驱动程序不会立即返回;它等待写操作成功,然后返回。显然,这可能会对性能产生严重影响。您可以使用hibernate.ogm.mongodb.safe属性设置该值。默认情况下,这个属性的值是true,这意味着写安全是活动的,但是如果写丢失不是您的主要问题,您可以将它设置为false

下面是如何直接使用 MongoDB 驱动程序来设置写安全:

DB db = mongo.getDB(" *database_name* ");
DBCollection dbCollection = db.getCollection(" *collection_name* ");
dbCollection.setWriteConcern(WriteConcern.SAFE);
dbCollection.insert( *piece_of_data* );
//or, shortly
dbCollection.insert( *piece_of_data* , WriteConcern.SAFE);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意目前 Hibernate OGM 只让你启用写安全 MongoDB 写关注(WriteConcern.SAFE)。因此,像写 FSYNC_SAFE ( WriteConcern.FSYNC_SAFE)、写 JOURNAL_SAFE ( WriteConcern.JOURNAL_SAFE)和写多数(WriteConcern.MAJORITY)这样的策略只能通过 MongoDB 驱动程序来控制。

hibernate.ogm.mongodb.connection_timeout

MongoDB 为不同种类的耗时操作支持一些超时选项。目前,Hibernate OGM 通过这个属性公开 MongoDB 选项connectTimeout(参见com.mongodb.MongoOptions)。这用毫秒表示,表示启动与 MongoDB 实例的连接时驱动程序使用的超时。默认情况下,Hibernate OGM 将其设置为 5000 毫秒,以覆盖驱动程序默认值 0(这意味着没有超时)。您可以按如下方式设置该属性:

mongo.getMongoOptions().connectTimeout= *n_miliseconds* ;

hibernate.ogm.mongodb.associations.store

该属性定义 Hibernate OGM 存储关联相关信息的方式。可接受的值有:IN_ENTITY, COLLECTION, and GLOBAL_COLLECTION。我将在本章稍后讨论这三种策略。

hibernate.ogm.datastore.grid_dialect

这是一个可选属性,通常会被忽略,因为数据存储提供者会自动选择最佳的网格方言。但是如果您想覆盖推荐值,您必须指定GridDialect实现的完全限定类名。对于 MongoDB,正确的值是org.hibernate.ogm.dialect.mongodb.MongoDBDialect

这是 Hibernate OGM 用来配置到 MongoDB 服务器的连接的一组属性。至此,您已经获得了创建与 MongoDB 服务器良好通信的基本设置。在未来的 OGM 版本中,我们希望能够访问 MongoDB 驱动程序的更多设置。

数据存储表示

如你所知,关系数据模型在 MongoDB 方面是没用的,MongoDB 是基于文档的数据库系统; MongoDB 中的所有记录(数据)都是文档。但是,即使如此,MongoDB 也必须在关系术语和它自己的概念之间保持概念上的一致。因此,MongoDB 不使用,而是使用集合,不使用记录,而是使用文档(集合包含文档)。MongoDB 文档是 BSON (二进制 JSON——类 JSON 文档的二进制编码序列化)对象,具有以下结构:

{
   field1: value1,
   field2: value2,
   field3: value3,
   ...
   fieldN: valueN
}

存储实体

好的,但是我们仍然在存储和检索 Java 实体,对吗?是的,答案肯定是肯定的!如果说 Hibernate ORM 提供了将 Java 实体转换成关系表的完整支持,那么 Hibernate OGM 提供了将 Java 实体转换成 MongoDB 集合的完整支持。每个实体代表一个 MongoDB 集合;每个实体实例代表一个 MongoDB 文档;并且每个实体属性将被翻译成一个文档字段(见图 2-1 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-1 。在 MongoDB 文档中存储 Java 对象

Hibernate OGM 团队努力为 MongoDB 尽可能自然地存储数据,以便第三方应用可以在没有 Hibernate OGM 帮助的情况下利用这些数据。例如,假设我们有一个 POJO 类,类似于清单 2-1 中的那个。(我确信您已经在关系数据库中存储了大量这样的 Java 对象,所以我不提供这个简单类的细节。)

清单 2-1。 一波乔类

import java.util.Date;

public class Players {

    private int id;
    private String name;
    private String surname;
    private int age;
    private Date birth;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }
}

现在,假设使用 Hibernate OGM 将这个 POJO 的实例存储到 MongoDB players集合中,如下所示:

{
        "_id": 1,
        "age": 26,
        "birth": ISODate("1986-06-03T15:43:37.763Z"),
        "name": "Nadal",
        "surname": "Rafael"
}

如果您使用以下命令通过 MongoDB shell 手动存储,这正是您所获得的结果:

>db.players.insert(
                  {
                        _id: 1,
                        age: 26,
                        birth: new ISODate("1986-06-03T15:43:37.763Z"),
                        name: "Nadal",
                        surname: "Rafael"
                  }
                       )

实际上,结果没有什么不同。您无法判断文档是由 Hibernate OGM 生成的还是通过 MongoDB shell 插入的。太好了!此外,Hibernate OGM 知道如何将这个结果转换回 POJO 的实例。那就更厉害了!而且您不会感到任何编程上的不适,因为 Hibernate OGM 不需要您编写任何底层 MongoDB 代码。那是最棒的!

存储主键 s

MongoDB 文档或集合具有非常灵活的结构。它支持简单的对象:在其他对象和数组中嵌入对象和数组;同一馆藏中不同种类的文献;而且它还包含一个专门为存储主键而保留的文档字段。这个字段被命名为_id,它的值可以是任何信息,只要它是唯一的。如果不将_id设置为任何值,该值将自动设置为“MongoDB Id Object”。

Hibernate OGM 在将标识符存储到 MongoDB 数据库时会识别这些规范;它允许您使用任何 Java 类型的标识符,甚至是复合标识符,并且它总是将它们存储在保留的_id字段中。

图 2-2 显示了不同 Java 类型的一些标识符,以及它们在 MongoDB 中的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-2 。Java 风格的主键和 MongoDB 标识符之间的对应

存储关联

可能关系数据库最强大的特性依赖于关联。任何有意义功能的数据库都可以利用关联:一对一、一对多、多对一和多对多。在关系模型中,关联需要存储附加信息,称为关联的导航信息。

例如,在双向多对多关联中,关系模型通常使用三个表,两个数据表和一个附加表,称为连接表。连接表包含一个组合键,该组合键由引用两个数据表主键的两个外键字段组成(参见图 2-3 )。请注意,同一对外键只能出现一次。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-3 。双向多对多关联,以关系模型表示形式显示

在 MongoDB 多对多关联中,您将连接表存储为文档。Hibernate OGM 提供了三种解决方案来完成这个任务:IN_ENTITY, COLLECTIONGLOBAL_COLLECTION。为了更好地理解这些策略,让我们临时准备一个简单的场景——两个关系表(PlayersTournaments)分别填充了三个玩家、两个锦标赛和一个多对多关联,如图图 2-4 所示。(第一个和第二个玩家P1P2参加了两个锦标赛T1T2,第三个玩家(P3)只参加第二个锦标赛T2。或者,从关联的另一方来看,第一个锦标赛T1包括第一和第二个玩家P1P2,第二个锦标赛T2包括第一、第二和第三个玩家P1P2P3。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-4 。关系模型表示中的双向多对多关联—测试用例

现在,让我们用这个测试用例来看看 Hibernate OGM 存储关联的策略。我们希望观察连接表是如何基于所选策略存储在 MongoDB 中的。我们将从默认策略IN_ENTITY,开始,接着是GLOBAL_COLLECTION,,最后是COLLECTION

在 JPA 术语中,表示这个关系模型的主要方式有:Players实体定义了一个名为idPlayers的主键字段,是关联的所有者;Tournaments实体定义了一个名为idTournaments的主键,并且是关联的非所有者方——它包含了mappedBy元素。而且,Players实体定义了一个Tournaments的 Java 集合,命名为tournaments,Tournaments实体定义了一个Players的 Java 集合,命名为players

身份

存储关联导航信息的默认策略名为IN_ENTITY 。在这种情况下,Hibernate OGM 将关联另一端的主键(外键)存储到:

  • 如果映射涉及单个对象,则为字段。
  • 如果映射涉及集合,则为嵌入式集合。

使用IN_ENTITY策略运行 MongoDB 的关系场景,结果如图 2-5 和图 2-6 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-5 。Hibernate OGM-IN_ENTITY 策略结果(玩家集合)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-6 。Hibernate OGM-IN_ENTITY 策略结果(锦标赛集合)

图 2-5 显示了Players关系表对应的 MongoDB Players集合;如您所见,每个集合的文档都包含作为嵌入集合的关联部分。(Players集合包含连接表中引用Tournaments集合的部分。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意从 shell 中探索 MongoDB 集合的最简单方法是调用find方法,该方法返回指定集合中的所有文档。此外,调用pretty方法会导致输出被很好地格式化。当一个集合包含的文档多于一个 shell 窗口所能容纳的文档时,您需要键入it命令,该命令支持文档分页。

Players集合显示了三个主文档,其中_id设置为 1、2 和 3,每个文档都在一个字段中封装了相应的外键,该字段的命名类似于所有者端声明的 Java 集合(tournaments)。嵌入集合中的每个文档都包含一个存储在字段中的外键值,该字段的名称由所有者端声明的 Java 集合名称(tournaments)和下划线以及非所有者端主键字段名(idTournaments)连接而成。

对应于Tournaments关系表的Tournaments集合就像是Players集合的反映——Players主键变成了Tournaments外键(Tournaments集合包含了引用Players集合的连接表部分)。图 2-6 显示了Tournaments集合的内容。

Tournaments集合包括两个主文档,其中_id被设置为 1 和 2。每一个都将相应的外键封装在一个字段中,该字段的名称类似于非所有者端声明的 Java 集合(players)。嵌入集合的每个文档都包含一个存储在字段中的外键值,该字段的名称由非所有者端声明的 Java 集合名称(players)与下划线和所有者端主键字段名(idPlayers)连接而成。

在单向情况下,只有代表所有者端的集合将包含关联的导航信息。

您可以通过将hibernate.ogm.mongodb.associations.store配置属性设置为值IN_ENTITY来使用这种存储关联导航信息的策略。实际上,这是该属性的默认值。

全局 _ 集合

当您不想将关联的导航信息存储到实体的集合中时,您可以选择GLOBAL_COLLECTION策略(或者COLLECTION,您将在下一节中看到)。在这种情况下,Hibernate OGM 创建了一个名为Associations的额外集合,专门用于存储所有导航信息。该集合的文档具有由两部分组成的特殊结构。第一部分包含一个复合标识符_id,由两个字段组成,它们的值代表关联所有者的主键和关联表的名称;第二部分包含一个名为rows的字段,它在一个嵌入式集合中存储外键。对于双向关联,创建另一个文档,其中 id 是相反的。

为 MongoDB 和GLOBAL_COLLECTION策略运行我们的关系场景揭示了如图 2-7 和图 2-8 所示的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-7 。Hibernate OGM-GLOBAL_COLLECTION 策略结果(玩家和锦标赛集合)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-8 。Hibernate OGM-GLOBAL_COLLECTION 策略结果(关联集合)

在图 2-7 中,可以看到PlayersTournaments集合只包含纯信息,没有导航信息。

包含导航关联的额外的、唯一的集合被命名为Associations,并在图 2-8 中列出。

这是一个双向的关联。车主侧(Players)映射在图 2-8 的左侧,非车主侧(Tournaments)映射在图 2-8 的右侧。在单向关联中,只存在所有者一方。

现在,关注第一个_id字段下的嵌套文档(图 2-8 ,左侧)。第一个字段名players_idPlayers,由非所有者端定义的相应 Java 集合名(players)组成,或者,对于单向关联,由表示所有者端的集合名(Players)和表示所有者端主键的字段名(idPlayers)组成。第二个字段名是table;它的值由表示所有者端的集合名和下划线以及表示非所有者端的集合名组成(Players_Tournaments)。rows嵌套集合包含每个外键一个文档。每个外键都存储在一个字段中,该字段的名称由所有者端(tournaments)中定义的相应 Java 集合名和下划线以及非所有者端(idTournaments)的主键字段名组成。双向性的结果是,事情发生了逆转,如图 2-8 右侧所示。

通过将hibernate.ogm.mongodb.associations.store配置属性设置为值GLOBAL_COLLECTION,可以使用这种策略来存储关联的导航信息。

收藏

如果GLOBAL_COLLECTION将所有导航信息存储在一个全局集合中,那么COLLECTION策略就不那么全局了,并且为每个关联创建一个 MongoDB 集合。例如,在我们的场景中,将有一个名为associations_Players_Tournaments的额外集合。在这个策略中,每个集合都以单词associations为前缀,后跟关联表的名称。使用这个约定可以很容易地将associations集合与其他集合区分开来。

该集合的文档具有由两部分组成的特殊结构。第一部分包含关联所有者的主键,第二部分包含一个名为rows的字段,它将所有外键存储在一个嵌入式集合中。对于每个外键,嵌入式集合中都有一个文档。对于双向情况,创建另一个文档,其中 id 是相反的。

如果您熟悉关系模型,这种策略应该更接近您的体验。在图 2-9 中,可以看到associations_Players_Tournaments集合的部分内容——车主端的导航信息(Players)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-9 。hibernate OGM-收集策略结果(协会 _ 玩家 _ 锦标赛收集)

您可以很容易地看到集合结构与GLOBAL_COLLECTION案例中的相同。惟一的区别是 _id 字段不再包含名为table的字段中的关联表名称,这是合乎逻辑的,因为关联表名称是集合名称的一部分(associations_Players_Tournaments)。

您可以通过将hibernate.ogm.mongodb.associations.store配置属性设置为值COLLECTION来使用这种存储关联导航信息的策略。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意基于这个例子,你可以很容易地直觉到一对一、一对多和多对一情况下的关联是如何表示的。请记住,集合和字段名可以被 JPA 注释修改,比如@Column, @Table, @JoinTable等等。我给出的例子没有使用这样的注释。

从 JPA 的角度来看,当双向关联没有定义拥有方(使用mappedBy元素)时,Hibernate OGM 认为每一方都是一个单独的关联。换句话说,在这种情况下,你将获得两个关联,而不是一个。例如,COLLECTION策略将产生两个集合来存储两个关联。

现在,由您来决定哪种策略更符合您的需求。

管理交易

在从关系模型系统切换到像 Mongo DB 这样的 NoSQL 平台之前,理解它们之间的差异以及它们在您的应用需求环境中的优缺点是很重要的。只知道 MongoDB 不支持 SQL,而关系模型不支持集合和文档,会导致应用实现出现严重问题。这实际上是两者之间的根本区别,但还有许多其他的区别,包括消耗的空间量和执行语句、缓存、索引以及可能是最痛苦的事务管理所需的时间。

当开发人员意识到数据事务完整性是必须的时,许多 MongoDB 先锋项目都悲惨地失败了,因为 MongoDB 不支持事务。MongoDB 遵循这个指令:"写操作在单个文档的层次上是原子的:没有一个写操作可以原子地影响多个文档或多个集合。“它还提供了两阶段提交机制,用于模拟多个文档上的事务。你会在www.docs.mongodb.org/manual/tutorial/perform-two-phase-commits/找到更多细节。但是这两种机制都忽略了事务系统最强大的特性——回滚操作。

因此,如果您需要事务,使用 MongoDB 可能是一个微妙甚至不合适的选择。MongoDB 不是 SQL 的“时尚”选择,只有当它比 RDBMS 更能满足您的应用需求时,才应该使用它。当您的数据库模型不隐含事务或者当您可以使您的数据库模型不需要事务时,您应该选择 MongoDB。

Hibernate OGM 不能提供回滚功能,但是通过在刷新期间应用更改之前查询所有更改,它确实减少了事务问题。为此,OGM 建议使用事务分界来触发提交时的刷新操作。

管理查询

Hibernate OGM 提供了三种针对 MongoDB 数据库执行查询的解决方案:

  • 部分 JP-QL 支持
  • Hibernate 搜索
  • 原生 MongoDB 查询

这些都将在第六章中讨论和演示。

摘要

虽然这是一个简短的章节,但它包含了大量的信息。我介绍了管理 Hibernate OGM 和 MongoDB 之间关系的规则。您看到了如何从 Hibernate OGM 配置 MongoDB,以及如何根据 OGM 实现将数据持久化到 MongoDB 中。此外,我描述了事务的 MongoDB 视图,并以 Hibernate OGM 支持的查询机制的快速枚举作为结束。

三、引导 Hibernate OGM

由于 Hibernate OGM 充当 NoSQL 数据存储的 JPA 实现,很明显我们可以通过 JPA 引导它。此外,它也可以通过 Hibernate 本地 API 进行引导。无论您选择哪种方式来引导 Hibernate OGM,强烈建议您在 Java 事务 API (JTA)环境中使用它,即使您没有使用 Java EE。

在进入实际的引导过程之前,让我们先简要地看一下这些规范。在接下来的章节中,您会希望记住这些技术的主要特性。当然,如果你已经是一个大师,你可以直接跳过。

JPA 简要概述

Java 持久性 API 的目标是为存储、更新和映射关系数据库和 Java 对象之间的数据的操作提供支持,反之亦然。你可以说 JPA 是决定直接使用对象而不是 SQL 语句(ORM 范式)的开发人员的完美工具。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 对象关系映射是一种编程技术,它在关系数据库和面向对象编程语言之间提供了一个虚拟的对象层。编程语言通过这一层读写关系数据库。您不用编写 SQL 语句来与数据库交互,而是使用对象。此外,代码更加清晰易读,因为它没有使用 SQL 语句进行“检测”。在写这本书的时候,JPA 规范有几个实现或*持久性提供者。*有些是受欢迎的、经过测试的、稳定的(EclipseLink、Hibernate 和 Apache OpenJPA),而有些可能不太常见,但具有非常高的基准性能(BatooJPA)。EclipseLink 是 JPA 的参考实现,它可以在 Java EE 环境和独立的 Java 应用中工作,每个 JPA 实现都应该这样。

JPA 易于使用,这要归功于持久性元数据 定义了 Java 对象和数据库表*之间的关系。*您可能熟悉语言级的持久性元数据,如 JDK 5.0 注释或 XDoclet 样式的注释,它们是类型安全的,并在编译时进行检查。可以说 JPA 注释实际上是普通的 JDK 5.0 注释。有些隐藏复杂的任务。一个这样的注释是javax.persistence.Entity ( @Entity注释),它用于标记应该在数据库中持久化的 POJO Java 类——用@Entity注释的每个类都存储在一个表中,每个表行都是一个实体类实例。实体必须定义主键(一个简单或复杂的主键,如果存在@GeneratedValue注释,则显式指定或自动生成)。实体不能是最终的,并且必须定义不带参数的构造函数。表名可以反映类名,也可以通过@Table注释显式提供,如@Table(name=" my_table_name ")

一个实体类定义了一组字段,每个字段默认为一个表中与该字段同名的列;您可以使用@Column注释对此进行更改,例如@Column(name=" my_column_name ")。JPA 可以通过 gettersetter 方法访问字段。默认情况下,用@Transient标注的字段不会被持久化,而其他字段会被持久化。

实体类是定义类(表)之间关系的地方。类与其他类可以有一对一(@OneToOne)、一对多(@OneToMany)、多对一(@ManyToOne)和多对多(@ManyToMany)的关系。当两个类存储彼此的引用时,这种关系是双向的,你必须用元素mappedBy在另一个类中指定关系的拥有方。当引用只是从一个类到另一个类而不是相反时,关系是单向的并且mappedBy元素是不必要的。

一旦有了反映数据库表的实体,就需要一个实体管理器(应用和持久性上下文之间的接口,),Hibernate 文档将其描述为“一组实体实例,其中对于任何持久性实体标识都有一个唯一的实体实例”,或者更简洁地说,一个实体管理器的所有实体能够提供存储、检索、合并和查找数据库中对象的方法。实际上,这是在 Java EE 环境中自动提供的javax.persistence.EntityManager,比如 GlassFish 或 JBoss。如果您在非 Java EE 环境中,比如 Tomcat 或 Java SE,您必须自己管理EntityManager生命周期。

可以由给定的EntityManager实例管理的一组实体(通常是逻辑相关的)被定义为一个持久性单元 ,其中的每一个都有一个惟一的名称,并驻留在一个名为 persistence.xml 的 XML 文档中。 Persistence.xml 是 JPA 的标准配置文件。它包含 JPA 提供者、JTA 或非 JTA 数据源、数据库连接信息,比如驱动程序、用户、密码、DDL 生成等等。(在 Java SE 应用中,该文件通常保存在源目录中名为 META-INF 的文件夹中,而在 web 应用中,该文件通常保存在 /src/conf 文件夹中,但是,根据应用的架构,它也可以位于其他位置)。一个 persistence.xml 文件可以包含多个持久性单元;根据您的应用使用的数据库,服务器将知道对哪个数据库执行查询。换句话说,通过一个持久性单元,应用用来获取应用管理的实体管理器的EntityManagerFactory被配置用于一组实体。您可以将此视为在 JPA 中实例化EntityManagerFactory的一种可移植的方式。

图 3-1 显示了 JPA 架构的主要组件之间的关系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-1 。JPA 体系结构的主要组件之间的关系

嗯,真够快的。现在让我们来看看 JTA。

JTA 概述

Java 事务 API (JTA) 支持分布式事务。基本上,一个事务由一组任务(例如,SQL 语句)组成,它们必须作为一个不可分割的单元来处理。这是一个原子操作,事实上,“一个任务对所有人,所有任务对一个人”的规则是事务的首要原则。交易以 ACID 属性为特征,如下所示:

  • 原子性要求如果任何一个任务失败,那么事务失败,并且它被回滚。如果所有任务都成功执行,则事务被提交。换句话说,交易是一个要么全有要么全无的命题。

  • 一致性确保任何提交的事务将以有效状态离开数据库(根据所有定义的规则,写入的数据必须有效)。

  • 隔离意味着你的交易是你的,是你一个人的;没有其他事务可以接触它,因为数据库使用锁定机制来保护事务,直到它成功或失败地结束。有四种隔离级别:

  • Read Uncommitted: 您的事务可以读取其他事务的未提交数据(在多线程环境中从不推荐)。

  • Read Committed: 你的事务永远不能读取其他事务的未提交数据。

  • Repeatable: 您的事务将在多次读取相同的行时获得相同的数据,直到它结束。

  • Serializable: 这种隔离级别保证了您所接触的一切(所有表)在事务处理期间保持不变。这是最严格的隔离级别,开销最大,导致的性能瓶颈也最多。

  • 耐久性保证系统崩溃后,任何提交的交易都是安全的。

这些概念非常重要,因为事务通常会修改共享资源。

一般来说,有两种管理交易的方式:

  • Container Managed Transactions (CMT) use deployment descriptors or annotations (transaction attributes). In this case, the container is responsible for starting, committing, and rolling back a transaction. This is the declarative technique of demarcating transactions. In EJB containers, you can explicitly indicate a container-managed transaction using the annotation @TransactionManagement, like this:

    @TransactionManagement(TransactionManagementType.CONTAINER)

  • Moreover, you can tell the EJB container how to handle the transaction via the @TransactionAttribute annotation, which supports six values: REQUIRED (default), REQUIRES_NEW, SUPPORTS, MANDATORY, NOT_SUPPORTED, NEVER. For example, you can set MANDATORY like this:

    @TransactionAttribute(TransactionAttributeType.MANDATORY)

  • Bean Managed Transactions (BMT) require you to explicitly (programmatically) start, commit, and roll back transactions. This is the programmatic technique of demarcating transactions. In EJB containers, you can explicitly indicate a bean-managed transaction via the annotation @TransactionManagement, like this:

    @TransactionManagement(TransactionManagementType.BEAN)

有两种类型的交易:

  • 本地事务访问并更新单个网络资源(一个数据库)上的数据。
  • 分布式事务访问和更新两个或多个网络资源(多个数据库)上的数据。

从编程角度来说,JTA 是一个基于三个主要接口访问事务的高级 API :

  • UserTransaction:``javax.transaction.UserTransaction接口允许开发者以编程方式控制事务边界。为了划分 JTA 事务,您调用该接口的begincommitrollback方法。
  • TransactionManager:``javax.transaction.TransactionManager允许应用服务器控制事务边界。
  • XAResource:``javax.transaction.xa.XAResource是基于 X/Open CAE 规范的标准 XA 接口的 Java 映射。你可以在www.en.wikipedia.org/wiki/X/Open_XAwww.docs.oracle.com/javaee/6/api/javax/transaction/xa/XAResource.html找到更多关于 XA 的细节。

这是对 JTA 的一瞥。

MongoDB 和事务

MongoDB 不支持事务,这似乎是一种限制,取消了任何潜在的好处。MongoDB 仅在更改影响单个文档或单个文档的多个子文档时支持原子性。当更改(比如写操作)影响多个文档时,它们是而不是自动应用的,这可能导致数据不一致、其他操作交错等等。显然,由于对多个文档的更改不是原子性的,回滚是不适用的。

MongoDB 在一致性和持久性方面做得更好。MongoDB 写操作可以跨连接保持一致。此外,MongoDB 支持接近实时的复制,因此可以确保操作在返回之前已经被复制。

Hibernate OGM 减轻了 MongoDB 对事务支持的不足,它在刷新时应用更改之前对所有更改进行排队。即使 MongoDB 不支持事务,Hibernate OGM 也建议使用事务分界来透明地触发刷新操作(在提交时)。但是,正如官方文档所指出的,回滚不是一个选项。因此,本书中开发的应用将使用 Hibernate OGM 推荐的 JTA。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意根据我提到的限制,很容易得出结论,MongoDB 不能满足我们应用的需求。但是,让我们考虑一下为什么我们会得出这样的结论。我们是否过于沉迷于复杂的数据库模式设计,有许多需要事务的连接和表,以及难以编写和管理的查询?在这里讨论这些问题远不是我的目的,但是也许你会花一点时间去思考它们,并为你的应用找到正确的答案。

Hibernate 原生 API 简介

直接使用 Hibernate API 的应用被称为本地 Hibernate 应用。开发一个本地 Hibernate 应用由几个简单的步骤组成,在这些步骤中,您可以:

  • 定义持久性类
  • 指定属性和映射文档
  • 将这些加载到应用的配置中
  • 基于这个配置,创建一个会话工厂
  • 从会话工厂获取(打开)会话
  • 执行查询和事务

本地 API 的起点和核心是org.hibernate.cfg.Configuration类,它使用属性和映射文档()。properties.cfg.xmlhbm.xml 文件)来创建org.hibernate.SessionFactory,一个线程安全的对象,它被实例化一次,并提供一个工厂来获取会话(org.hibernate.Session)。Session实例用于执行交易(JTA)和/或查询。

图 3-2 表示 Hibernate 原生 API 架构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-2 。Hibernate 本地 API 架构

使用 JPA 引导 Hibernate OGM

使用 JPA 引导 Hibernate OGM 是最简单的情况,因为 Hibernate OGM 充当持久性提供者。如前所述,持久性提供者是在持久性单元内的 persistence.xml 文件中指定的。 persistence.xml JTA 或非 JTA;特定于数据库的要求;服务器配置;诸如此类。我试图为 Hibernate OGM 编写一个包含最低强制设置的 persistence.xml 文件。

  1. The first step is to write a persistence.xml skeleton, which (in a Java SE/EE application) generally looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0" FontName2">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>
    

    这个文件通常保存在源目录中一个名为 META-INF 的文件夹中,尽管在 web 应用中它通常保存在 /src/conf 文件夹中。

    接下来,您添加一个持久性单元;你想叫它什么都可以。JPA 实现可以通过 RESOURCE_LOCAL 自己管理事务,或者让应用服务器的 JTA 实现管理事务。您使用transaction-type属性来指定实体管理器工厂为持久性单元提供的实体管理器应该是 JTA 的还是资源本地的。这里我将把事务类型表示为JTA,因为我们想要使用 JTA 实体管理器。(无论服务器环境如何,Hibernate OGM 都推荐使用 JTA)。

    <persistence-unit name=" *{PU_NAME}*" transaction-type="JTA">
    </persistence-unit>
    ...
    

    记住不要使用RESOURCE_LOCAL(一个资源本地实体管理器),因为它使用基本的 JDBC 级事务,并且更特定于 Java SE 应用,而 JTA 是 Java EE 环境中的缺省值。

  2. 现在您需要指定持久性提供者。您可能熟悉用于 GlassFish v3 的 EclipseLink 2.0、用于 JBoss AS 7 的 Hibernate 4、用于 WebSphere 6 和 7 的 OpenJPA 以及用于 WebLogic 的 OpenJPA/KODO。对于 Hibernate OGM,提供者被命名为org.hibernate.ogm.jpa.HibernateOgmPersistence,它可以被显式地添加到 persistence.xml 中,比如:

    ...
    <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
    ...
    
  3. 现在我们来到了 persistence.xml 的属性部分。使用hibernate.transaction.jta.platform设置的第一个属性是 JTA 平台。该属性可以有以下值(这些类属于 Hibernate 核心;它们是部署在不同应用服务器上的事务管理器):

  • JBoss 应用服务器 7 ( www.jboss.org/as7 ) 1 org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform
  • Bitronix JTA 交易管理器(www.docs.codehaus.org/display/BTM/Home ) org.hibernate.service.jta.platform.internal.BitronixJtaPlatform
  • Borland 企业服务器 6.0 ( www.techpubs.borland.com/am/bes/v6/ ) org.hibernate.service.jta.platform.internal.BorlandEnterpriseServerJtaPlatform
  • JBoss 事务(已知与org.jboss.jbossts:jbossjta:4.9.0.GA一起工作的独立 JTA 事务管理器);不适用于 Jboss AS 7) ( www.jboss.org/jbosstm ) org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform
  • 乔纳斯·OSGi 企业服务器(OW2) ( www.jonas.ow2.org/xwiki/bin/view/Main/ ) org.hibernate.service.jta.platform.internal.JOnASJtaPlatform
  • Java 开放事务管理器(JOTM),一个独立的事务管理器(www.jotm.objectweb.org/ ) org.hibernate.service.jta.platform.internal.JOTMJtaPlatform
  • JRun 4 应用服务器(www.adobe.com/products/jrun/ ) org.hibernate.service.jta.platform.internal.JRun4JtaPlatform
  • NoJtaPlatform类,未配置 JTA 时使用的无操作版本(www.docs.jboss.org/hibernate/orm/4.0/javadocs/org/hibernate/service/jta/platform/internal/NoJtaPlatform.html ) org.hibernate.service.jta.platform.internal.NoJtaPlatform
  • Oracle 应用服务器 10 g (OC4J) ( www.oracle.com/technetwork/middleware/ias/index-099846.html ) org.hibernate.service.jta.platform.internal.OC4JJtaPlatform
  • Caucho 树脂应用服务器(www.caucho.com/ ) org.hibernate.service.jta.platform.internal.ResinJtaPlatform
  • Sun ONE Application Server 7(该事务管理器也与 GlassFish v3 应用服务器一起工作)(www.docs.oracle.com/cd/E19957-01/817-2180-10/pt_chap1.html ) org.hibernate.service.jta.platform.internal.SunOneJtaPlatform
  • Weblogic 应用服务器(www.oracle.com/us/products/middleware/cloud-app-foundation/weblogic/overview/index.html ) org.hibernate.service.jta.platform.internal.WeblogicJtaPlatform
  • WebSphere 应用服务器版本 6 ( www-01.ibm.com/software/webservers/appserv/was/ ) org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform
  • WebSphere 应用服务器版本 4、5.0 和 5.1 ( www-01.ibm.com/software/webservers/appserv/was/ ) org.hibernate.service.jta.platform.internal.WebSphereJtaPlatform
  • 事务管理器查找桥,一个到遗留(和废弃的)org.hibernate.transaction.TransactionManagerLookup实现(www.docs.jboss.org/hibernate/orm/4.0/javadocs/org/hibernate/service/jta/platform/internal/TransactionManagerLookupBridge.html ) org.hibernate.service.jta.platform.internal.TransactionManagerLookupBridge的桥
  • 猎户座应用服务器——这个服务器好像已经不存在了org.hibernate.service.jta.platform.internal.OrionJtaPlatform

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意请记住,这些值在本书写作时是有效的。它们在 Hibernate 4.1 中是可用的,但是很有可能在将来会改变。你可以在www.docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html_single/Hibernate 开发者指南中查看这个列表。

这里有一个为柯乔树脂设置 JTA 平台的例子:

...
<property name="hibernate.transaction.jta.platform"
         value="org.hibernate.service.jta.platform.internal.ResinJtaPlatform"/>
...

接下来的五个属性配置使用哪个 NoSQL 数据存储以及如何连接到它。例如,您可以通过设置数据存储提供者、网格方言(可选)、数据库、主机和端口来连接到现成的 MongoDB 发行版,如下所示:

...
<property name="hibernate.ogm.datastore.provider" value="mongodb"/>
<property name="hibernate.ogm.datastore.grid_dialect"
value="org.hibernate.ogm.dialect.mongodb.MongoDBDialect"/>
<property name="hibernate.ogm.mongodb.database" value="test"/>
<property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
<property name="hibernate.ogm.mongodb.port" value="27017"/>
...

就这样!现在我们可以把这些片段粘在一起,为开箱即用的 MongoDB 提供一个通用的 persistence.xml ,如清单 3-1 所示。在下一章中,我们将修改这个文件以适应不同的环境。

清单 3-1。 一个通用的 persistence.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" FontName2">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=" *{PU_NAME}*" transaction-type="JTA">
    <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
    <properties>
      <property name="hibernate.transaction.jta.platform"
                value=" *{JTA_PLATFORM}*"/>
      <property name="hibernate.ogm.datastore.provider" value="mongodb"/>
      <property name="hibernate.ogm.datastore.grid_dialect"
                value="org.hibernate.ogm.dialect.mongodb.MongoDBDialect"/>
      <property name="hibernate.ogm.mongodb.database" value="test"/>
      <property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
      <property name="hibernate.ogm.mongodb.port" value="27017"/>
    </properties>
  </persistence-unit>
</persistence>

使用 Hibernate Native API 引导 Hibernate OGM

前面,您看到了通过简单的几个步骤就可以开发一个本地 API 应用。其中三个步骤——加载属性并将文件映射到应用中;为当前配置创建全局线程安全SessionFactory;以及通过SessionFactory获得Session s(单线程工作单元)——通常在众所周知的HibernateUtil类中实现。(你可以写这个类,但是你也可以在网上找到不同的“形状”)不变的是,在这个类中,你会有一些类似这样的代码行(对于 Hibernate 3):

private static final SessionFactory sessionFactory;
...
sessionFactory = new Configuration().configure().buildSessionFactory();
...

看第二行,它通过一个org.hibernate.cfg.Configuration类的实例构建了SessionFactory。实际上,这是设置 Hibernate OGM 与原生 API 一起工作的入口点,因为您需要使用org.hibernate.ogm.cfg.OgmConfiguration类,而不是使用特定于 Hibernate ORM 的org.hibernate.cfg.Configuration类。因此,第二行将变成:

...
sessionFactory = new OgmConfiguration().configure().buildSessionFactory();
...

从 Hibernate 4 开始,这段代码将给出一个关于不推荐使用的方法buildSessionFactory()的警告。在这种情况下,javadoc 建议使用形式buildSessionFactory(ServiceRegistry serviceRegistry)。因此,如果您使用 Hibernate 4(推荐),请用下面的代码替换前面的代码:

private static final SessionFactory sessionFactory;
private static final ServiceRegistry serviceRegistry;
...
OgmConfiguration cfgogm = new OgmConfiguration();
cfgogm.configure();
serviceRegistry = new ServiceRegistryBuilder().
applySettings(cfgogm.getProperties()).buildServiceRegistry();
sessionFactory = cfgogm.buildSessionFactory(serviceRegistry);
...

这种方法(使用 Hibernate 3 或 4)需要一个包含特定配置的 hibernate.cfg.xml 文件。对于 Hibernate OGM,该文件需要包含正确的事务策略和正确的事务管理器查找策略。您必须通过设置 Hibernate 配置属性 hibernate.transaction.factory_class来为Transaction实例指定一个工厂类。可接受的值为:

  • org.hibernate.transaction.JDBCTransactionFactory—这是默认值,它代表数据库(JDBC)事务。
  • org.hibernate.transaction.JTATransactionFactory—使用 bean 管理的事务,这意味着您必须手动划分事务边界。
  • org.hibernate.transaction.CMTTransactionFactory—该值委托给容器管理的 JTA 事务。

通过编程,您可以像这样实现此设置:

...
OgmConfiguration cfgogm = new OgmConfiguration();
...
cfgogm.setProperty(Environment.TRANSACTION_STRATEGY,
" *{TRANSACTION_STRATEGY}*");
...

接下来,您必须通过设置名为hibernate.transaction.jta.platform 的属性来指定 JTA 平台。该属性的值必须由查找实现的完全限定类名组成。前面的“使用 JPA 引导 Hibernate OGM”一节中列出了可接受的值。

通过编程,您可以像这样实现此设置:

...
OgmConfiguration cfgogm = new OgmConfiguration();
...
cfgogm.setProperty(Environment.JTA_PLATFORM," *{JTA_PLATFORM}*");
...

最后,您需要配置您想要使用哪个 NoSQL 数据存储库以及如何连接到它。

对于开箱即用的 MongoDB 发行版,您需要设置数据存储提供者、网格方言(可选)、数据库、主机和端口,如下所示:

...
<property name="hibernate.ogm.datastore.provider">mongodb</property>
<property name="hibernate.ogm.mongodb.database">test</property>
<property name="hibernate.ogm.datastore.grid_dialect">
                org.hibernate.ogm.dialect.mongodb.MongoDBDialect</property>
<property name="hibernate.ogm.mongodb.host">127.0.0.1</property>
<property name="hibernate.ogm.mongodb.port">27017</property>
...

通过编程,您可以用清单 3-2 中的代码实现这些设置。

清单 3-2。 将 MongoDB 配置为数据存储

...
OgmConfiguration cfgogm = new OgmConfiguration();
...
cfgogm.setProperty("hibernate.ogm.datastore.provider","mongodb");
cfgogm.setProperty("hibernate.ogm.mongodb.database","test");
cfgogm.setProperty("hibernate.ogm.datastore.grid_dialect ","
                    org.hibernate.ogm.dialect.mongodb.MongoDBDialect");
cfgogm.setProperty("hibernate.ogm.mongodb.host","127.0.0.1");
cfgogm.setProperty("hibernate.ogm.mongodb.port","27017");
...  

因此,如果您使用非编程设置,那么 hibernate.cfg.xml 可能如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" " http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd ">
<hibernate-configuration>
  <session-factory>
    <property name="hibernate.transaction.factory_class">
       { *TRANSACTION_STRATEGY* }
    </property>
    <property name="hibernate.transaction.jta.platform">
       *{JTA_PLATFORM}*
    </property>
    <property name="hibernate.ogm.datastore.provider">mongodb</property>
    <property name="hibernate.ogm.mongodb.database">test</property>
    <property name="hibernate.ogm.datastore.grid_dialect">
       org.hibernate.ogm.dialect.mongodb.MongoDBDialect</property>
    <property name="hibernate.ogm.mongodb.host">127.0.0.1</property>
    <property name="hibernate.ogm.mongodb.port">27017</property>
    <mapping resource="..."/>
    ...
  </session-factory>
</hibernate-configuration>

清单 3-3 显示了使用这个配置文件的 HibernateUtil 类。

清单 3-3。 ibernateUtil

import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.ogm.cfg.OgmConfiguration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

/**
 * HibernateUtil class (based on hibernate.cfg.xml)
 *
 */
public class HibernateUtil {

    private static final Logger log = Logger.getLogger(HibernateUtil.class.getName());
    private static final SessionFactory sessionFactory;
    private static final ServiceRegistry serviceRegistry;

    static {
        try {
            // create a new instance of OmgConfiguration
            OgmConfiguration cfgogm = new OgmConfiguration();

            //process configuration and mapping files
            cfgogm.configure();
            // create the SessionFactory
            serviceRegistry = new ServiceRegistryBuilder().
                 applySettings(cfgogm.getProperties()).buildServiceRegistry();
            sessionFactory = cfgogm.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
            log.log(Level.SEVERE,
                    "Initial SessionFactory creation failed !", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

如果你使用的是编程设置,你不需要一个 hibernate.cfg.xml 文件,你的 HibernateUtil 将会如清单 3-4 所示。

清单 3-4。?? 一个不需要 Hibernate.cfg.xml 的 HibernateUtil 类

import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Environment;
import org.hibernate.ogm.cfg.OgmConfiguration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

/**
 * HibernateUtil class (no need of hibernate.cfg.xml)
 *
 */
public class HibernateUtil {

    private static final Logger log = Logger.getLogger(HibernateUtil.class.getName());
    private static final SessionFactory sessionFactory;
    private static final ServiceRegistry serviceRegistry;

    static {
        try {
            // create a new instance of OmgConfiguration
            OgmConfiguration cfgogm = new OgmConfiguration();

            // enable transaction strategy
            cfgogm.setProperty(Environment.TRANSACTION_STRATEGY,
                                                "{ *TRANSACTION_STRATEGY*}");
            // specify JTA platform
            cfgogm.setProperty(Environment.JTA_PLATFORM, "{ *JTA_PLATFORM*}");

            //configure MongoDB connection
            cfgogm.setProperty("hibernate.ogm.datastore.provider", "mongodb");
            cfgogm.setProperty("hibernate.ogm.datastore.grid_dialect",
              "org.hibernate.ogm.dialect.mongodb.MongoDBDialect");
            cfgogm.setProperty("hibernate.ogm.mongodb.database", "test");
            cfgogm.setProperty("hibernate.ogm.mongodb.host", "127.0.0.1");
            cfgogm.setProperty("hibernate.ogm.mongodb.port", "27017");

            //add our annotated class
            cfgogm.addAnnotatedClass(*.class);

            // create the SessionFactory
            serviceRegistry = new ServiceRegistryBuilder().
               applySettings(cfgogm.getProperties()).buildServiceRegistry();
            sessionFactory = cfgogm.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
            log.log(Level.SEVERE,
                "Initial SessionFactory creation failed !", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

现在,图 3-2 中呈现的 Hibernate Native API 可以重绘成图 3-3 中的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-3 。Hibernate OGM 中的 Hibernate Native API 架构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意使用默认配置(org/hibernate/ogm/datastore/Infinispan/default-config . XML)设置 infini span 可以通过将hibernate.ogm.datastore.provider属性的值指定为infinispan来完成。通过将相同的属性设置为Ehcache,您可以使用默认配置(org/hibernate/ogm/datastore/Ehcache/default-Ehcache . XML)设置 Ehcache。对于这两个 NoSQL 产品,Hibernate OGM 还支持一个特定的属性来指示 XML 配置文件。对于 Infinispan,该属性称为hibernate . ogm . Infinispan . configuration _ resourcename,对于 Ehcache,该属性称为hibernate . ogm . Ehcache . configuration _ resourcename。因此,对于 Infinispan 和 Ehcache,不需要设置方言、数据库、端口和主机。

Hibernate OGM 过时配置选项

随着 Hibernate OGM 的出现,Hibernate ORM 中的一组选项不再可用。因此,根据 Hibernate OGM 规范,以下选项不应在 OGM 环境中使用:

  • hibernate.dialect
  • hibernate.connection.*特别是hibernate.connection.provider_class
  • hibernate.show_sql and hibernate.format_sql
  • hibernate.default_schema and hibernate.default_catalog
  • hibernate.use_sql_comments
  • hibernate.jdbc.*
  • hibernate.hbm2ddl.autohibernate.hbm2ddl.import_file

摘要

在简要了解了 Java 持久性 API (JPA) 、Java 事务 API (JTA) 和 Hibernate 本机 API 之后,您看到了如何使用 JPA 和 Hibernate 本机 API 引导 Hibernate OGM。您了解了如何编写一个通用的 persistence.xml,以及如何为 Hibernate OGM 实现一个 HibernateUtil 类。最后,您看到了 Hibernate OGM 中不再提供的 Hibernate ORM 配置属性列表。

12013 年 4 月 Red Hat,Inc .宣布下一代 JBoss 应用服务器将被称为 Wildfly。参见http://gb.redhat.com/about/news/press-archive/2013/4/red-hat-reveals-plans-for-its-next-generation-java-application-server-project

四、实际场景中的 Hibernate OGM

到目前为止,您已经了解了 Hibernate OGM 可以通过 Java 持久性 API 或 Hibernate 本机 API 来使用。此外,您了解了实现 Hibernate OGM 引导的原理,并且已经查看了一些相关的代码片段。显然,从这些代码片段跳到真正的应用需要的不仅仅是复制和粘贴,因为您必须处理集成过程以及每个环境的特定特性和设置。

试图给出一个完全符合每个程序员需求的例子是不可救药的,但是我能做的是提供一系列使用 Hibernate OGM 的例子。在本章中,我将向您展示一些现成的 Hibernate OGM 应用,它们可以部署在 Java EE 6 服务器(如 JBoss 和 GlassFish)和 Web 服务器(如 Tomcat)上,使用 Seam 和 Spring 等框架以及 EJB 等规范。除了直接与 Hibernate OGM 交互的内核技术之外,我们还将使用一些开发工具,如 NetBeans 和 Eclipse 等 ide,以及 Maven、JBoss Forge、Ant 等,它们帮助我们以最少的工作量构建应用。把这些工具看作是我的选择,而不是必须的。您可以使用产生相同结果的任何其他工具。

整套应用共享一些简单的业务逻辑,在 MongoDB 集合中存储一个随机整数——我们称这个整数为幸运数字。正如您将看到的,存储的整数甚至不是用户请求的;它是在用户按下按钮时随机生成的(每次按下按钮都会生成并存储一个新的整数)。这个琐碎的业务逻辑的要点是尽可能保持应用代码简单,并关注 Hibernate OGM 与上下文的集成。我们真正关心的是成功地将 Hibernate OGM 绑定到应用上下文,并设置与 MongoDB 的交互。在后面的章节中,我们将有足够的时间来讨论 MongoDB 的高级设置、存储原则、JP-QL、Hibernate 搜索等等。

一般先决条件

在我们开始之前,确保您已经正确安装了 MongoDB(正如您在第一章中看到的),并且您有可用的 Hibernate OGM JARs,包括 MongoDB 支持所需的 jar(本地或通过 Maven 工件)。其余的工具,比如应用服务器、框架、ide 等等,可以根据你的需求单独安装;你可能不会对下面所有的例子感兴趣。在任何情况下,为了测试本章中完整的应用套件,您将需要以下内容:

  • Java EE 6
  • JDK 1.7
  • GlassFish AS 3(与 NetBeans 7.2.1 或 7.3 捆绑在一起)
  • JBoss AS 7(应单独安装)
  • Apache Tomcat 7(与 NetBeans 7.2.1 或 7.3 捆绑在一起)
  • NetBeans 7.2.1 或 7.3(建议与 GlassFish AS 3 和 Tomcat 7 一起使用)
  • Eclipse JUNO (JBoss AS 7 可以通过 JBoss AS 工具在这个 Eclipse 发行版下进行配置)
  • MongoDB 2.2.2(你应该从第一章的开始安装)
  • Hibernate OGM 4.0.0.Beta2(来自第一章,你应该有一个名为 Hibernate OGM Core 和 MongoDB 的 NetBeans 和 Eclipse 库)
  • MongoDB Java 驱动程序 2.8.0(这存在于 Hibernate OGM 核心和 MongoDB 库中)
  • JBoss JTA 4.16.4 最终版本
  • Forge 1.0.5 或 1.3.1(独立或作为 Eclipse 插件运行)
  • Spring 3.3.1(与 NetBeans 7.2.1 或 7.3 捆绑在一起)

此外,在您开始之前,您可能会发现了解以下内容会有所帮助:

  • 本章介绍的每个应用都可以从 Apress 资源库下载。每个应用都有一小段描述应用名称和测试的技术条件。换句话说,你没有必要在阅读这本书的时候重新构建每一个应用,除非你想这么做。
  • 这些示例向您展示了如何在涉及多种技术的不同类型的应用中集成 MongoDB 和 Hibernate OGM。如您所知,这样的应用需要许多附加文件——XML 配置文件、XHTML 页面、servlets、托管 beans、控制器等等。我试图保持代码尽可能的干净,以便更容易理解如何集成 MongoDB 和 Hibernate OGM,所以我跳过了不相关的“意大利面条”代码。此外,我不会试图教您如何创建 servlet、会话 bean 或 XHTML 页面,或者如何编写 web.xml 文件。我假设您已经知道如何使用 NetBeans、Eclipse 或其他 IDE。不要期望看到循序渐进的 NetBeans 或 Eclipse 教程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意对于作为 Apache Maven 项目开发的应用,不要忘记编辑settings.xml,就像你在第一章中看到的那样。或者,如果你认为settings.xml太冗长,你可以简单地在你的pom.xml中使用<repository>标签。但是请记住,缺少存储库会导致错误。

Java SE 和 MongoDB——Hello World 示例

我们将以一个例外开始我们的应用系列:第一个应用不涉及 Hibernate OGM。这个应用实际上只是一个快速测试,以确保 MongoDB 服务器正在运行并对连接尝试做出响应。考虑一下我们面向 Java-MongoDB 新手的 Hello World 应用。如果你认为这是浪费你的时间,你可以跳过它。要不,我们走吧!

这是有史以来最简单的 Java SE/MongoDB 示例——它只是将一个随机数存储到 MongoDB 集合中。

先决条件

  • MongoDB 2.2.2
  • MongoDB Java 驱动程序 2 . 8 . 0(mongo-Java-driver-2 . 8 . 0 . jar)
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)

发展中的

启动 NetBeans 后,创建一个由简单的 Maven Java 应用组成的新项目,并将其命名为HelloWorld。在New Java Application向导中,为Artifact Id键入HelloWorld,为Group IdPackage键入hello.world.mongodb。一旦您看到项目列在Projects窗口中,编辑pom.xml文件(它必须在Project Files节点下)。在pom.xml文件中,通过粘贴以下代码添加 MongoDB Java 驱动程序版本 2.8.0:

<dependencies>
   <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongo-java-driver</artifactId>
      <version>2.8.0</version>
</dependency>
..
<dependencies>

现在保存项目,驱动程序 JAR 将列在Dependencies节点下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意如果你不是 Maven 迷,创建一个简单的 Java 应用,从 GitHub 下载 MongoDB Java 驱动 2.8.0,https://github.com/mongodb/mongo-java-driver/downloads。显然,在这种情况下,你必须手动将其添加到Libraries节点。

现在必要的库已经可用了。接下来,编辑应用的主类。如果你没有重命名它,它会被列为hello.world.mongodb.helloworld包的Source Packages节点中的App.java。在main方法下,逐步插入以下代码:

  1. 在默认端口 270127

    Mongo mongo = new Mongo("127.0.0.1", 27017);
    

    上连接到本地主机(127.0.0.1)上的 MongoDB 存储

  2. 创建一个名为helloworld_db的 MongoDB 数据库。这个数据库很可能是由 MongoDB 自动创建的,因为它并不存在。

    DB db = mongo.getDB("helloworld_db");
    
  3. 创建一个名为helloworld的 MongoDB 集合。这个集合可能会由 MongoDB 在helloworld_db数据库中自动创建,因为它并不存在。

    DBCollection dbCollection = db.getCollection("helloworld");
    
  4. 创建用于存储键/值对的文档。密钥只是文本,值是生成的数字。

    BasicDBObject basicDBObject = new BasicDBObject();
     basicDBObject.put("Lucky number", new Random().nextInt(1000));
    
  5. 将该对保存到helloworld集合:

    dbCollection.insert(basicDBObject);
    

搞定了。

现在,把这五个步骤放在一起。清单 4-1 显示了结果。

***清单 4-1。***Hello World 举例

package hello.world.mongodb.helloworld;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import java.net.UnknownHostException;
import java.util.Random;

/**
 * Hello world!
 *
 */
public class App {

    public static void main(String[] args) {
        try {
            // connect to the MongoDB store
            Mongo mongo = new Mongo("127.0.0.1", 27017);

            // get the MongoDB database, helloworld_db
            DB db = mongo.getDB("helloworld_db");

            //get the MongoDB collection named helloworld
            DBCollection dbCollection = db.getCollection("helloworld");

            // create a document for storing a key/value pair
            BasicDBObject basicDBObject = new BasicDBObject();
            basicDBObject.put("Lucky number", new Random().nextInt(1000));

            // save the pair into helloworld collection
            dbCollection.insert(basicDBObject);

            System.out.println("MongoDB has stored the lucky number!");

        } catch (UnknownHostException e) {
            System.err.println("ERROR: " + e.getMessage());
        } catch (MongoException e) {
            System.err.println("ERROR: " + e.getMessage());
        }
    }
}

测试

按照第一章中的方法启动 MongoDB 服务器。接下来,由于您在 NetBeans(或 Eclipse)中,有一个Run按钮可以实现这一功能。Run应用,如果你得到消息“ MongoDB 已经存储了幸运数字!”,一切都完美地进行着。

打开命令提示符,键入图 4-1 所示的命令,查看您的工作结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-1 。查看“helloworld”收藏内容

如果您没有获得类似的结果,那么在创建下一个应用之前必须解决一个问题。

完整的 Hello World 应用可以在 Apress 存储库中获得,当然,它被命名为HelloWorld。它是一个 NetBeans 项目,在 JDK 1.7 和 MongoDB 2.2.2 下进行了测试。

通过 Hibernate Native API 使 OGM Hibernate

一旦您确认 MongoDB 已经准备好为您的应用提供服务,就该转向 Hibernate OGM 了。在这一节中,我们将使用 Hibernate 本机 API 开发一系列涉及 Hibernate OGM 的应用。以下是我们将开发的应用:

  • 在非 JTA 环境中 Hibernate OGM(JDBC 事务,Apache Tomcat 7)
  • 在独立的 JTA 环境中 Hibernate OGM(JBoss JTA,Apache Tomcat 7)
  • 在内置的 JTA 环境中 Hibernate OGM(没有 EJB,GlassFish AS 3)
  • 在内置的 JTA 环境中 Hibernate OGM(EJB 3/BMT,GlassFish AS 3)
  • 在内置的 JTA 环境中 Hibernate OGM(EJB 3/CMT,GlassFish AS 3)

非 JTA 环境中的 hibernate OGM(JDBC 事务,Apache Tomcat 7)

这个应用将通过 Hibernate Native API 在非 JTA 环境中引导 Hibernate OGM。我们将使用旧式的 JDBC 交易,而不是 JTA。实际上,我们将使用 Hibernate 的事务 API 和内置的每请求会话功能,而不是直接调用 JDBC API。该应用将部署在 Apache Tomcat 7 web 容器下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意当 Hibernate OGM 在非 JTA 环境中使用时,回滚特性不能得到保证。这就是为什么 Hibernate OGM 团队不推荐在 Hibernate OGM 4.0.0.Beta2 发行版中使用这种环境,但是这种情况有望在下一个发行版中变得更加有利。因为我们使用的是不支持事务的 MongoDB,所以这不是我们所关心的。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta2
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)
  • 阿帕奇雄猫 7

发展中的

启动 NetBeans 后,创建一个包含一个空 Maven web 应用的新项目,并将其命名为HOGMviaHNAPI_JDBC_Tomcat7。在New Web Application向导中,为Group IdPackage字段键入hogm.hnapi。不要忘记选择 Apache Tomcat 7 web 服务器来部署这个应用。一旦你看到在Projects窗口中列出的项目,编辑pom.xml文件(它必须在Project Files节点下)。在pom.xml文件中,通过粘贴以下依赖项来添加 Hibernate OGM 发行版(包括 MongoDB 支持):

<dependencies>
   <dependency>
      <groupId>org.hibernate.ogm</groupId>
      <artifactId>hibernate-ogm-core</artifactId>
      <version>4.0.0.Beta2</version>
   </dependency>
   <dependency>
      <groupId>org.hibernate.ogm</groupId>
      <artifactId>hibernate-ogm-mongodb</artifactId>
      <version>4.0.0.Beta1</version>
   </dependency>
...
<dependencies>

现在保存项目,MongoDB Java 驱动程序 JAR 将列在 Dependencies 节点下。

编写应用代码

现在我们准备添加一些代码。我们从一个简单的 POJO 类开始,它能够表示数据库中的对象。正如你在清单 4-2 中看到的,该类包含一个名为luckynumber的字段(除了主键字段)和众所周知的 getter 和 setter 方法。

清单 4-2。??【幸运数字】Pojo 类

package hogm.hnapi.pojo;

public class LuckyNumberPojo {

    private String id;
    private int luckynumber;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getLuckynumber() {
        return luckynumber;
    }

    public void setLuckynumber(int luckynumber) {
        this.luckynumber = luckynumber;
    }
}

大多数使用 Hibernate 的应用需要一个名为HibernateUtil,的特殊类,它是一个助手类,提供对代码中任何地方的SessionFactory的访问。互联网上有很多 Hibernate ORM 的版本,比如“买者自负”演示中的那个。对于 Hibernate OGM,我们可以基于 Hibernate ORM 的最简单版本开发一个HibernateUtil,它通常看起来像清单 4-3 中的所示。您可能对它很熟悉,并且在 Hibernate 3 中使用过多次。

清单 4-3。 一个基本的冬眠类用于冬眠 ORM

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

    private static final SessionFactory sessionFactory;

    static {
        try {
            sessionFactory = new
                             Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

现在,为 Hibernate OGM 开发一个HibernateUtil是基于这个源代码的两个主要修改的任务。首先,我们需要实例化用于配置 Hibernate OGM 环境的OgmConfiguration类,而不是创建一个Configuration类的新实例。其次,从 Hibernate 4 开始,会话工厂必须通过传递给buildSessionFactory方法的服务注册中心获得。记住这些,代码可以很容易地转换成 Hibernate OGM 的HibernateUtil,如清单 4-4 中的所示。

清单 4-4。 一个 HibernateUtil 类为 Hibernate OGM

package hogm.hnapi.util.with.hibernate.cfg;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.ogm.cfg.OgmConfiguration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil {

    private static final Logger log =
            Logger.getLogger(HibernateUtil.class.getName());
    private static final SessionFactory sessionFactory;
    private static final ServiceRegistry serviceRegistry;

    static {
        try {
            // create a new instance of OmgConfiguration
            OgmConfiguration cfgogm = new OgmConfiguration();

            // process configuration and mapping files
            cfgogm.configure();
            // create the SessionFactory
            serviceRegistry = new ServiceRegistryBuilder().
             applySettings(cfgogm.getProperties()).buildServiceRegistry();
            sessionFactory = cfgogm.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
            log.log(Level.SEVERE,
                    "Initial SessionFactory creation failed !", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

为了从这个HibernateUtil,获得有效的会话工厂,我们需要构建 Hibernate 配置文件(hibernate.cfg.xml)和相应的映射文件(*.hbm.xml)。如您所知,hibernate.cfg.xml文件包含了用于调整 Hibernate 环境和数据库连接的主要信息。因为我们处于非 JTA 环境中,并且遵循众所周知的 Hibernate 线程绑定策略(Hibernate 将当前会话绑定到当前 Java 线程),所以我们首先设置访问该策略所必需的两个属性:

<property name="hibernate.transaction.factory_class">
          org.hibernate.transaction.JDBCTransactionFactory
</property>
<property name="hibernate.current_session_context_class">
          thread
</property>

接下来的五个属性是我们的重中之重,因为它们代表了 MongoDB 配置,所以我们指定了数据存储提供者、方言、要连接的数据库的名称、MongoDB 服务器主机和端口(我们将使用 localhost 和 MongoDB 服务器的默认端口 27017):

<property name="hibernate.ogm.datastore.provider">mongodb</property>
<property name="hibernate.ogm.datastore.grid_dialect">
          org.hibernate.ogm.dialect.mongodb.MongoDBDialect</property>
<property name="hibernate.ogm.mongodb.database">tomcat_db</property>
<property name="hibernate.ogm.mongodb.host">127.0.0.1</property>
<property name="hibernate.ogm.mongodb.port">27017</property>

最后,我们添加映射资源,在本例中,它由单个类LuckyNumberPojo表示。添加最后一行:

<mapping resource="/LuckyNumberPojo.hbm.xml"/>

hibernate.cfg.xml的末尾,得到中所示的代码,清单 4-5 。

清单 4-5。 一个 Hibernate 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd ">
<hibernate-configuration>
  <session-factory>
    <property name="hibernate.transaction.factory_class">
              org.hibernate.transaction.JDBCTransactionFactory</property>
    <property name="hibernate.current_session_context_class">thread</property>
    <property name="hibernate.ogm.datastore.provider">mongodb</property>
    <property name="hibernate.ogm.datastore.grid_dialect">
              org.hibernate.ogm.dialect.mongodb.MongoDBDialect</property>
    <property name="hibernate.ogm.mongodb.database">tomcat_db</property>
    <property name="hibernate.ogm.mongodb.host">127.0.0.1</property>
    <property name="hibernate.ogm.mongodb.port">27017</property>
    <mapping resource="/LuckyNumberPojo.hbm.xml"/>
  </session-factory>
</hibernate-configuration>

当 web 应用启动时,文件hibernate.cfg.xml必须位于类路径的根目录中。在一个 Maven 项目中,就像这样,它应该保存在src/main/resources目录中(在 NetBeans 中,这个目录可以在Other Sources节点中找到)。在非 Maven 应用中,将文件保存在WEB-INF/classes目录中。

写作是我们的下一个目标。因为我们有一个普通的 POJO,所以任务很简单。首先,我们将主键字段和生成器描述为 UUID2。(这将生成符合 IETF RFC 4122(变体 2)的 128 位 UUID。更多详情请见www.ietf.org/rfc/rfc4122.txt。)然后我们描述luckynumber场。结果如清单 4-6 所示。

***清单 4-6。***luckynumberpojo . hbm . XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" " http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd ">
<hibernate-mapping>
    <class name="hogm.hnapi.pojo.LuckyNumberPojo" table="jdbc">
        <id name="id" type="string">
            <column name="id" />
            <generator class="uuid2" />
        </id>
        <property name="luckynumber" type="int">
            <column name="luckynumber"/>
        </property>
    </class>
</hibernate-mapping>

该文件应该放在与hibernate.cfg.xml.相同的文件夹中

赋值table="jdbc"在 MongoDB 中创建了一个名为jdbc的集合。如果你想创建一个名为 XXX .jdbc,的收藏,你可以像这样添加catalog=" XXX ",:

<class name="hogm.hnapi.pojo.LuckyNumberPojo" table="jdbc" catalog=" *XXX* ">

最后,我们已经到了可以添加一些业务逻辑的时候了。我们将编写一个 DAO 类,将幸运数字保存到数据库中。这样的类通常至少包含所有 CRUD 操作的方法。然而,我们需要的只是一个持久化操作的方法。实际上,persist 有两个实现,每个打开会话策略一个。如您所知,Hibernate 提供了用于获取当前会话的getCurrentSessionopenSession方法。调用getCurrentSession将 Hibernate 在后台绑定的“当前”会话返回到事务范围,或者在第一次调用getCurrentSession时打开一个新的会话。只要事务运行,该会话在代码中的任何地方都是可用的,并且当事务结束时,它会自动关闭并刷新。如果你想显式刷新和关闭会话,你必须使用openSession方法。清单 4-7 展示了我们的带有两个persist方法的 DAO 类,一个用于getCurrentSession,一个用于openSession。两者都使用声明性的事务边界划分,使用org.hibernate.Session方法,如beginTransactioncommit

清单 4-7。 刀法类用两种persist

package hogm.hnapi.dao;

import hogm.hnapi.pojo.LuckyNumberPojo;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.Session;

public class LuckyNumberDAO {

    private static final Logger log = Logger.getLogger(LuckyNumberDAO.class.getName());

     /**
     * Insert data (use getCurrentSession and POJO)
     *
     * @param transientInstance
     * @throws Exception
     */
    public void persist_cs_with_cfg(LuckyNumberPojo transientInstance) throws java.lang.Exception {

        log.log(Level.INFO, "Persisting LuckyNumberPojo instance ...");
        Session session = hogm.hnapi.util.with.hibernate.cfg.HibernateUtil.
                                      getSessionFactory().getCurrentSession();
        try {
             session.beginTransaction();
             session.persist(transientInstance);
             session.getTransaction().commit();

             log.log(Level.INFO, "Persist successful ...");
        } catch (RuntimeException re) {
            session.getTransaction().rollback();
            log.log(Level.SEVERE, "Persist failed ...", re);
            throw re;
        }
    }

    /**
     * Insert data (use openSession and POJO)
     *
     * @param transientInstance
     * @throws Exception
     */
    public void persist_os_with_cfg(LuckyNumberPojo transientInstance) throws java.lang.Exception {

        log.log(Level.INFO, "Persisting LuckyNumberPojo instance ...");
        Session session = hogm.hnapi.util.with.hibernate.cfg.HibernateUtil.
                                     getSessionFactory().openSession();

        try {
             session.beginTransaction();
             session.persist(transientInstance);
             session.flush(); // flush happens automatically anyway
             session.getTransaction().commit();

             log.log(Level.INFO, "Persist successful...");
        } catch (RuntimeException re) {
            session.getTransaction().rollback();
            log.log(Level.SEVERE, "Persist failed...", re);
            throw re;
        } finally {
            session.close();
        }
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意虽然这里没有列出,但是这个应用的源代码(可以在 Apress 资源库中找到)也演示了使用实体而不是 POJO,并用HibernateUtil类中的编程配置替换hibernate.cfg.xml

我们快完成了!我们剩下要实现的就是一个简单的用户界面和一个 servlet。当用户按下界面中的一个按钮时,一个空表单被提交给 servlet,servlet 调用我们的 DAO 类(persist_cs_with_cfgpersist_os_with_cfg)将生成的幸运数字存储到数据库中。servlet 的主要代码片段如清单 4-8 所示。

清单 4-8。 幸运数字 Servlet

package hogm.hnapi.servlet;
...
@WebServlet(name = "LuckyNumberServlet", urlPatterns = {"/LuckyNumberServlet"})
public class LuckyNumberServlet extends HttpServlet {
 ...
 protected void processRequest(HttpServletRequest request, HttpServletResponse
 response) throws ServletException, IOException, Exception {
   ...
   LuckyNumberDAO luckyNumberDAO = new LuckyNumberDAO();
   LuckyNumberPojo luckyNumberPojo = new LuckyNumberPojo();
   luckyNumberPojo.setLuckynumber(new Random().nextInt(1000000));

   luckyNumberDAO.persist_cs_with_cfg(luckyNumberPojo);
   // luckyNumberDAO.persist_os_with_cfg(luckyNumberPojo);
   ...
  }
}

提交给这个 servlet 的 HTML 表单也非常简单。代码放在index.jsp页面上,在 NetBeans 项目中,该页面列在项目的Web Pages节点下)。

...
<form action="./LuckyNumberServlet" method="POST">
    <input type="submit" value="Generate Lucky Number">
</form>
...
Done!

测试

启动 MongoDB 服务器,如你在第一章中所见。接下来,因为您处于 NetBeans/Tomcat(或 Eclipse/Tomcat)环境中,所以只需保存项目并单击Run(或 Eclipse 中的Run on Server)按钮来启动 Tomcat 并部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-2 所示的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-2 。运行 HOGMviaHNAPI_JDBC_Tomcat7 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(tomcat_db)集合(jdbc))中。打开命令提示符,输入如图 4-3 所示的命令,查看您的工作结果。这让您可以监控 Tomcat 日志消息,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-3 。正在检查“jdbc”收集内容

这个名为HOGMviaHNAPI_JDBC_Tomcat7的应用的完整源代码可以在 Apress 资源库中找到。它是一个 NetBeans 项目,在 Tomcat 7 下测试过。(我用的是 NetBeans 7.2.1 捆绑的 Tomcat。)

在独立的 JTA 环境中 Hibernate OGM(JBoss JTA,Apache Tomcat 7)

我们的下一个应用将在独立的 JTA 环境中通过 Hibernate Native API 引导 Hibernate OGM。正如您将看到的,Hibernate 可以在任何使用 JTA 的环境中工作,事实上,它可以自动将当前会话绑定到当前 JTA 事务。由于 Tomcat 不是 J2EE 环境,它不提供自动 JTA 事务管理器,所以我们必须选择 JTA 的独立实现。有几个开源的实现,如 JOTM,Bitronix JTA 和 Atomikos,但我更喜欢 JBoss JTA。它是著名的 JBoss TS(阿尔诸那事务服务)的一部分,附带了 JTA 和 JTS API 的一个非常健壮的实现。

现在让我们看看这个应用的先决条件是什么。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta2
  • JBoss JTA 4.16.4 最终版本
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)
  • 阿帕奇雄猫 7

发展中的

启动 NetBeans 并创建一个包含一个空 Maven web 应用的新项目,并将其命名为HOGMviaHNAPI_JTA_Tomcat7。在New Web Application向导中,为Group IdPackage字段键入hogm.hnapi。不要忘记选择 Apache Tomcat 7 web 服务器来部署这个应用。一旦您看到项目列在Projects窗口中,编辑pom.xml文件(它必须在Project Files节点下)。在pom.xml中,通过粘贴以下依赖项来添加 Hibernate OGM 发行版(包括 MongoDB 支持)。

<dependencies>
   <dependency>
      <groupId>org.hibernate.ogm</groupId>
      <artifactId>hibernate-ogm-core</artifactId>
      <version>4.0.0.Beta2</version>
   </dependency>
   <dependency>
      <groupId>org.hibernate.ogm</groupId>
      <artifactId>hibernate-ogm-mongodb</artifactId>
      <version>4.0.0.Beta1</version>
   </dependency>
...
<dependencies>

现在保存项目,驱动程序 JAR 将列在Dependencies节点下。

我们仍然需要在应用类路径中为 JBoss JTA 添加 jar,所以现在添加这个依赖项:

<dependencies>
   <dependency>
      <groupId>org.jboss.jbossts</groupId>
      <artifactId>jbossjta</artifactId>
      <version>4.16.4.Final</version>
   </dependency>
...
<dependencies>

编写应用代码

我们现在已经有了所有必要的工件,所以是时候开始开发应用了。首先,我们将创建一个基本的实体类,它可以表示数据库中的对象。该类将只包含一个名为luckynumber的字段(除了主键)。您应该熟悉这类实体,从技术上讲,它们只是带注释的 POJOs。(更多细节,请参考第二章。)清单 4-9 展示了LuckyNumberEntity类。

***清单 4-9。***lucky number entity 类

package hogm.hnapi.pojo;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name="jta")
public class LuckyNumberEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name="uuid", strategy="uuid2")
    private String id;

    @Column(name="luckynumber", nullable=false)
    private int luckynumber;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getLuckynumber() {
        return luckynumber;
    }

    public void setLuckynumber(int luckynumber) {
        this.luckynumber = luckynumber;
    }        
}

在前面的应用中,我们使用了一个简单的 POJO,并且我们开发了一个专门设计的HibernateUtil类,用于在基于hibernate.cfg.xml和映射文件的代码中的任何地方获得会话工厂。在这个应用中,我们将采用另一种方法——我们将使用一个实体(一个用 JDK 5 注解扩展的 POJO)和一个提供以编程方式配置的会话工厂的HibernateUtil。换句话说,没有hibernate.cfg.xml也没有映射文件。

有几个特定于我们的应用的配置属性。首先,我们通过将hibernate.transaction.factory_class设置为org.hibernate.transaction.JTATransactionFactory并将hibernate.current_session_context_class设置为jta,告诉 Hibernate 我们想要使用手动事务划分。从编程角度来说,这些属性被映射为org.hibernate.cfg.Environment类中的常量值:

...
// create a new instance of OmgConfiguration
OgmConfiguration cfgogm = new OgmConfiguration();

cfgogm.setProperty(Environment.TRANSACTION_STRATEGY,
                                 "org.hibernate.transaction.JTATransactionFactory");
cfgogm.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "jta");
...

接下来,我们指定 JTA 平台,JBoss JTA。为此,我们添加以下内容:

cfgogm.setProperty(Environment.JTA_PLATFORM,
"org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform");

注意,我们设置了 JBoss JTA 独立发行版,而不是 JBoss AS 使用的发行版。

根据 JBoss TS 文档,为了选择本地 JBoss JTA 实现,您需要指定两个属性,com.arjuna.ats.jta.jtaTMImplementationcom.arjuna.ats.jta.jtaUTImplementation。因为这些属性不是 Hibernate 环境的一部分,所以它们在Environment类中没有关联。您可以像这样指定它们:

cfgogm.setProperty("com.arjuna.ats.jta.jtaTMImplementation",
"com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple");
cfgogm.setProperty("com.arjuna.ats.jta.jtaUTImplementation",
"com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple");

接下来,我们配置 MongoDB 连接:数据存储提供者、方言、要连接的数据库的名称、主机和端口(我们将使用本地主机和默认的 MongoDB 服务器端口 27017):

cfgogm.setProperty("hibernate.ogm.datastore.provider", "mongodb");
cfgogm.setProperty("hibernate.ogm.datastore.grid_dialect",
                   "org.hibernate.ogm.dialect.mongodb.MongoDBDialect"); cfgogm.setProperty("hibernate.ogm.mongodb.database", "tomcat_db");
cfgogm.setProperty("hibernate.ogm.mongodb.host", "127.0.0.1");
cfgogm.setProperty("hibernate.ogm.mongodb.port", "27017");

最后,我们将实体添加到等式中,删除LuckyNumberEntity.hbm.xml映射文件:

cfgogm.addAnnotatedClass(hogm.hnapi.pojo.LuckyNumberEntity.class);

现在将所有这些配置属性添加到特定于 OGM 发行版的HibernateUtil类中,以获得清单 4-10 中所示的代码。请注意,我在前面的应用中更详细地讨论了这个类。

清单 4-10。 另一类冬眠动物

package hogm.hnapi.util.without.hibernate.cfg;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Environment;
import org.hibernate.ogm.cfg.OgmConfiguration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil {

    private static final Logger log = Logger.getLogger(HibernateUtil.class.getName());
    private static final SessionFactory sessionFactory;
    private static final ServiceRegistry serviceRegistry;

    static {
        try {
            // create a new instance of OmgConfiguration
            OgmConfiguration cfgogm = new OgmConfiguration();

            // enable JTA strategy
            cfgogm.setProperty(Environment.TRANSACTION_STRATEGY,
                                            "org.hibernate.transaction.JTATransactionFactory");
            cfgogm.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "jta");

            // specify JTA platform
            cfgogm.setProperty(Environment.JTA_PLATFORM,
                      "org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform");

            // in order to select the local JBoss JTA implementation it is necessary to specify these properties
            cfgogm.setProperty("com.arjuna.ats.jta.jtaTMImplementation",
                      "com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple");
            cfgogm.setProperty("com.arjuna.ats.jta.jtaUTImplementation",
                      "com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple");

            //configure MongoDB connection
            cfgogm.setProperty("hibernate.ogm.datastore.provider", "mongodb");
            cfgogm.setProperty("hibernate.ogm.datastore.grid_dialect",
                      "org.hibernate.ogm.dialect.mongodb.MongoDBDialect");
//you can ignore this setting
            cfgogm.setProperty("hibernate.ogm.mongodb.database", "tomcat_db");
            cfgogm.setProperty("hibernate.ogm.mongodb.host", "127.0.0.1");
            cfgogm.setProperty("hibernate.ogm.mongodb.port", "27017");

            //add our annotated class
            cfgogm.addAnnotatedClass(hogm.hnapi.pojo.LuckyNumberEntity.class);

            // create the SessionFactory
            serviceRegistry = new ServiceRegistryBuilder().applySettings(cfgogm.getProperties()).
                                                buildServiceRegistry();
            sessionFactory = cfgogm.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
            log.log(Level.SEVERE, "Initial SessionFactory creation failed !", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

到目前为止,我们已经有了实体和会话工厂提供者。下一部分非常有趣,因为我们开始开发 DAO 类。这意味着使用 JBoss JTA 来划分事务,为此我们关注两个 JBoss JTA 类:

  • com.arjuna.ats.jta.UserTransaction—该类自动将新创建的事务与调用线程相关联,并公开了用于控制事务边界的begin, commit,rollback等方法。它还提供了一个名为userTransaction的静态方法,该方法返回一个代表用户事务的javax.transaction.UserTransaction:

    javax.transaction.UserTransaction tx = com.arjuna.ats.jta.UserTransaction.userTransaction();
    
  • com.arjuna.ats.jta.TransactionManager—这是一个允许应用服务器控制事务边界的接口。它还提供了像begin, commit,rollback,这样的方法,但是它是专门为应用服务器设计的,应用服务器可以初始化事务管理器并调用它来为您划分事务。可以通过transactionManager的方法获得javax.transaction.TransactionManager,比如:

javax.transaction.TransactionManager tx = com.arjuna.ats.jta.TransactionManager.transactionManager();

如果你更喜欢用 Hibernate getCurrentSession的方法来获取当前的Session,,你可以使用 JBoss JTA 实现一个将幸运数字保存到数据库中的 DAO 方法,如清单 4-11 所示。

清单 4-11。 幸运数字道类 - getCurrentSession方法

package hogm.hnapi.dao;
...
public class LuckyNumberDAO {
 ...
 private static final Logger log =
   Logger.getLogger(LuckyNumberDAO.class.getName());
 ...
 public void persist_cs_without_cfg(LuckyNumberEntity transientInstance) throws
 java.lang.Exception {

        log.log(Level.INFO, "Persisting LuckyNumberEntity instance ...");

        // javax.transaction.TransactionManager tx =
                                        com.arjuna.ats.jta.TransactionManager.transactionManager();
        javax.transaction.UserTransaction tx = com.arjuna.ats.jta.UserTransaction.userTransaction();

        try {
            tx.begin();
            hogm.hnapi.util.without.hibernate.cfg.HibernateUtil.getSessionFactory().
                                                    getCurrentSession().persist(transientInstance);
            tx.commit();

            log.log(Level.INFO, "Persist successful...");
        } catch (RuntimeException re) {
            tx.rollback();
            log.log(Level.SEVERE, "Persist failed...", re);
            throw re;
        }
    }
}

但是,如果您想自己控制会话刷新和关闭,请选择 Hibernate openSession方法,它可以以几乎相同的方式与 JBoss JTA 交织在一起,如清单 4-12 中的所示。

清单 4-12。 幸运数字道类- openSession方法

package hogm.hnapi.dao;
...
public class LuckyNumberDAO {
 ...
 private static final Logger log =
   Logger.getLogger(LuckyNumberDAO.class.getName());
 ...
 public void persist_os_without_cfg(LuckyNumberEntity transientInstance) throws
 java.lang.Exception {

        log.log(Level.INFO, "Persisting LuckyNumberEntity instance ...");

        // javax.transaction.TransactionManager tx =
                                        com.arjuna.ats.jta.TransactionManager.transactionManager();
        javax.transaction.UserTransaction tx = com.arjuna.ats.jta.UserTransaction.userTransaction();
        Session session = hogm.hnapi.util.without.hibernate.cfg.HibernateUtil.
                                       getSessionFactory().openSession();

        try {
            tx.begin();
            session.persist(transientInstance);
            session.flush();
            tx.commit();

            log.log(Level.INFO, "Persist successful...");
        } catch (RuntimeException re) {
            tx.rollback();
            log.log(Level.SEVERE, "Persist failed...", re);
            throw re;
        } finally {
            session.close();
        }
    }
}

申请快完成了。它的主要部分是可用的,我们只需要添加一个 servlet 来调用 DAO 方法,以及一个简单的用户界面来向这个 servlet 提交一个空表单。来自LuckyNumberServlet的主要代码片段显示在清单 4-13 中。

***清单 4-13。***LuckyNumberServlet 的一个片段

package hogm.hnapi.servlet;
...
@WebServlet(name = "LuckyNumberServlet", urlPatterns = {"/LuckyNumberServlet"})
public class LuckyNumberServlet extends HttpServlet {
...

 protected void processRequest(HttpServletRequest request, HttpServletResponse
 response) throws ServletException, IOException, Exception {
  ...
  LuckyNumberDAO luckyNumberDAO = new LuckyNumberDAO();
  LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
  luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));

  luckyNumberDAO.persist_cs_without_cfg(luckyNumberEntity);
  // luckyNumberDAO.persist_os_without_cfg(luckyNumberEntity);
  ...
 }
}

下面是与这个 servlet 交互的表单(在 index.jsp 中):

...
<form action="./LuckyNumberServlet" method="POST">
   <input type="submit" value="Generate Lucky Number">
</form>
...

搞定了。

测试

首先启动 MongoDB 服务器,如第一章所述。接下来,因为您处于 NetBeans/Tomcat(或 Eclipse/Tomcat)环境中,所以只需保存项目并单击Run(或 Eclipse 中的Run on Server)按钮来启动 Tomcat 并部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-4 所示的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-4 。运行 HOGMviaHNAPI_JTA_Tomcat7 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(tomcat_db)集合(jta))中。打开命令提示符,输入如图 4-5 所示的命令,查看您的工作结果。和以前一样,您可以监控 Tomcat 日志消息,查看是否发生了任何不需要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-5 。检查 jta 集合内容

这个名为HOGMviaHNAPI_JTA_Tomcat7的应用的完整源代码可以在 Apress 资源库中找到。它是 NetBeans 项目,在 Tomcat 7 下测试过(我使用了 NetBeans 7.2.1 捆绑的 Tomcat)。

在内置的 JTA 环境中 Hibernate OGM(没有 EJB,GlassFish AS 3)

在前面的例子中,我们开发了一个基于独立 JTA 环境的应用。我们可以重用大部分代码来编写相同类型的应用,但是这次是基于内置的 JTA 环境提供者,比如 GlassFish v3 AS。您可能知道,这是一个完全兼容的 J2EE 应用服务器,它自动处理(通过 JTA TransactionManager)每个数据源的事务生命周期。换句话说,我们将开发与上一节相同的应用,但是我们将使用容器提供的 JTA 事务管理器,而不是使用和配置 JBoss JTA。请注意,我们仍然手动划分事务边界;这不是容器管理事务(CMT)策略。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta2
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)
  • 玻璃鱼 3.1.2.2

正在开发

启动 NetBeans 后,创建一个包含一个空 Maven web 应用的新项目,并将其命名为HOGMviaHNAPI_JTA_GlassFish3。在New Web Application向导中,为Group IdPackage字段键入hogm.hnapi,并选择 GlassFish web server 来部署该应用。现在,只需遵循上一节中的场景。我们将做一些小而重要的修改。

编写应用代码

在添加 Hibernate OGM/Mongo DB jar 之后(像前面的例子一样使用 Maven),创建相同的LuckyNumberEntity实体。继续编写清单 4-14 中所示的HibernateUtil类。

清单 4-14。 一个修改过的 HibernateUtil 类

package hogm.hnapi.util.without.hibernate.cfg;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Environment;
import org.hibernate.ogm.cfg.OgmConfiguration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil {

    private static final Logger log = Logger.getLogger(HibernateUtil.class.getName());
    private static final SessionFactory sessionFactory;
    private static final ServiceRegistry serviceRegistry;

    static {
        try {
            // create a new instance of OmgConfiguration
            OgmConfiguration cfgogm = new OgmConfiguration();

            // enable JTA strategy
            cfgogm.setProperty(Environment.TRANSACTION_STRATEGY,
                         "org.hibernate.transaction.JTATransactionFactory");
            cfgogm.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "jta");

            // specify JTA platform
            cfgogm.setProperty(Environment.JTA_PLATFORM,
                         "org.hibernate.service.jta.platform.internal.SunOneJtaPlatform");

            //configure MongoDB connection
            cfgogm.setProperty("hibernate.ogm.datastore.provider", "mongodb");
            cfgogm.setProperty("hibernate.ogm.datastore.grid_dialect",
                         "org.hibernate.ogm.dialect.mongodb.MongoDBDialect");
//you can ignore this setting
            cfgogm.setProperty("hibernate.ogm.mongodb.database", "glassfish_db");
            cfgogm.setProperty("hibernate.ogm.mongodb.host", "127.0.0.1");
            cfgogm.setProperty("hibernate.ogm.mongodb.port", "27017");

            //add our annotated class
            cfgogm.addAnnotatedClass(hogm.hnapi.pojo.LuckyNumberEntity.class);

            // create the SessionFactory
            serviceRegistry = new ServiceRegistryBuilder().applySettings(cfgogm.getProperties()).
                                         buildServiceRegistry();
            sessionFactory = cfgogm.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
            log.log(Level.SEVERE, "Initial SessionFactory creation failed !", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

如您所见,相关代码以粗体显示:

cfgogm.setProperty(Environment.JTA_PLATFORM,
             "org.hibernate.service.jta.platform.internal.SunOneJtaPlatform");

这段代码告诉 Hibernate 要使用的 JTA 平台。显然,我们希望使用内置的 JTA 平台,对于 GlassFish v3 来说,不需要任何库或 JAR 一切都由容器提供。通过查看第二章中的可用 JTA 平台列表,您可以很容易地为其他受支持的容器(JTA 内置平台)修改该属性(hibernate.transaction.jta.platform)。例如,如果您在 JBoss 7 AS 下部署这个应用,内置的 JTA 平台是org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform;不要将这个 JTA 与独立的 JBoss JTA 平台混淆。

如果你决定使用hibernate.cfg.xml,添加 JTA 平台,就像这样:

<property name="hibernate.transaction.jta.platform">
                org.hibernate.service.jta.platform.internal.SunOneJtaPlatform</property>

现在让我们开发 DAO 类。如果您跟踪了早期的应用,您会知道我们只关注在使用getCurrentSessionopenSession方法获得的 Hibernate 会话中将对象持久化到数据库中。如您所知,Hibernate 可以自动将当前会话绑定到当前 JTA 事务,但为此我们需要控制事务本身并添加相应的分界。为了在 J2EE 环境中完成这个任务,我们可以简单地利用标准的 JNDI 子上下文java:comp/UserTransaction.javax.transaction.UserTransaction应该在遵循 J2EE 规范的java:comp/UserTransaction,中可用:

UserTransaction tx = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction");

现在,对于getCurrentSession方法,我们可以调用清单 4-15 中的begin, commit,rollback方法。

清单 4-15。getCurrentSession接近

package hogm.hnapi.dao;
...
public class LuckyNumberDAO {
...
private static final Logger log =
   Logger.getLogger(LuckyNumberDAO.class.getName());
 ...
 public void persist_cs_without_cfg(LuckyNumberEntity transientInstance) throws
 java.lang.Exception {

        log.log(Level.INFO, "Persisting LuckyNumberEntity instance ...");

        UserTransaction tx = (UserTransaction) new
InitialContext().lookup("java:comp/UserTransaction");

        try {
            tx.begin();
            hogm.hnapi.util.without.hibernate.cfg.HibernateUtil.getSessionFactory().
                                                     getCurrentSession().persist(transientInstance);
            tx.commit();

            log.log(Level.INFO, "Persist successful...");
        } catch (RuntimeException re) {
            tx.rollback();
            log.log(Level.SEVERE, "Persist failed...", re);
            throw re;
        }
    }
}

或者,如果你喜欢使用清单 4-16 中的方法。

清单 4-16。openSession接近

package hogm.hnapi.dao;
...
public class LuckyNumberDAO {
...
private static final Logger log =
   Logger.getLogger(LuckyNumberDAO.class.getName());
 ...
 public void persist_os_without_cfg(LuckyNumberEntity transientInstance) throws
 java.lang.Exception {

        log.log(Level.INFO, "Persisting LuckyNumberEntity instance ...");

        UserTransaction tx = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction");
        Session session = hogm.hnapi.util.without.hibernate.cfg.HibernateUtil.
                                                                  getSessionFactory().openSession();

        try {
            tx.begin();
            session.persist(transientInstance);
            session.flush();
            tx.commit();

            log.log(Level.INFO, "Persist successful...");
        } catch (RuntimeException re) {
            tx.rollback();
            log.log(Level.SEVERE, "Persist failed...", re);
            throw re;
        } finally {
            session.close();
        }
    }
}

现在,整个 Hibernate OGM 机制已经设置好了。剩下的工作就是添加一个简单的用户界面,将一个“空”表单提交给一个与 DAO 类通信的基本 JSF bean(如果您不是 JSF 迷,可以用 servlet 代替它)。清单 4-17 显示了与 DAO 类交互的代码。

***清单 4-17。***TestManagedBean 类

package hogm.hnapi.jsf;
 ...
 public class TestManagedBean {
 ...
 public void persistAction() throws Exception {
  ...
  LuckyNumberDAO luckyNumberDAO = new LuckyNumberDAO();
  LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
  luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));

  luckyNumberDAO.persist_cs_without_cfg(luckyNumberEntity);
  // luckyNumberDAO.persist_os_without_cfg(luckyNumberEntity);
 ...
 }
}

这里是用户表单的代码,它在index.xhtml页面上:

...
<h:form>
   <h:commandButton action="#{bean.persistAction()}" value="Generate Lucky Number"/>
</h:form>
...

就这样!

测试

现在启动 MongoDB 服务器,就像你在第一章中看到的那样。接下来,因为您处于 NetBeans/GlassFish(或 Eclipse/GlassFish)环境中,所以只需保存项目并单击Run(或 Eclipse 中的Run on Server)按钮来启动 GlassFish 并部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-6 所示的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-6 。运行 HOGMviaHNAPI_JTA_GlassFish3 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(glassfish_db)集合(jta))中。打开命令提示符,键入图 4-7 中的命令,查看您的工作结果。您还可以监控 GlassFish 日志消息,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-7 。检查 jta 集合内容

这个应用的完整源代码被命名为HOGMviaHNAPI_JTA_GlassFish3,可以在 Apress 存储库中找到。它是一个 NetBeans 项目,并在 GlassFish 3 下进行了测试(我使用了与 NetBeans 7.2.1 捆绑在一起的 GlassFish)。

内置 JTA 环境中的 hibernate OGM(EJB 3/BMT,GlassFish AS 3)

在前面的示例中,我们开发了一个基于 GlassFish 3 内置 JTA 环境的应用。您看到了如何通过在 JNDI 子上下文java:comp/UserTransaction中查找来获得当前事务,以及如何在一个普通的 DAO 类中手工划分事务边界。现在我们将开发相同类型的应用,但是这次我们将使用一个被注释为 bean 管理事务(BMT)的 EJB 组件。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta1
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)
  • 玻璃鱼 3.1.2.2

正在开发

启动 NetBeans 后,创建一个包含一个空 Maven web 应用的新项目,并将其命名为HOGMviaHNAPI_JTA_EJB_BMT_GlassFish3。在New Web Application向导中,为Group IdPackage字段键入hogm.hnapi。不要忘记选择 GlassFish web 服务器来部署这个应用。请注意,即使我们要添加一个 EJB 组件,我们也不会创建一个企业应用来将 web 模块与 EJB 模块分开。我们更喜欢 web 应用,因为我们希望能够从 EJB 组件调用 web 组件。

编写应用代码

在添加 Hibernate OGM/Mongo DB jar 之后(像前面的例子一样使用 Maven)创建众所周知的LuckyNumberEntity实体(这次使用@Table(name="bmt"),或 POJO 版本LuckyNumberPojo,如果您想使用hibernate.cfg.xml)。继续编写HibernateUtil类,启用 JTA 策略并添加 GlassFish 3 内置的 JTA 平台:

OgmConfiguration cfgogm = new OgmConfiguration();
...
cfgogm.setProperty(Environment.TRANSACTION_STRATEGY,
                                 "org.hibernate.transaction.JTATransactionFactory");
cfgogm.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "jta");
cfgogm.setProperty(Environment.JTA_PLATFORM,
                                "org.hibernate.service.jta.platform.internal.SunOneJtaPlatform");

或者,如果您喜欢使用hibernate.cfg.xml文件,将它添加到那里(在这种情况下,不要忘记写入LuckyNumberPojo.hbm.xml并指定table="bmt" ) :

<property name="hibernate.transaction.factory_class">
                            org.hibernate.transaction.JTATransactionFactory</property>
<property name="hibernate.current_session_context_class">jta</property>
<property name="hibernate.transaction.jta.platform">
                            org.hibernate.service.jta.platform.internal.SunOneJtaPlatform</property>

接下来,添加一个名为BMTBean的无状态 bean(一个 EJB 组件);没有必要为它创建一个接口。因为默认情况下,EJB 方法中的代码是在事务中执行的,所以我们必须通过添加@TransactionManagement语句来修改它,如下所示:

package hogm.hnapi.ejb;
...
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class BMTBean {
...

你可以在第二章中找到关于这个注释的更多细节。

现在我们控制了事务边界。我们所需要的是可以使用@Resource注释获得的UserTransaction,就像这样:

@Resource
private UserTransaction userTransaction;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 你也可以通过EJBContext、通过 JNDI 查找甚至通过 CDI 注入机制(@Inject UserTransaction)获得UserTransaction 。在选择您的方法之前,咨询 J2EE 实现的官方文档总是一个好主意。

现在,我们可以很容易地调用UserTransaction.begin, commitsetRollbackOnly方法,通过从getCurrentSessionopenSession.获得的 Hibernate OGM 会话来控制与 MongoDB 数据库的事务(如果听起来 MongoDB 支持事务,其实并不支持。请记住,我们使用这种方法是因为 OGM 文档推荐使用事务分界,即使对于 MongoDB 也是如此。)例如,我们可以存储一个幸运数字,如清单 4-18 所示。注意,代码包含两种情况——使用实体和 POJO。

清单 4-18。 存储幸运数字的两种方法——BMT 法

package hogm.hnapi.ejb;

import hogm.hnapi.pojo.LuckyNumberEntity;
import hogm.hnapi.pojo.LuckyNumberPojo;
import java.util.Random;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.inject.Named;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import org.jboss.logging.Logger;

@Stateless
@Named("bean")
@TransactionManagement(TransactionManagementType.BEAN)
public class BMTBean {

    @Resource
    private UserTransaction userTransaction;
    private static final Logger log = Logger.getLogger(BMTBean.class.getName());

    public void persistAction() {

        log.info("Persisting LuckyNumberEntity instance ...");

        LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
        luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));
        LuckyNumberPojo luckyNumberPojo = new LuckyNumberPojo();
        luckyNumberPojo.setLuckynumber(new Random().nextInt(1000000));

        try {
            // Start the transaction
            userTransaction.begin();

            hogm.hnapi.util.without.hibernate.cfg.HibernateUtil.getSessionFactory().
                                                   getCurrentSession().persist(luckyNumberEntity);
            hogm.hnapi.util.with.hibernate.cfg.HibernateUtil.getSessionFactory().
                                                   getCurrentSession().persist(luckyNumberPojo);

            //persist here through openSession method

            // Commit the transaction
            userTransaction.commit();
        } catch (Exception ex) {
            try {
                //Rollback the transaction
                userTransaction.setRollbackOnly();
            } catch (IllegalStateException ex1) {
                log.log(Logger.Level.ERROR, ex1, ex1);
            } catch (SystemException ex1) {
                log.log(Logger.Level.ERROR, ex1, ex1);
            }
        }
        log.info("Persist successful ...");
    }
}

为了运行这个应用,我们选择激活 JSF 框架和 CDI 支持(通过在/WEB-INF文件夹中添加相应的beans.xml)。我们已经用@Named("bean")注释了 EJB 组件——如代码所示——并且我们使用简单的 JSF 表单从应用起始页调用它,就像这样(index.xhtml):

...
<h:form>
   <h:commandButton action="#{bean.persistAction()}"
                    value="Generate Lucky Number"/>            
</h:form>
...

测试

启动 MongoDB 服务器,如你在第一章中所见。接下来,由于您处于 NetBeans/GlassFish(或 Eclipse/GlassFish)环境中,保存项目并单击Run(或 Eclipse 中的Run on Server)按钮启动 GlassFish 并部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-8 所示的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-8 。运行 HOGMviaHNAPI _ JTA _ EJB _ BMT _ glassfish 3 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(glassfish_db)集合,(bmt).)对于每一次按,插入两个新文档,一个用于 enitity,一个用于 POJO。打开命令提示符,键入图 4-9 中的命令,查看您的工作结果。您还可以监控 GlassFish 日志消息,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-9 。查看 bmt 收藏内容

这个应用的完整源代码可以在 Apress 存储库中找到,命名为HOGMviaHNAPI_JTA_EJB_BMT_GlassFish3。它是一个 NetBeans 项目,并在 GlassFish 3 下进行了测试(我使用了捆绑到 NetBeans 7.2.1 的 GlassFish)。

在内置的 JTA 环境中 Hibernate OGM(EJB 3/CMT,GlassFish AS 3)

在前面的示例中,我们开发了一个基于 GlassFish 3 内置 JTA 环境和 bean 管理事务(BMT)的应用。通过应用一些必要的更改,我们可以很容易地将这个应用转换成容器管理的事务(CMT)。我可以告诉你“检查前面的例子,修改这个,修改那个。。. ",但如果你对之前的申请不感兴趣,你可能不会觉得那太有吸引力。所以我会尽量在这里提供更多的信息,并要求你只从以前的应用中复制本章中重复几次的部分。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta1
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)
  • 玻璃鱼 3.1.2.2

正在开发

启动 NetBeans 后,创建一个包含一个空 Maven web 应用的新项目,并将其命名为HOGMviaHNAPI_JTA_EJB_CMT_GlassFish3。在New Web Application向导中,为Group IdPackage字段键入hogm.hnapi。不要忘记选择 GlassFish web 服务器来部署这个应用。注意,即使我们添加了一个 EJB 组件,我们也不会创建一个企业应用来将 web 模块与 EJB 模块分开。我们更喜欢 web 应用,因为我们希望能够从 EJB 组件调用 web 组件。

编写应用代码

在添加 Hibernate OGM/MongoDB jar(像前面的例子一样使用 Maven)之后,创建众所周知的LuckyNumberEntity实体(这次使用@Table(name="cmt"),或 POJO 版本LuckyNumberPojo,如果您想使用hibernate.cfg.xml)。继续编写HibernateUtil类,启用 CMT 策略并添加 GlassFish 3 内置的 JTA 平台:

OgmConfiguration cfgogm = new OgmConfiguration();
 ...
cfgogm.setProperty(Environment.TRANSACTION_STRATEGY,
                                 "org.hibernate.transaction.CMTTransactionFactory");
cfgogm.setProperty(Environment.JTA_PLATFORM,
                                 "org.hibernate.service.jta.platform.internal.SunOneJtaPlatform");

或者,如果您喜欢使用hibernate.cfg.xml文件,将它添加到那里(在这种情况下,不要忘记写入LuckyNumberPojo.hbm.xml and specify table="cmt"):

<property name="hibernate.transaction.factory_class">
                              org.hibernate.transaction.CMTTransactionFactory</property>
<property name="hibernate.transaction.jta.platform">
                              org.hibernate.service.jta.platform.internal.SunOneJtaPlatform</property>

添加一个名为CMTBean的无状态 bean(一个 EJB 组件)(不需要为它创建接口)。因为默认情况下,EJB 方法中的代码是在事务中执行的,所以我们不需要干预。然而,只是为了好玩,我们可以手动提供已经默认的注释— @TransactionManagement@TransactionAttribute.关于这个注释的更多细节可以在第二章中找到。

现在我们可以很容易地利用 CMT 策略,并使用从getCurrentSessionopenSession方法获得的 Hibernate OGM 会话在 MongoDB 数据库中存储幸运数字,如清单 4-19 所示。注意,代码包含两种情况——使用实体和 POJO。

清单 4-19。 存储幸运数字的两种方法——CMT 法

package hogm.hnapi.ejb;

import hogm.hnapi.pojo.LuckyNumberEntity;  //entity case
import hogm.hnapi.pojo.LuckyNumberPojo;    //POJO case
import java.util.Random;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.inject.Named;
import org.jboss.logging.Logger;

@Stateless
@Named("bean")
@TransactionManagement(TransactionManagementType.CONTAINER) //this is the default
public class CMTBean {

    private static final Logger log = Logger.getLogger(CMTBean.class.getName());

    @TransactionAttribute(TransactionAttributeType.REQUIRED) //this is the default
    public void persistAction() {

        log.info("Persisting LuckyNumberEntity instance ...");

        LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
        luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));
        LuckyNumberPojo luckyNumberPojo = new LuckyNumberPojo();
        luckyNumberPojo.setLuckynumber(new Random().nextInt(1000000));

        hogm.hnapi.util.without.hibernate.cfg.HibernateUtil.getSessionFactory().
                                              getCurrentSession().persist(luckyNumberEntity);
        hogm.hnapi.util.with.hibernate.cfg.HibernateUtil.getSessionFactory().
                                              getCurrentSession().persist(luckyNumberPojo);
        //persist here through openSession method
        log.info("Persist successful ...");
    }
}

为了运行这个应用,我们将激活 JSF 框架和 CDI 支持(通过在/ WEB-INF文件夹中添加相应的beans.xml)。我们已经用@Named("bean")注释了 EJB 组件——如代码所示——并且我们使用简单的 JSF 表单从应用起始页调用它,就像这样(index.xhtml):

...
<h:form>
   <h:commandButton action="#{bean.persistAction()}"
                    value="Generate Lucky Number"/>
</h:form>
...

测试

按照第一章中的方法启动 MongoDB 服务器。接下来,因为您处于 NetBeans/GlassFish(或 Eclipse/GlassFish)环境中,所以只需保存项目并单击Run(或 Eclipse 中的Run on Server)按钮来启动 GlassFish 并部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-10 所示的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-10 。运行 HOGMviaHNAPI _ JTA _ EJB _ CMT _ glassfish 3 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(glassfish_db)集合(cmt).)中。每次按下按钮,都会插入两个新文档,一个用于 enitity,一个用于 POJO。打开命令提示符,键入图 4-11 中的命令,查看您的工作结果。您可以监控 GlassFish 日志消息,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-11 。检查“cmt”集合内容

这个应用的完整源代码被命名为HOGMviaHNAPI_JTA_EJB_CMT_GlassFish3,可以在 Apress 存储库中找到。它是一个 NetBeans 项目,并在 GlassFish 3 下进行了测试(我使用了捆绑到 NetBeans 7.2.1 的 GlassFish)。

通过 Java 持久性 API (JPA 2.0)Hibernate OGM

Hibernate OGM 也可以通过 JPA 引导。这非常有用,因为它不涉及任何 Hibernate ORM 知识,也不需要任何与 Hibernate 相关的代码。实际上,如果您以前使用过 JPA(不管是哪种实现),将 Hibernate OGM 配置为您的 JPA 提供者应该是小菜一碟。

在本节中,您将看到一组应用,它们将 Hibernate OGM 作为不同架构和技术下的 JPA 提供者。您将看到它是如何工作的:

  • 内置 JTA 环境(EJB 3,GlassFish AS 3)
  • 内置 JTA 环境(EJB 3,JBoss AS 7)
  • 独立的 JTA 环境(Apache Tomcat 7)
  • 内置 JTA 环境(JBoss AS 7 和 Seam 3 应用)
  • 内置 JTA 环境(GlassFish 3 和 Spring 3 应用)
  • 非 JTA 环境(RESOURCE_LOCAL,Apache Tomcat 7)

在内置的 JTA 环境中 Hibernate OGM(EJB 3,GlassFish AS 3)

我们从部署在 GlassFish 上的企业应用(称为 EAR—企业归档)开始。这是 Java 世界中经常使用的经典重型应用之一,通常涉及几种技术,如 JPA、JSF、Struts、EJB、Hibernate、Spring 等等。网络技术进入一个模块(战争模块),EJB 组件进入另一个模块(EJB 模块)。WAR 模块可以访问 EJB 模块,但反之则不行。从程序员的角度来看,JPA 的核心由一个名为persistence.xml的 XML 文件组成,它作为配置文件放在 EJB 模块中。因此,让我们来看看 Hibernate OGM 作为 JPA 提供者时这个文件是什么样子的。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta2
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)
  • 玻璃鱼 3.1.2.2

发展中的

启动 NetBeans 后,创建一个包含一个空 Maven enterprise 应用的新项目,并将其命名为HOGMviaJPA_EE_GlassFish。在New Enterprise Application向导中,为Group IdPackage字段键入hogm,并选择用于部署该应用的 GlassFish 应用服务器。在Projects窗口中看到项目后,您可以在HOGMviaJPA_EE_GlassFish-ear项目模块中编辑pom.xml文件(它必须在Project Files节点下)。在pom.xml,中,通过粘贴以下依赖项来添加 Hibernate OGM 发行版(包括 MongoDB 支持):

<dependencies>
   <dependency>
      <groupId>org.hibernate.ogm</groupId>
      <artifactId>hibernate-ogm-core</artifactId>
      <version>4.0.0.Beta2</version>
   </dependency>
   <dependency>
      <groupId>org.hibernate.ogm</groupId>
      <artifactId>hibernate-ogm-mongodb</artifactId>
      <version>4.0.0.Beta1</version>
   </dependency>
...
<dependencies>

现在保存项目,MongoDB Java 驱动程序 JAR 将列在Dependencies节点下。

编写应用代码

现在,我们有了所有需要的工件,所以我们准备添加一些代码。首先,在HOGMviaJPA_EE_GlassFish -ejb 模块中,我们开发了一个基本的实体类,它能够表示数据库中的对象。它包含一个名为luckynumber的字段(除了主键字段)。(您应该熟悉这类实体,从技术上讲,它们只是带注释的 POJOs。你可以在第二章中找到更多细节。)清单 4-20 显示了 LuckyNumberEntity 类的代码。

***清单 4-20。***lucky number entity 类

package hogm.jpa.entities;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "jpa")
public class LuckyNumberEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(name = "luckynumber", nullable = false)
    private int luckynumber;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public int getLuckynumber() {
        return luckynumber;
    }

    public void setLuckynumber(int luckynumber) {
        this.luckynumber = luckynumber;
    }
}

让我们继续我们感兴趣的要点,将 Hibernate OGM 集成为 JPA 提供者。您可以从使用 NetBeans 向导创建一个persistence.xml框架开始。这将为 GlassFish 默认数据源提供一个“空”的持久性单元(这是最方便的,因为我们实际上并不需要它)或者没有数据源。从 Hibernate OGM 的角度来看,这个数据源是不需要的,也从来没有使用过,但是根据具体情况,您可能需要指定一个现有的数据源,因为这是 JPA 的要求。(根据 JPA 1.0/2.0 规范,"" JTA 的事务类型假设将提供 JTA 数据源—要么由 jta-data-source 元素指定,要么由容器提供。))为了确定,你得自己测试一下。据我所知,没有必要指定数据源;在 NetBeans 向导中将该字段留空,您将获得一个没有数据源的persistence.xml框架——没有<jta-data-source>标签。如果出现相关错误,那么在 GlassFish 中添加默认数据源,如下所示:

...
<!-- out of the box data source for GlassFish v3-->
<jta-data-source>jdbc/sample</jta-data-source>
...

我们还将持久性单元重命名为HOGM_JPA_GLASSFISH_PU,并将事务类型表示为JTA。这是推荐的。请记住,我们有两个可能的值:RESOURCE_LOCAL表示事务将由 JPA provider 实现管理,JTA表示事务将由应用服务器(在本例中是 GlassFish)管理。最后,我们指定由这个持久性单元管理的实体列表。

此外,我们正在添加 Hibernate OGM 作为 JPA 提供者。这非常简单快捷,因为它只需要添加<provider>标记,就像这样:

...
<provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
...

默认情况下,NetBeans 将自动检测实体,并将标签<exclude-unlisted-classes>,添加到persistence.xml中,该标签默认为false—由该持久性单元管理的归档中的所有实体 beans。您可以保持不变,或者删除该标记并显式添加实体类:

...
<class>hogm.jpa.entites.LuckyNumberEntity</class>
...

由于我们处于 JTA 环境中,JTA 平台应该会被自动检测到并被使用,而无需我们的干预。但是,可以肯定的是,您可以相应地设置hibernate.transaction.jta.platform属性:

...
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
...

我们快完成了。我们只需要配置 MongoDB 连接(提供者、方言(可选)、数据库名称、主机和端口)。一旦我们完成了这些,我们就有了完整的persistence.xml文件,如清单 4-21 所示。

清单 4-21。 persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" FontName2">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="HOGM_JPA_GLASSFISH_PU" transaction-type="JTA">
   <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
    <class>hogm.jpa.entities.LuckyNumberEntity</class>
    <properties>
      <property name="hibernate.transaction.jta.platform"
                     value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
      <property name="hibernate.ogm.datastore.provider" value="mongodb"/>
      <property name="hibernate.ogm.datastore.grid_dialect"
                      value="org.hibernate.ogm.dialect.mongodb.MongoDBDialect"/>
      <property name="hibernate.ogm.mongodb.database" value="glassfish_db"/>
      <property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
      <property name="hibernate.ogm.mongodb.port" value="27017"/>
    </properties>
  </persistence-unit>
</persistence>

Hibernate OGM 现在可以作为 JPA 提供者为我们的应用提供服务了。

这是一个企业应用,所以 EJB 组件(默认情况下是事务性的)非常适合利用 OGM 提供的全新实体管理器。CMTBean实现了将幸运数字存储到 MongoDB 数据库的业务逻辑(不需要本地或远程接口),如清单 4-22 所示。

***清单 4-22。***CMT bean 类

package hogm.jpa.ejb;

import hogm.jpa.entities.LuckyNumberEntity;
import java.util.Random;
import javax.ejb.Stateless;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
@Named("bean")
public class CMTBean {

    @PersistenceContext(unitName = "HOGM_JPA_GLASSFISH_PU")
    private EntityManager em;

    public void persistAction() {
        LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
        luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));

        em.persist(luckyNumberEntity);
    }
}

最后,我们需要一些粘合代码来获得一个功能应用。如您所见,EJB 组件用@Named进行了注释,这意味着您需要通过添加beans.xml文件来激活 CDI 支持。如果您按下正确的按钮,NetBeans 会为您完成这项工作,但您也可以手动添加。在一个 Maven 项目中,在*-ejb模块中,beans.xml应该放在src/main/resources文件夹中(在Other Resource节点下)。并且在*-war模块中,beans.xml要放在/WEB-INF文件夹中(在Web Pages节点下)。两处都加beans.xml

通过 CDI 调用 EJB 可以从 JSF 表单中完成—您需要激活 JSF 框架:

...
<h:form>
   <h:commandButton action="#{bean.persistAction()}" value="Generate Lucky Number"/>
</h:form>
...

完成了!

测试

启动 MongoDB 服务器,就像你在第一章中看到的那样。接下来,因为您处于 NetBeans/GlassFish(或 Eclipse/GlassFish)环境中,所以只需保存项目并选择HOGMviaJPA_EE_GlassFish-ear节点。单击Run(或 Eclipse 中的Run on Server)按钮启动 Glassfish 并部署和运行应用。如果应用成功启动,您将在浏览器中看到类似于图 4-12 所示的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-12 。运行 HOGMviaJPA_EE_GlassFish 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(glassfish_db)集合(jpa))中。打开命令提示符,键入图 4-13 中的命令,查看您的工作结果。您可以监控 GlassFish 日志消息,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-13 。正在检查 jpa 收藏内容

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意忽略hibernate_sequences收藏,因为暂时不相关。你将在第五章中了解它是如何出现的以及为什么会出现。

这个应用的完整源代码被命名为HOGMviaJPA_EE_GlassFish,可以在 Apress 存储库中找到。它是一个 NetBeans 项目,并在 GlassFish 3 下进行了测试(我使用了捆绑到 NetBeans 7.2.1 的 GlassFish)。

在内置的 JTA 环境中 Hibernate OGM(EJB 3,JBoss AS 7)

在这一节中,您将看到如何运行上一节中开发的应用,但是使用 JBoss AS 而不是 GlassFish AS。不幸的是,它不能像在 JBoss 应用服务器下那样工作,所以您需要在应用服务器级别调整一些东西,并在persistence.xml文件中添加一些修改。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta2
  • JDK 1.7
  • 日蚀朱诺
  • JBoss AS 7.1

发展中的

有一个不成文的规则,GlassFish 粉丝更喜欢 NetBeans IDE 和 JBoss,因为粉丝喜欢使用 Eclipse IDE。显然,这不是强制性的。毕竟,我们讨论的是独立于 ide 的企业应用,应该可以在任何经过认证的 EE 应用服务器下工作。不过,您很可能同意这种关联,这就是为什么我们将使用 Eclipse IDE 将 JBoss 开发为应用。因此,在启动 Eclipse 之后,创建一个新项目,它由一个名为HOGMviaJPA_EE_JbossAS的空Enterprise Application Project组成。选择 EAR 版本 6.0,JBoss AS 7.1,默认配置为目标运行时。添加 Web 和 EJB 模块,命名为HOGMviaJPA_EE_JBossAS-webHOGMviaJPA_EE_JBossAS-ejb

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意我使用了 Eclipse JUNO 发行版,并通过 JBoss AS Tools 插件添加了 JBoss AS 7.1,因为这个应用服务器在 JUNO 中默认是不可用的(我使用的链接是www.download.jboss.org/jbosstools/updates/development/indigo/)。可以随意使用任何其他 Eclipse 发行版,只要它绑定到 JBoss AS 7.1。

现在,我们将让这个应用保持原样,并将注意力转移到 JBoss AS 7 模块上,因为我们需要将 Hibernate OGM JARs 配置为应用服务器内部的一个模块。没有这个模块,我们将无法成功部署包含 OGM 的 Hibernate 应用。

首先,定位三个罐子:hibernate-ogm-core-4.0.0.Beta2.jar, hibernate-ogm-mongodb-4.0.0.Beta1.jarmongo-java-driver-2.8.0.jar。接下来,浏览{JBOSSAS_HOME?? 路径并创建一个名为ogm的新文件夹。将这三个 jar 复制到这个新文件夹中,并将清单 4-23 中的module.xml文件添加到这个文件夹中。

清单 4-23。 module.xml

<module name="org.hibernate" slot="ogm">
    <resources>
        <resource-root path="hibernate-ogm-mongodb-4.0.0.Beta1.jar"/>
        <resource-root path="hibernate-ogm-core-4.0.0.Beta2.jar"/>
         <resource-root path="mongo-java-driver-2.8.0.jar"/>
    </resources>

    <dependencies>
        <module name="org.jboss.as.jpa.hibernate" slot="4"/>
        <module name="org.hibernate" slot="main" export="true" />
        <module name="javax.api"/>
        <module name="javax.persistence.api"/>
        <module name="javax.transaction.api"/>
        <module name="javax.validation.api"/>
        <module name="org.infinispan"/>
        <module name="org.javassist"/>
        <module name="org.jboss.logging"/>
    </dependencies>
</module>

保存文件。这里我们还要做一件事——在模块中添加 Hibernate 4.1.9 来代替 4.0.1。首先,定位以下 jar:hibernate-core-4.1.9.Final.jarhibernate-entitymanager-4.1.9.Final.jar,然后浏览{ JBOSSAS_HOME }/modules/org/hibernate/main路径。现在,把旧的罐子换成这些,或者只加这些。编辑同一文件夹中的module.xml文件,并相应替换旧的引用:

<module name="org.hibernate">
    <resources>
        <resource-root path="hibernate-core-4.1.9.Final.jar"/>
        <resource-root path="hibernate-entitymanager-4.1.9.Final.jar"/>
        <resource-root path="hibernate-commons-annotations-4.0.1.Final.jar"/>
        <resource-root path="hibernate-infinispan-4.0.1.Final.jar"/>
        <!-- Insert resources here -->
    </resources>
...

搞定了。我们完成了为 Hibernate OGM 应用准备 JBoss AS 7.1 的所有必要工作。

编写应用代码

现在,我们可以切换回应用开发,更具体地说,切换到persistence.xml文件,该文件必须进行一些重要的修改,您将在接下来的段落中看到。要添加这个文件,您可以使用 Eclipse IDE 向导,如下所示:

  • Project Explorer中,找到HOGMviaJPA_EE_JBossAS-ejb模块。右击它并从上下文菜单中选择Properties。导航到Properties窗口中的Project Facets,找到 JPA 刻面。选择它,你应该会看到类似于图 4-14 所示的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-14 。添加 JPA 方面

  • 我们对底部的文本"Further configuration required ..."(或者它可能会说"Further configuration available ...")特别感兴趣。点击文本打开Modify Faceted Project窗口。我们必须选择 JPA 实现,它是 Hibernate OGM。在JPA Implementation部分选择Generic 2.0Platform,选择User LibraryType
  • 接下来,我们必须指定 Hibernate OGM 和 MongoDB 库。如果您遵循了第一章中的章节“使用 Eclipse IDE 获得 Hibernate OGM 发行版”,那么您应该拥有 Hibernate OGM 核心和 MongoDB 库*。*选中后点击OK,,如图图 4-15 所示。如果没有这个库,现在就创建它。点击ApplyOK返回应用主界面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-15 。选择 JPA 实现

现在,您应该在HOGMviaJPA_EE_JBossAS-ejb | JPA Content节点下看到一个空的persistence.xml叶子。在编辑器中打开这个文件,让我们添加我们需要的内容:

  • 将持久化单元重命名为HOGM_JPA_JBOSSAS_PU,并将交易类型设置为JTA :

    <persistence-unit name="HOGM_JPA_JBOSSAS_PU" transaction-type="JTA">
    
  • 使用<provider>标签:

    <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
    

    将 JPA 提供者指定为 Hibernate OGM

  • 添加可以由持久性单元定义的EntityManager实例管理的实体(在我们的例子中,是一个名为LuckyNumberEntity :

    <class>hogm.jpa.entities.LuckyNumberEntity</class>
    

    的实体)

  • Optionally, indicate the JTA platform. Normally, this is auto-detected in an EE environment. Notice that for JBoss AS 7, the correct value is:

    org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform
    

    这不是用于 JBoss JTA 单机版的值。

    <property name="hibernate.transaction.jta.platform"
                    value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
    
  • 默认情况下,JPA 应用将使用 JBoss AS 7 集成适配器模块中配置的 Hibernate 集成类,除非您将设置为另一个值的属性jboss.as.jpa.adapterModule添加到您的persistence.xml属性列表中。该属性的值表示 Hibernate 集成类的名称,这些集成类帮助应用服务器与持久性提供者一起工作。在我们的例子中,我们需要 Hibernate 集成类 4,所以我们使用下面的设置:

    <property name="jboss.as.jpa.adapterModule" value="org.jboss.as.jpa.hibernate:4"/>
    
  • 我们还需要添加属性jboss.as.jpa.providerModule来表明我们希望使用 Hibernate OGM。这是我们在本节前面手动添加的模块:

    <property name="jboss.as.jpa.providerModule" value="org.hibernate:ogm"/>
    
  • 此外,我们需要禁用持久性单元的类转换器(默认情况下,类增强或重写是允许的)。为此,将jboss.as.jpa.classtransformer设置为false :

    <property name="jboss.as.jpa.classtransformer" value="false"/>        
    
  • 接下来,通过将hibernate.listeners.envers.autoRegister属性设置为false :

    <property name="hibernate.listeners.envers.autoRegister" value="false"/>
    

    来关闭自动 Envers 事件监听器注册

  • 最后,配置 MongoDB 连接(提供者、方言(可选)、数据库名称、主机和端口)。一旦你完成了,整个persistence.xml文件就可用了,如清单 4-24 所示。

清单 4-24。 persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" FontName2">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="HOGM_JPA_JBOSSAS_PU" transaction-type="JTA">
   <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
   <class>hogm.jpa.entities.LuckyNumberEntity</class>
   <properties>
      <property name="hibernate.transaction.jta.platform"
                      value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
      <property name="jboss.as.jpa.adapterModule" value="org.jboss.as.jpa.hibernate:4"/>
      <property name="jboss.as.jpa.providerModule" value="org.hibernate:ogm"/>
      <property name="jboss.as.jpa.classtransformer" value="false"/>
      <property name="hibernate.listeners.envers.autoRegister" value="false"/>
      <property name="hibernate.ogm.datastore.provider" value="mongodb"/>
      <property name="hibernate.ogm.datastore.grid_dialect"
                      value="org.hibernate.ogm.dialect.mongodb.MongoDBDialect"/>
      <property name="hibernate.ogm.mongodb.database" value="jbossas_db"/>
      <property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
      <property name="hibernate.ogm.mongodb.port" value="27017"/>
    </properties>
 </persistence-unit>
</persistence>

关于没有指定数据源的事实,请记住,正如我前面指出的,Hibernate OGM 不需要数据源。然而,在某些情况下,数据源必须被指定为符合 JPA 规范。对于 JBoss AS 7.1,提供数据源的最简单方法(以防出现相关错误,但我没有)是添加现成的数据源,如下所示:

...
<!-- out of the box data source for GlassFish v3-->
<jta-data-source> java:jboss/datasources/ExampleDS</jta-data-source>
...

在这一点上,我可以说我们尊重运行 Hibernate OGM 应用的每一个 JBoss AS 7 需求。

接下来,您必须添加前面示例中讨论的应用代码(LuckyNumberEntity实体、CMTBean EJB 组件(不要忘记将单元名称改为HOGM_JPA_JBOSSAS_PU)和index.xhtml网页),并添加 CDI 和 JSF 设置(可以从Project Facets向导中选择)。完成后,您应该能够部署和运行应用,而不会出现任何不愉快的事件。为了做到这一点,我使用 JBoss 作为 Eclipse JUNO 的工具,但是您可以按照自己喜欢的方式来做。

测试

按照第一章中的方法启动 MongoDB 服务器。接下来,因为您处于 Eclipse/JBoss AS(或 NetBeans/JBoss AS)环境中,所以只需保存项目并选择Run on Server(或 NetBeans 中的Run)来部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-16 所示的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-16 。运行 HOGMviaJPA_EE_JBossAS 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(jbossas_db)集合(jpa))中。打开命令提示符,键入图 4-17 中的命令,查看您的工作结果。您可以将 JBoss 作为日志消息来监控,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-17 。检查 jpa 收藏内容

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 你将会看到hibernate_sequences系列如何以及为什么会出现在第五章中。

这个应用的完整源代码被命名为HOGMviaJPA_EE_JBossAS,可以在 Apress 存储库中找到。它是一个 Eclipse 项目,在 JBoss AS 7.1 下测试过。

在独立的 JTA 环境中 Hibernate OGM(Apache Tomcat 7)

在本章的前面,我们通过 Hibernate Native API 创建了一个 Hibernate OGM,它被部署在一个带有 Tomcat 7 web 服务器的独立 JTA 环境中。在这一节中,我们将用 Java 持久性 API 替换 Hibernate 本机 API 部分。我们将使用一个EntityManager来代替 Hibernate Session,

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta1
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)
  • 阿帕奇雄猫 7

正在开发

启动 NetBeans 后,创建一个包含一个空 Maven web 应用的新项目,并将其命名为HOGMviaJPAJTA_Tomcat7。在New Web Application向导中,为Group IdPackage字段键入hogm.hnapi,并选择 Apache Tomcat 7 web server 来部署这个应用。当您在Projects窗口中看到项目时,编辑pom.xml文件(它必须在Project Files节点下)。在pom.xml文件中,通过粘贴以下依赖项来添加 Hibernate OGM(包括 MongoDB 支持)和 JBoss JTA(独立于 JBoss 的 JTA)发行版:

<dependencies>
   <dependency>
      <groupId>org.hibernate.ogm</groupId>
      <artifactId>hibernate-ogm-core</artifactId>
      <version>4.0.0.Beta2</version>
   </dependency>
   <dependency>
      <groupId>org.hibernate.ogm</groupId>
      <artifactId>hibernate-ogm-mongodb</artifactId>
      <version>4.0.0.Beta1</version>
   </dependency>
   <dependency>
      <groupId>org.jboss.jbossts</groupId>
      <artifactId>jbossjta</artifactId>
      <version>4.16.4.Final</version>
   </dependency>
...
<dependencies>

现在保存项目,驱动程序 JAR 将列在Dependencies节点下。

编写应用代码

现在添加名为LuckyNumberEntity.的著名实体,您可以在前面的示例中找到它;这是一个简单的 POJO,用@Entity, @Table(name="jpa")进行了注释,有一个名为id的主键字段,类型为String,使用 UUID2 生成器生成,还有一个名为luckynumberint字段。

接下来,我们将编写persistence.xml文件。在一个 Maven 项目中,将这个文件放在Other Sources/src/main/resources/META-INF文件夹中,开始时将持久性单元命名为HOGM_JPA_JTA_TOMCAT_PU,将事务类型命名为JTA:

<persistence-unit name="HOGM_JPA_JTA_TOMCAT_PU" transaction-type="JTA">
...

通过添加<provider>标签,将 Hibernate OGM 设置为 JPA 提供者:

<provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>

使用<class>属性在这个持久性单元中添加实体类:

<class>hogm.hnapi.entities.LuckyNumberEntity</class>

接下来,我们需要指定 JTA 平台——JBoss JTA。为此,请添加以下内容:

<property name="hibernate.transaction.jta.platform"
                 value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/>

注意,我们指定了 JBoss JTA 独立发行版,而不是 JBoss AS 使用的发行版。

JBoss TS 文档指出,为了选择本地 JBoss JTA 实现,您必须指定两个属性:com.arjuna.ats.jta.jtaTMImplementationcom.arjuna.ats.jta.jtaUTImplementation。我们可以这样指定它们:

<property name="com.arjuna.ats.jta.jtaTMImplementation"
                value="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple"/>
<property name="com.arjuna.ats.jta.jtaUTImplementation"
                value="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>

现在,我们将使用数据存储提供者、方言、要连接的数据库的名称以及主机和端口来配置 MongoDB 连接(我们将使用本地主机和默认的 MongoDB 服务器端口 27017)。把所有东西放在一起,我们得到了清单 4-25 中所示的persistence.xml文件。

清单 4-25。 Persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" FontName2">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="HOGM_JPA_JTA_TOMCAT_PU" transaction-type="JTA">
        <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
        <class>hogm.hnapi.entities.LuckyNumberEntity</class>
        <properties>
            <property name="hibernate.transaction.jta.platform"
                      value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/>
            <property name="com.arjuna.ats.jta.jtaTMImplementation"
                      value="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple"/>
            <property name="com.arjuna.ats.jta.jtaUTImplementation"
                      value="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>
            <property name="hibernate.ogm.datastore.provider" value="mongodb"/>
            <property name="hibernate.ogm.datastore.grid_dialect"
                      value="org.hibernate.ogm.dialect.mongodb.MongoDBDialect"/>
            <property name="hibernate.ogm.mongodb.database" value="tomcat_db"/>
            <property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
            <property name="hibernate.ogm.mongodb.port" value="27017"/>
        </properties>
    </persistence-unit>
</persistence>

至此,我们有了一个实体和相应的持久性单元,所以是时候添加一个 DAO 类来将幸运数字存储到 MongoDB 数据库中了。首先,基于这个持久性单元(HOGM_JPA_JTA_TOMCAT_PU),我们需要获得一个实体管理器工厂,并从这个工厂获得一个实体管理器,如下所示:

private static final EntityManagerFactory emf =
                             Persistence.createEntityManagerFactory("HOGM_JPA_JTA_TOMCAT_PU");
private final EntityManager em = emf.createEntityManager();

现在,实体管理器已经准备好加入一个事务并对 MongoDB 数据库执行语句(在我们的例子中,是持久化语句),但是为此我们需要获取用户事务来设置事务边界。我们在以前的应用中已经这样做了,但如果您不记得了,至少有两种方法可以做到:

  • 使用静态方法transactionManager:
javax.transaction.TransactionManager tx = com.arjuna.ats.jta.TransactionManager.transactionManager();
  • 使用静态方法userTransaction:
javax.transaction.UserTransaction tx = com.arjuna.ats.jta.UserTransaction.userTransaction();

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意TransactionManager接口允许应用服务器代表被管理的应用控制事务边界,而UserTransaction接口允许应用控制事务边界。显然,当应用控制事务边界时,您可以使用这两种方法,但是当您允许应用服务器控制事务边界时,您必须使用TransactionManager

现在,您可以用控制事务流的begin, commit,rollback方法来区分持久化语句。在事务开始后(当调用begin方法时),实体管理器必须通过调用joinTransaction方法加入它,如下所示:

...
tx.begin();
em.joinTransaction();
em.persist(transientInstance);
tx.commit();
...

提供用于清除和关闭实体管理器的代码,一些用于监控应用流的消息,您将得到如清单 4-26 所示的 DAO 类。

清单 4-26。 幸运数字道类

package hogm.hnapi.dao;

import hogm.hnapi.entities.LuckyNumberEntity;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class LuckyNumberDAO {

    private static final Logger log = Logger.getLogger(LuckyNumberDAO.class.getName());
    private static final EntityManagerFactory emf =
                                 Persistence.createEntityManagerFactory("HOGM_JPA_JTA_TOMCAT_PU");
    private final EntityManager em = emf.createEntityManager();

    public void persistAction(LuckyNumberEntity transientInstance) throws java.lang.Exception {

        log.log(Level.INFO, "Persisting LuckyNumberEntity instance ...");

        javax.transaction.TransactionManager tx =
                                      com.arjuna.ats.jta.TransactionManager.transactionManager();
        // javax.transaction.UserTransaction tx = com.arjuna.ats.jta.UserTransaction.userTransaction();

        try {
            tx.begin();
            em.joinTransaction();
            em.persist(transientInstance);
            tx.commit();

            log.log(Level.INFO, "Persist successful ...");
        } catch (Exception re) {
            tx.rollback();

            log.log(Level.SEVERE, "Persist failed ...", re);
            throw re;
        }  finally {
            if (em != null) {
                em.clear();
                em.close();
            }
        }
    }
}

重要的部分完成了!我们只需添加一个简单的 servlet 来处理 DAO 类,如下所示:

package hogm.hnapi.servlet;
...
@WebServlet(name = "LuckyNumberServlet", urlPatterns = {"/LuckyNumberServlet"})

public class LuckyNumberServlet extends HttpServlet {
...
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, Exception {
  ...
  LuckyNumberDAO luckyNumberDAO = new LuckyNumberDAO();
  LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
  luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));

  luckyNumberDAO.persistAction(luckyNumberEntity);
  ...
  }
}

一个普通的 JSP 页面(index.jsp)向我们的 servlet 发送空请求:

...
<form action="./LuckyNumberServlet" method="POST">
   <input type="submit" value="Generate Lucky Number">
</form>
...

搞定了。

测试

按照第一章中的方法启动 MongoDB 服务器。接下来,因为您处于 NetBeans/Tomcat(或 Eclipse/Tomcat)环境中,所以只需保存项目并单击Run(或 Eclipse 中的Run on Server)按钮来启动 Tomcat 并部署和运行应用。如果应用成功启动,您将在浏览器中看到类似于图 4-18 所示的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-18 。运行 HOGMviaJPAJTA_Tomcat7 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(tomcat_db)集合(jpa))中。打开命令提示符,键入图 4-19 中的命令,查看您的工作结果。您可以监控 Tomcat 日志消息,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-19 。检查 jpa 集合内容

这个应用的完整源代码被命名为HOGMviaJPAJTA_Tomcat7,可以在 Apress 存储库中找到。它是一个 NetBeans 项目,并在 GlassFish AS 3 下进行了测试。

内置 JTA 环境中的 hibernate OGM(JBoss AS 7 和 Seam 3 应用)

我在本章的最后保留了两个应用,它们涉及到 Seam 和 Spring,这两个强大而流行的 J2EE 框架。作为一个 Seam 迷,我已经看到 Seam 成为一个成熟和健壮的框架,在版本 3 中转变为"一个为 Java EE 6 应用开发量身定制的模块和开发人员工具的集合,以 CDI 为核心。

由于模块化框架结构和 CDI 注入机制,您可以创建仅涉及您需要的模块的 Seam 3 应用。在下一个应用中,我们使用一个名为 Seam Persistence 的 Seam 3 模块(这是最接近我们主题的模块),它“将事务和持久性引入托管 beans,提供一个简化的事务 API,并将事务传播事件与 CDI 事件总线挂钩。“在 Seam 持久性的众多特性中,有两个非常突出:

  • Seam 管理的持久化上下文——这是一个内置的 Seam 组件,能够管理实体管理器(对于 JPA 它甚至可以在 SE 环境中工作,因为 Seam 持久性扩展将引导EntityManagerFactory和会话(Session代表 Hibernate)。此外,它在 EE 容器内外都提供了稳定性和健壮性。
  • 声明性事务——Seam 已经升级了 EJB 3 众所周知的@TransactionAttribute,为普通 beans 提供声明性事务,更酷的是,这可以在 EJB 完全未知的 EE 容器之外工作。

如果你在这两个特性上再加上简单的配置和集成,你会发现 Seam 的持久性真的很棒!

因此,让我们编写一个使用 Seam 3(Seam 持久性模块)和 Hibernate OGM 作为 JPA 的应用。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta2
  • JDK 1.7
  • 日蚀朱诺
  • 伪造 1.0.5 或 1.1.3
  • JBoss AS 7

发展中的

我们首先关心的是如何开始一个 Seam 持久性项目,因为有几种可能性。例如,您可以通过 Maven 工件添加 Seam 持久性分布:

<dependencies>
 <dependency>
<groupId>org.jboss.seam.persistence</groupId>
<artifactId>seam-persistence-api</artifactId>
<version>${seam.persistence.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.seam.persistence</groupId>
<artifactId>seam-persistence-impl</artifactId>
<version>${seam.persistence.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.seam.solder</groupId>
<artifactId>seam-solder</artifactId>
<version>${seam.solder.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.seam.xml</groupId>
<artifactId>seam-xml-config</artifactId>
<version>${seam.xml.version}</version>
</dependency>
 ...
</dependencies>

或者,更好的是,您可以使用 JBoss Tools 用于 Eclipse,或者使用 Seam Forge Tools 用于 Eclipse(实际上 Seam Forge Tools 现在是 JBoss Tools 的子工具)。然而,对于我们的需求,决定是明确的:我们将为 Eclipse JUNO 使用 Seam Forge 工具插件(www.forge.jboss.org/)。你可能已经在你的 Eclipse 发行版中或者 Eclipse 之外安装了它,并且已经使用过很多次了,但是如果你是 Forge 的新手并且你想快速安装它,那么进入Help|Install New Software窗口,添加JBoss Tools( http://download.jboss.org/jbosstools/updates/development/indigo/ )或者从列表中选择它),展开Abridged JBoss Tools 3.3节点,然后选择Forge Tools(参见图 4-20 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-20 。为 Eclipse 安装锻造工具

按照向导中的步骤进行安装,然后重新启动 IDE。现在,从Window | Show View窗口,你可以激活Forge | Forge Console.最初锻造没有运行,但是可以通过按下锻造条上的绿色小三角来启动(图 4-21 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-21 。Eclipse 中的伪造控制台

使用 Forge 的最大好处是你不需要阅读大量的教程,因为它只是命令行工具和自动化的外壳。没有复杂的向导、设置、XMLs 配置或任何东西,只有一束命令,可以在几秒钟内生成整个项目,包括 Seam 和 EE。

编写应用代码

我假设你现在正在看一个 Seam Forge 控制台。(在 Eclipse 下建议这样做,因为它让您在每次键入命令后都能看到项目的创建进度。)让我们插入使用 Seam 持久性模块生成新的 Seam 3 项目所需的命令。

首先,我们需要在项目上下文之外为 Forge 安装 Seam 持久性插件(如果它不存在的话)。这可以通过以下命令轻松完成:

forge install-plugin seam-persistence --version 3.1.0.Final

现在我们可以插入创建新项目的命令:

  • 创建一个名为HOGMviaJPA_SEAM3 :

    new-project --named HOGMviaJPA_SEAM3
    

    的新项目

  • 将 JavaServer Faces scaffold 添加到新项目中(对所有问题都回答是):

    scaffold setup
    
  • 选择要安装的 JBoss Java EE 版本。在版本列表中,找到org.jboss.spec:jboss-javaee-6.0:pom::3.0.1.Final并在其前面键入数字(如果没有,则选择最新的最终版本)。

  • 在一堆成功消息之后,你会看到问题“在 web-root 的哪个子目录下创建 scaffold?”。类型main

  • 安装 Seam 持久模块:

    seam-persistence setup
    
  • 您将被要求指出要安装哪个版本。找到org.jboss.seam.persistence:seam-persistence:::3.1.0.Final版本,并在其前面键入编号(如果没有,选择最新的最终版本)。

  • 安装 Seam 托管持久性上下文:

    seam-persistence install-managed-persistence-context
    
  • 系统将提示您为持久性上下文生成器指定包和类名。只需按下每个问题的Enter键,接受默认建议。

  • 通过键入:

    seam-persistence enable-declarative-tx
    

    激活声明性事务支持

  • 生成一个实体类——LuckyNumberEntity类(通过按Enter键接受建议的包名):

    entity --named LuckyNumberEntity
    
  • 通过键入:

    field int --named luckynumber
    

    将字段luckynumber添加到实体中

  • 搞定了。我们已经拥有了所有需要的组件,所以我们准备构建我们的项目。类型:

    build
    

如果构建成功结束,那么您已经做了很好的工作,项目应该在 Eclipse IDE 的Project Explorer选项卡下可见。不要担心标记项目有错误的红色“x”——这是因为persistence.xml文件是空的。(即使你没有那个红色的“x”,你仍然需要用正确的设置填充persistence.xml。)

让我们使用 Eclipse IDE 向导来消除这个恼人的错误。在Project Explorer中,找到HOGMviaJPA_SEAM3项目节点,右键单击并从上下文菜单中选择Properties。现在,按照本章中“在内置 JTA 环境中 Hibernate OGM(EJB 3,JBoss AS 7 ) ”一节的“编写应用”部分的说明,获取清单 4-27 中显示的persistence.xml内容。

清单 4-27。 persistence.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence FontName2">http://java.sun.com/xml/ns/persistence "
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance " version="2.0"
xsi:schemaLocation=" http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd ">
 <persistence-unit name="HOGM_JPA_SEAM3_PU" transaction-type="JTA">

   <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
   <class>com.example.HOGMviaJPA_SEAM3.model.LuckyNumberEntity</class>
   <properties>
      <property name="hibernate.transaction.jta.platform"
                      value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
      <property name="jboss.as.jpa.adapterModule" value="org.jboss.as.jpa.hibernate:4"/>
      <property name="jboss.as.jpa.providerModule" value="org.hibernate:ogm"/>
      <property name="jboss.as.jpa.classtransformer" value="false"/>
      <property name="hibernate.listeners.envers.autoRegister" value="false"/>
      <property name="hibernate.ogm.datastore.provider" value="mongodb"/>
      <property name="hibernate.ogm.datastore.grid_dialect"
                      value="org.hibernate.ogm.dialect.mongodb.MongoDBDialect"/>
      <property name="hibernate.ogm.mongodb.database" value="jbossas_db"/>
      <property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
      <property name="hibernate.ogm.mongodb.port" value="27017"/>
   </properties>
 </persistence-unit>
</persistence>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意在保存和构建之前,通过添加一行@Table(name="seam")来编辑LuckyNumberEntity

用 Forge build 命令保存并构建项目,错误就会消失。

现在我们需要添加将幸运数字持久化到 MongoDB 数据库中的业务逻辑,我认为 EJB 组件正是我们所需要的,因为我们可以很好地利用它的 CDI 特性。首先,在 Java sources ( src/main/java)下创建一个名为com.example.HOGMviaJPA_SEAM3.view的新包,里面有一个名为CMTBean的空无状态 bean。如果您从 Eclipse 向导创建无状态 bean,请选择 EJB 节点下的会话 Bean (EJB 3.x)叶。

现在我们将使用 Seam 管理的持久性上下文。如果您不熟悉它,您可能会认为将它粘贴到我们的 EJB 组件中会导致大量的代码。但是请记住,我们需要做的只是使用 CDI @Inject注释来获得 Seam managed EntityManager:

@Inject @Forge EntityManager em;

@Forge表示一个 CDI 限定符(Seam 管理的持久性上下文工厂类和限定符类都是由 Seam Forge 生成的,并放在 Java sources src/main/java 下的包 com.example.HOGMviaJPA_SEAM3 中)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意我们没有使用声明性事务特性(尽管我们安装了它),因为我们在 EE 环境中,EJB 默认是事务性的。

这一行代码完成了注入和管理实体管理器的所有工作。接下来,我们将使用最常见的方法来赋予持久化过程生命:

...
public void persistAction() {
   LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
   luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));
   em.persist(luckyNumberEntity);
  }
...

最后,我们用@Named注释我们的 EJB 组件,使它在一个简单的 JSF 表单中可见。清单 4-28 显示了完整的 EJB 代码。

***清单 4-28。***EJB 全集

package com.example.HOGMviaJPA_SEAM3.view;

import java.io.Serializable;
import java.util.Random;
import javax.ejb.Stateful;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;

import com.example.HOGMviaJPA_SEAM3.Forge;
import com.example.HOGMviaJPA_SEAM3.model.LuckyNumberEntity;

@Named("bean")
@Stateful
@RequestScoped
public class CMTBean implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Inject @Forge EntityManager em;

  public void persistAction() {
      LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
      luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));
      em.persist(luckyNumberEntity);
  }
}

通过对 Seam Forge 在src/main/webapp/main/index.xhtml下生成的index.xhtml文件做一些修改,就可以很容易地用LuckyNumberEntity实例调用persistAction方法。第一个修改涉及使用 Taglib 指令导入 JSF 标记库;为此使用 XML 语法(参见粗体代码):

<ui:composition FontName2">http://www.w3.org/1999/xhtml "
        xmlns:ui=" http://java.sun.com/jsf/facelets "
        template="/resources/scaffold/pageTemplate.xhtml"
       xmlns:h=" [`java.sun.com/jsf/html`](http://java.sun.com/jsf/html) " >
...

其次,将下一个表单放入代码中的某个地方——我将它粘贴到<ui:define>标签中。因为这只是一个例子,所以我保留了生成的设计:

...
<ui:define name="subheader">
<h:form>
   <h:commandButton action="#{bean.persistAction()}" value="Generate Lucky Number"/>
</h:form>
</ui:define>
...

最后,指定应用起始页。编辑web.xml文件(在src/main/webapp/WEB-INF文件夹下),并在末尾添加以下代码:

...
<welcome-file-list>
    <welcome-file>faces/main/index.xhtml</welcome-file>
</welcome-file-list>
...

保存并再次构建项目(使用 Forge 控制台)就这样!

测试

按照第一章中的方法启动 MongoDB 服务器。接下来,因为您处于 Eclipse/JBoss AS(或 NetBeans/JBoss AS)环境中,所以只需保存项目并单击Run on Server(或 NetBeans 中的Run)按钮来启动 JBoss AS 并部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-22 所示的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-22 。运行 HOGMviaJPA_SEAM3 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(jbossas_db)集合(seam))中。打开命令提示符并键入图 4-23 中的命令来查看您的工作结果。您可以将 JBoss 作为日志消息来监控,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-23 。查缝收藏内容

这个应用的完整源代码被命名为HOGMviaJPA_SEAM3,可以在 Apress 资源库中找到。它是一个 Eclipse 项目,在 JBoss AS 7 下测试过。

内置 JTA 环境中的 hibernate OGM(GlassFish 3 和 Spring 3 应用)

市面上最好的开源 Java 企业框架之一,拥有数百万粉丝,就是 Spring,尤其是 distribution 3。在本节中,我们将开发一个通过 JPA 集成 Spring 3 和 Hibernate OGM 的应用。既然您正在阅读本节,那么您可能是 Spring 的粉丝,这个应用对您来说可能看起来非常简单。请记住,这里的重点是向您展示如何将 Hibernate OGM 添加到这个等式中。所以,让我们用 Spring 和 Hibernate OGM 来持久化一些幸运数字。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta2
  • JDK 1.7
  • NetBeans IDE 7.2.1(或 Eclipse JUNO)
  • Spring 3.1.1
  • 玻璃鱼 3.1.2.2

发展中的

启动 NetBeans 后,创建一个由空的Web Application组成的新项目(注意,我们不会在这个应用中使用 Maven),并将其命名为HOGMviaJPA_Spring3。选择 GlassFish AS 来部署该应用,并从 NetBeans 向导添加 Spring Web MVC 框架。

一旦在Projects窗口下有了项目,除了 NetBeans 自动添加的 Spring 3 . 1 . 1 jar 之外,您还需要提供几个 jar。从 Hibernate OGM/MongoDB JARs 开始,它应该在第一章中创建的 Hibernate OGM 核心和 MongoDB 用户库中可用。继续从网上下载两个罐子:asm-3.1.jar ( http://asm.ow2.org/)和aopalliance.jar ( http://aopalliance.sourceforge.net/)。

现在您已经有了所有必需的 jar,我们可以开始编码了。

编写应用代码

我们将从开发实体类和persistence.xml开始。向我们的 MongoDB 数据库提供幸运数字代码的实体类,如清单 4-29 所示,非常简单。

清单 4-29。 实体类

package hogm.spring;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "spring")
public class LuckyNumberEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(name = "luckynumber", nullable = false)
    private int luckynumber;

    public LuckyNumberEntity() {
    }

    public int getLuckynumber() {
        return luckynumber;
    }

    public void setLuckynumber(int luckynumber) {
        this.luckynumber = luckynumber;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

添加一个空的persistence.xmlpersistence.xml包含一个持久化单元HOGMviaJPA_SPRING3_PU和一个定义为JTA的事务类型:

<persistence-unit name="HOGMviaJPA_SPRING3_PU" transaction-type="JTA">
...

接下来,通过添加<provider>标记将 Hibernate OGM 指定为 JPA 提供者:

<provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>

使用<class>属性将清单 4-28 中的entity类添加到这个持久性单元中:

<class>hogm.spring.LuckyNumberEntity</class>

使用hibernate.transaction.jta.platform属性指定 JTA 平台。该属性的值可以在第二章的列表中找到。对于 GlassFish AS,请使用:

...
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
...

我们差不多完成了;我们只需要配置 MongoDB 连接(提供者、方言(可选)、数据库名称、主机和端口)。一旦我们完成了这些,我们就有了如清单 4-30 所示的整个persistence.xml文件。

清单 4-30。 p ersistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" FontName2">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="HOGMviaJPA_SPRING3_PU" transaction-type="JTA">
    <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
    <class>hogm.spring.LuckyNumberEntity</class>
    <properties>
      <property name="hibernate.transaction.jta.platform"
                      value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
      <property name="hibernate.ogm.datastore.provider" value="mongodb"/>
      <property name="hibernate.ogm.datastore.grid_dialect"
                      value="org.hibernate.ogm.dialect.mongodb.MongoDBDialect"/>
      <property name="hibernate.ogm.mongodb.database" value="glassfish_db"/>
      <property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
      <property name="hibernate.ogm.mongodb.port" value="27017"/>
    </properties>
  </persistence-unit>
</persistence>

现在,我们准备添加一些 DAO 业务逻辑来利用 JPA 设置。为此,我们可以编写一个简单的 Spring 组件(用@Component注释该类),它注入一个EntityManager并实现一个事务性的persist方法,如下所示:

package hogm.spring;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class LuckyNumberDAO {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void persist(LuckyNumberEntity luckyNumberEntity) {
        em.persist(luckyNumberEntity);
    }
}

注意,我们使用了@Transactional注释,因为我们希望 Spring 在事务中包装该方法。

为了创建一个经典的 Spring 应用,我们需要一个 Spring 控制器(用@Controller注释这个类),它能够接收来自多个用户的 HTTP 请求,并且能够参与 MVC 工作流。我们的控制器将接收用户的 HTTP GET 请求,并为每个请求生成一个新的幸运数字,该数字将成为传递给 DAO persist方法的参数。为此,我们使用了让容器自动连接 bean 的@Autowired注释——在我们的例子中,是在清单 4-31 中显示的LuckyNumberDAO bean。

清单 4-31。??【幸运数字】刀豆

package hogm.spring;

import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LuckyNumberController {

    @Autowired
    private LuckyNumberDAO luckyNumberDao;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index(ModelMap map) {
        LuckyNumberEntity luckyNumberEntity = new LuckyNumberEntity();
        luckyNumberEntity.setLuckynumber(new Random().nextInt(1000000));

        luckyNumberDao.persist(luckyNumberEntity);

        return "index";
    }
}

用户可以使用我们添加到WEB-INF/jsp/index.jsp页面的 Spring 表单发出 HTTP GET 请求。我们使用 Taglib 指令来导入 Spring 标签库:

<%@ taglib prefix="form" uri=" http://www.springframework.org/tags/form " %>
...
<form:form method="GET" commandName="/">
   <input type="submit" value="Generate Lucky Number" />
</form:form>
...

快好了。再有两个 XML 配置文件,我们就可以运行应用了。清单 4-32 中的所示的众所周知的dispatcher-servlet.xml,,需要包含几个设置,例如启用 Spring MVC @Controller编程模型,定义实体管理器工厂(注意我们指明了我们的 Hibernate OGM 持久性单元名称)和 Spring JTA 事务管理器(它应该放在WEB-INF文件夹中)。

清单 4-32。 d ispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans FontName2">http://www.springframework.org/schema/beans "
       xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
       xmlns:p=" http://www.springframework.org/schema/p "
       xmlns:mvc=" http://www.springframework.org/schema/mvc "
       xmlns:tx=" http://www.springframework.org/schema/tx "
       xmlns:context=" http://www.springframework.org/schema/context "
       xsi:schemaLocation=" http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd ">

    <context:component-scan base-package="hogm.spring" />
    <context:annotation-config/>
    <mvc:annotation-driven />
    <tx:annotation-driven transaction-manager="txManager" />

    <bean id="jspViewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass"
                    value="org.springframework.web.servlet.view.JstlView" />
            <property name="prefix" value="/WEB-INF/jsp/" />
            <property name="suffix" value=".jsp" />
    </bean>

    <bean id="entityManagerFactory"
               class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
            <property name="persistenceUnitName" value="HOGMviaJPA_SPRING3_PU"/>
    </bean>

    <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    </bean>
</beans>

最后,生成的web.xml要做相应的调整,如清单 4-33 所示。应该放在WEB-INF文件夹里。

清单 4-33。.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app    version="3.0"
     FontName2">http://java.sun.com/xml/ns/javaee "
     xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
     xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee
     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd ">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>/</welcome-file>
    </welcome-file-list>
</web-app>

搞定了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意 Spring 也支持 NoSQL 数据存储,像 MongoDB,没有 Hibernate OGM。更多详情,请访问www.springsource.org/spring-data/mongodb

测试

启动 MongoDB 服务器,如你在第一章中所见。接下来,因为您处于 NetBeans/GlassFish(或 Eclipse/GlassFish)环境中,所以只需保存项目并单击Run(或 Eclipse 中的Run on Server)按钮来启动 GlassFish 并部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-24 所示的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-24 。运行 HOGMviaJPA_SPRING3 应用

按几次Generate Lucky Number按钮,将一些幸运数字保存到 MongoDB 数据库(glassfish_db)集合(spring))中。打开命令提示符并键入图 4-25 中的命令来查看您的工作结果。您可以监控 GlassFish 日志消息,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-25 。查看 Spring 收藏内容

这个应用的完整源代码被命名为HOGMviaJPA_SPRING3,可以在 Apress 存储库中找到。它是一个 NetBeans 项目,并且是在 GlassFish AS 3 下测试的。

在非 JTA 环境中 Hibernate OGM(RESOURCE _ LOCAL,Apache Tomcat 7)

在这一节中,我们将开发一个 Hibernate OGM 应用,它将在不推荐的条件和环境中运行——这就是为什么我们把它放在最后。基本思想是,我们将在非 EE 环境中(在 Tomcat web 容器中)使用类型为RESOURCE_LOCAL的事务。换句话说,我们将让 JPA provider 实现管理非 JTA 容器中的事务(它不提供 JTA 实现,因此它显然不提供自动事务管理)。

Hibernate OGM 文档不建议在 JTA 环境之外使用 OGM(内置或独立)。但是,不推荐并不意味着它不起作用(特别是对于不支持事务的 MongoDB)。因此,我们可以尝试一下,并得出一些结论。

先决条件

  • MongoDB 2.2.2
  • Hibernate OGM 4.0.0.Beta2
  • JDK 1.7
  • NetBeans 7.2.1(或 Eclipse JUNO)
  • 阿帕奇雄猫 7

发展中的

启动 NetBeans 后,创建一个包含一个空 Maven web 应用的新项目,并将其命名为HOGMviaJPA_RESOURCELOCAL_Tomcat7。在New Web Application向导中,为Group IdPackage字段键入hogm.hnapi,并选择 Tomcat 应用服务器来部署这个应用。一旦你看到项目列在Projects窗口中,你需要编辑pom.xml文件(它必须在Project Files节点下)。在pom.xml文件中,通过粘贴众所周知的依赖项来添加 Hibernate OGM 发行版(包括 MongoDB 支持)。

编写应用代码

我们从开发实体类和persistence.xml开始。为我们的 MongoDB 数据库提供幸运数字代码的实体类(LuckyNumberEntity)非常简单,我们几乎在前面所有的例子中都使用过它。因此,我们可以跳过这里的列表(只要记住使用@Table(name="jpa_rl")。接下来,我们关注放在Other Sources/src/main/resources/META-INF)文件夹中的persistence.xml,。正如你在清单 4-34 中看到的,它没有指定 JTA 平台,没有特殊设置,只有设置为RESOURCE_LOCALtransaction-type和 MongoDB 连接设置。

清单 4-34。 persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" FontName2">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="HOGM_JPA_RESOURCE_LOCAL_PU" transaction-
                                type="RESOURCE_LOCAL">
        <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
        <class>hogm.hnapi.entities.LuckyNumberEntity</class>
        <properties>
            <property name="hibernate.ogm.datastore.provider" value="mongodb"/>
            <property name="hibernate.ogm.datastore.grid_dialect"
                            value="org.hibernate.ogm.dialect.mongodb.MongoDBDialect"/>
            <property name="hibernate.ogm.mongodb.database" value="tomcat_db"/>
            <property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
            <property name="hibernate.ogm.mongodb.port" value="27017"/>
        </properties>
    </persistence-unit>
</persistence>

现在我们开发负责在 MongoDB 数据库中保存幸运数字的 DAO 类,如清单 4-35 所示。如您所见,我们需要一些管道代码,因为我们使用的是 JPA 提供者提供的事务机制,在我们的例子中是 Hibernate OGM。交易方式begin, commit,rollback通过EntityManager提供。

清单 4-35。??【幸运数字】道类

package hogm.hnapi.dao;

import hogm.hnapi.entities.LuckyNumberEntity;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class LuckyNumberDAO {

    private static final Logger log = Logger.getLogger(LuckyNumberDAO.class.getName());
    private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("HOGM_JPA_RESOURCE_LOCAL_PU");
    private EntityManager em = emf.createEntityManager();

    public void persistAction(LuckyNumberEntity transientInstance) throws java.lang.Exception {

        log.log(Level.INFO, "Persisting LuckyNumberEntity instance ...");

        try {
            em.getTransaction().begin();
            em.persist(transientInstance);
            em.getTransaction().commit();

            log.log(Level.INFO, "Persist successful...");
        } catch (Exception re) {
            em.getTransaction().rollback();

            log.log(Level.SEVERE, "Persist failed...", re);
            throw re;
        } finally {
            if (em != null) {
                em.clear();
                em.close();
            }
        }
    }
}

现在假设我们有一段代码将用户与 DAO 类(一个 servlet 和一个简单的 XHTML 页面)连接起来,我们运行应用时看到一个错误,如下所示:

Caused by: java.lang.ClassNotFoundException: Could not load requested class : com.arjuna.ats.jta.TransactionManager

这个错误包含两个提示:第一,JPA 提供者没有找到任何 JTA 实现(正常,因为我们在一个非 JTA 环境中),第二,默认情况下,JPA 提供者正在寻找 JBoss JTA 实现。因此,我们需要添加 JBoss JTA JARs,并且我们必须在pom.xml文件中添加相应的 Maven 工件:

<dependency>
    <groupId>org.jboss.jbossts</groupId>
    <artifactId>jbossjta</artifactId>
    <version>4.16.4.Final</version>
</dependency>

现在,再次运行应用,一切都将正常工作(不要忘记 web 页面和 servlet——您可以从以前的项目中复制它们,或者直接从 Apress 存储库中下载应用)。

测试

启动 MongoDB 服务器,如你在第一章中所见。接下来,因为您处于 NetBeans/Tomcat(或 Eclipse/Tomcat)环境中,所以只需保存项目并单击Run(或 Eclipse 中的Run on Server)按钮来启动 Tomcat 并部署和运行应用。如果应用成功启动,你会在浏览器中看到类似于图 4-26 所示的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-26 。运行 HOGMviaJPA _ resource local _ Tomcat 7 应用

按几次生成幸运数字按钮,将一些幸运数字保存到 MongoDB 数据库(tomcat_db)集合(jpa_rl).)打开命令提示符,键入来自图 4-27 的命令,查看您的工作结果。您可以监控 Tomcat 日志消息,以防发生任何不必要的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-27 。检查 jpa_rl 收藏内容

这个应用的完整源代码被命名为HOGMviaJPA_RESOURCELOCAL_Tomcat7,可以在 Apress 资源库中找到。它是一个 NetBeans 项目,在 Tomcat 7 下进行了测试。

在这个例子中,我们通过 Hibernate Native API 和 JPA 完成了基于 bootstrapping Hibernate OGM 的应用集。

如果您不是 Maven 爱好者,但仍然想测试这些应用,您可以在Libraries节点下手动添加所需的 jar(在 NetBeans/Eclipse 中)并使用 NetBeans/Eclipse 接口工具编译和运行应用(正如在第一章中关于在本地获取 Hibernate OGM 和 MongoDB JARs 的章节中所讨论的)。

如果您也不喜欢 ide,您可以在您最喜欢的编辑器中编辑源代码,甚至是记事本,并从命令行使用 Ant 手动编译应用。例如,清单 4-36 中的 Ant 脚本(build.xml)可以用来编译部署在 Tomcat 下的应用。只需安装 Ant ( http://ant.apache.org/)并将其放在您的类路径中。将 Ant 脚本放在应用根文件夹中,打开命令提示符,导航到该文件夹并键入build.。这将编译应用并构建应用 WAR:

清单 4-36。 build.xml

<project name="hibernate" default="war">

<property name="sourcedir" value="${basedir}/WEB-INF/src"/>
<property name="targetdir" value="${basedir}/WEB-INF/classes"/>
<property name="librarydir" value="${basedir}/WEB-INF/lib"/>
<property name="builddir" value="${basedir}/build"/>

<path id="libraries">
      <fileset dir="${librarydir}">
          <include name="*.jar"/>
      </fileset>
</path>

<target name="clean">
     <delete dir="${targetdir}"/>
     <mkdir dir="${targetdir}"/>
     <delete dir="${builddir}"/>
     <mkdir dir="${builddir}"/>
</target>

<target name="compile" depends="clean, copy-resources">
    <javac srcdir="${sourcedir}"
           destdir="${targetdir}"
           classpathref="libraries"/>
</target>

<target name="copy-resources">
    <copy todir="${targetdir}">
        <fileset dir="${sourcedir}">
              <exclude name="**/*.java"/>
        </fileset>
    </copy>
</target>

<target name="war" depends="compile">
  <jar jarfile="${builddir}/{ *app_name* }.war" basedir="${basedir}"/>
 </target>

</project>

显然,您必须处理应用服务器和浏览器的启动/停止操作。

合成

开发和测试这些应用产生了这个部分。在分析了这些应用之后,我们可以得出一些关于 Hibernate OGM 和 MongoDB 在不同应用环境中集成的一般性结论。显然,Hibernate OGM 能够在许多不同的环境和架构中运行,并且可以与许多框架和工具一起使用。

此外,根据环境(尤其是 EE 和 JTA 单机版)和引导(通过 Hibernate Native API 或 JPA),我们可以提取 Hibernate OGM 正确服务 Java 应用所需的一系列强制和/或推荐设置。

在 EE 容器中通过 JPA Hibernate OGM

当您在 EE 容器中通过 JPA 使用 Hibernate OGM 时,您会希望在persistence.xml文件中包含以下设置:

  • 使用persistence-unit标签的transaction-type JTA 属性将交易类型设置为 JTA。
  • 使用hibernate.transaction.jta.platform属性将 JTA 平台设置为正确的 EE 容器。
  • 指定 JTA 数据源。这应该进行测试,在某些情况下可以跳过。对于 GlassFish,您可以使用内置数据源jdbc/sample(这是相关的 JNDI 名称),对于 JBoss,您可以使用java:/DefaultDS(7 版之前)或java:jboss/datasources/ExampleDS(7 版及更高版本)。使用jta-data-source标签指定数据源。

通过 EE 容器中的 Hibernate Native API 来 Hibernate OGM

当您通过 EE 容器中的 Hibernate Native API 使用 Hibernate OGM 时,您应该在hibernate.cfg.xml文件(或其编程版本)中包含以下设置:

  • 如果手动划分事务边界,则将属性hibernate.transaction.factory_class设置为org.hibernate.transaction.JTATransactionFactory,如果使用声明性事务划分,则设置为org.hibernate.transaction.CMTTransactionFactory
  • 将属性hibernate.current_session_context_class设置为jpa,以指示用于确定“当前”Session实例范围的策略。
  • 使用hibernate.transaction.jta.platform属性将 JTA 平台设置为正确的 EE 容器

在独立的 JTA 中通过 JPA Hibernate OGM

当您在非 EE 容器(独立的 JTA,如 Tomcat)中通过 JPA 使用 Hibernate OGM 时,您应该在persistence.xml文件中包含以下设置:

  • 使用persistence-unit标签的transaction-type JTA 属性将交易类型设置为 JTA。
  • 使用hibernate.transaction.jta.platform属性设置 JTA 平台(这是独立的 JTA——JOTM、JBoss JTA、Bitronix 等等——不是 JTA 内置的容器)。
  • 请查看特定于所选独立 JTA 的文档,因为它可能需要设置一些特定的属性。

通过独立 JTA 中的 Hibernate Native API 来 Hibernate OGM

当您在非 EE 容器(独立的 JTA,如 Tomcat)中通过 Hibernate Native API 使用 Hibernate OGM 时,您应该在hibernate.cfg.xml文件(或其编程版本)中包含以下设置:

  • 如果手动划分事务边界,则将属性hibernate.transaction.factory_class设置为org.hibernate.transaction.JTATransactionFactory,如果使用声明性事务划分,则设置为org.hibernate.transaction.CMTTransactionFactory
  • 将属性hibernate.current_session_context_class设置为jpa,以指示确定当前Session实例范围的策略。
  • 使用hibernate.transaction.jta.platform property设置 JTA 平台(这是独立的 JTA——JOTM、JBoss JTA、Bitronix 等等——不是 JTA 内置的容器)。
  • 请查看特定于所选独立 JTA 的文档,因为它可能需要设置一些特定的属性。

非 JTA 中 JPA 的 OGM 冬眠

当您在非 JTA 环境(如 Tomcat)中通过 JPA 使用 Hibernate OGM 时,您应该在persistence.xml文件中包含以下设置:

  • 使用persistence-unit标签的transaction-type JTA 属性将交易类型设置为RESOURCE_LOCAL
  • 不要指定任何 JTA 平台,而是向应用提供 JBoss JTA JARs。
  • 自己管理EntityManager及其 JTA 交易。

通过非 JTA 的 Hibernate Native API 来 Hibernate OGM

当您在非 JTA 环境(如 Tomcat)中通过 Hibernate Native API 使用 Hibernate OGM 时,您应该在hibernate.cfg.xml文件(或其编程版本)中包含以下设置:

  • 将属性hibernate.transaction.factory_class设置为org.hibernate.transaction.JDBCTransactionFactory.
  • 将属性hibernate.current_session_context_class设置为thread.
  • 使用 Hibernate 的事务和内置的每请求会话功能,而不是调用 JDBC API。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意:hibernate.transaction.jta.platform属性(表示 JTA 平台)接受的值可在第二章的“Bootstrap Hibernate OGM Using JPA”一节中找到。

摘要

在本章中,您看到了如何通过改变容器环境、引导过程以及相关的框架和工具,将 Hibernate OGM 与不同种类的应用集成在一起。本章介绍的应用列表包括:

  • Java SE 和 Mongo DB—hello world 示例
  • 在非 JTA 环境(JDBC 事务,Tomcat 7)中 Hibernate OGM(通过 Hibernate Native API)
  • 在独立的 JTA 环境(JBoss JTA,Tomcat 7)中 Hibernate OGM(通过 Hibernate Native API)
  • 在内置的 JTA 环境(没有 EJB,GlassFish 3)中 Hibernate OGM(通过 Hibernate Native API)
  • 在内置的 JTA 环境(EJB/BMT,GlassFish 3)中 Hibernate OGM(通过 Hibernate Native API)
  • 在内置的 JTA 环境(EJB/CMT,GlassFish 3)中 Hibernate OGM(通过 Hibernate Native API)
  • 在内置的 JTA 环境(GlassFish AS 3)中 Hibernate OGM(通过 JPA)
  • 在内置的 JTA 环境(JBoss AS 7)中 Hibernate OGM(通过 JPA)
  • 在内置的 JTA 环境(JBoss AS 7 和 Seam 应用)中 Hibernate OGM(通过 JPA)
  • 在内置的 JTA 环境(GlassFish 和 Spring 应用)中 Hibernate OGM(通过 JPA)
  • 在独立的 JTA 环境中 Hibernate OGM(通过 JPA)JPA/JTA(Tomcat)
  • 在非 JTA 环境中 Hibernate OGM(RESOURCE _ LOCAL,Apache Tomcat 7)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值