当前位置: 首页 > 工具软件 > Hibernate OGM > 使用案例 >

ogm session_带有Hibernate OGM的NoSQL –第三部分:在WildFly上构建REST应用程序

慎志国
2023-12-01

ogm session

欢迎回到我们的教程系列“带有Hibernate OGM的NoSQL”! 感谢Gunnar Morling( @gunnarmorling )创建了本教程。 在这一部分中,您将学习如何在WildFly服务器上运行的Java EE应用程序中使用Hibernate OGM。 使用本教程前面部分已经知道的实体模型 ,我们将构建一个基于REST的小型应用程序来管理加息。 如果您还没有阅读本系列的前两期,可以在这里找到它们:

在下面的内容中,您将学习如何准备WildFly以使其与Hibernate OGM一起使用,配置JPA持久性单元,创建用于访问数据的存储库类以及在这些之上提供REST资源。 在本文中,我们将主要关注与持久性相关的方面,因此使用REST / JAX-RS的一些基本经验可能会有所帮助。 本教程的完整源代码托管在GitHub上。

准备WildFly

WildFly服务器运行时基于JBoss Modules系统。 这提供了一个模块化的类加载环境,其中每个库(例如Hibernate OGM)都是其自己的模块,声明了它依赖的其他模块的列表,并且仅从那些其他依赖项中“看到”了类。 这种隔离使人们摆脱了可怕的“类路径地狱”。

SourceForge提供了包含Hibernate OGM所有必需模块的ZIP文件。 我们昨天发布的 Hibernate OGM 4.2支持WildFly 9,因此请下载hibernate-ogm-modules-wildfly9-4.2.0.Final.zip 。 如果您使用的是WildFly 8,请使用Hibernate OGM 4.1并获取hibernate-ogm-modules-wildfly8-4.1.3.Final.zip

将与您的WildFly版本相对应的归档文件解压缩到应用程序服务器的modules目录中。 如果您希望原始的WildFly目录保持不变,则还可以将Hibernate OGM模块档案解压缩到任何其他文件夹,并将其配置为服务器要使用的“模块路径”。 为此,请导出以下两个环境变量,以匹配您的特定环境:

export JBOSS_HOME=/path/to/wildfly
export JBOSS_MODULEPATH=$JBOSS_HOME/modules:/path/to/ogm/modules

如果您正在使用Maven WildFly插件 (例如在开发过程中启动WildFly),则可以通过在POM文件中使用以下插件配置来实现相同的目的:

...
<plugin>
    <groupId>org.wildfly.plugins</groupId>
    <artifactId>wildfly-maven-plugin</artifactId>
    <version>1.1.0.Alpha1</version>
    <configuration>
        <jboss-home>/path/to/wildfly</jboss-home>
        <modules-path>/path/to/ogm/modules</modules-path>
    </configuration>
</plugin>
...

设置项目

首先使用“ war”包装类型创建一个新的Maven项目。 将以下内容添加到您的pom.xml中

...
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.hibernate.ogm</groupId>
            <artifactId>hibernate-ogm-bom</artifactId>
            <type>pom</type>
            <version>4.2.0.Final</version>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
...

这样可以确保获得匹配版本的Hibernate OGM模块和任何(可选)依赖项。 然后将依赖项添加到Java EE 7 API和Hibernate OGM后端模块之一,例如Infinispan ,JBoss的高性能,分布式键/值数据网格(其他任何诸如hibernate-ogm-mongodb或全新的hibernate -ogm-cassandra模块也可以工作):

...
<dependencies>
    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>7.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.hibernate.ogm</groupId>
        <artifactId>hibernate-ogm-infinispan</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>
...

provided作用域使这些依赖项可用于编译,但是阻止将它们添加到生成的WAR文件中。 那是因为Java EE API已经是WildFly的一部分,而Hibernate OGM将通过您之前解压缩的模块来贡献。

但是,仅将这些模块添加到服务器并不会削减它。 还需要将它们注册为应用程序的模块依赖项。 为此,添加具有以下内容的文件src / main / webapp / WEB-INF / jboss-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure
    xmlns="urn:jboss:deployment-structure:1.2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <deployment>
        <dependencies>
            <module name="org.hibernate" slot="ogm" services="import" />
            <module name="org.hibernate.ogm.infinispan" services="import" />
            <module name="org.hibernate.search.orm" services="import" />
        </dependencies>
    </deployment>
</jboss-deployment-structure>

这将使Hibernate OGM核心和Infinispan后端以及Hibernate Search可用于您的应用程序。 后者将很快用于运行JP-QL查询。

添加实体类和存储库

有了基本的项目基础结构之后,就该添加实体类和存储库类以访问它们了。 实体类型与第1部分中的基本相同,只是现在使用@Indexed进行注释,以允许它们通过Hibernate Search和Lucene进行查询:

@Entity
@Indexed
public class Person {

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

    private String firstName;
    private String lastName;

    @OneToMany(
        mappedBy = "organizer",
        cascade = { CascadeType.PERSIST, CascadeType.MERGE },
        fetch = FetchType.EAGER
    )
    private Set<Hike> organizedHikes = new HashSet<>();

    // constructors, getters and setters...
}
@Entity
@Indexed
public class Hike {

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

    private String description;
    private Date date;
    private BigDecimal difficulty;

    @ManyToOne
    private Person organizer;

    @ElementCollection(fetch = FetchType.EAGER)
    @OrderColumn(name = "sectionNo")
    private List<HikeSection> sections;

    // constructors, getters and setters...
}
@Embeddable
public class HikeSection {

    private String start;
    private String end;

    // constructors, getters and setters...
}

为了使用这些实体,必须定义一个JPA持久性单元。 为此,创建文件src / main / resources / META-INF / persistence.xml

<?xml version="1.0" encoding="utf-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">

    <persistence-unit name="hike-PU" transaction-type="JTA">
        <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>

            <class>org.hibernate.ogm.demos.ogm101.part3.model.Person</class>
            <class>org.hibernate.ogm.demos.ogm101.part3.model.Hike</class>

            <properties>
                <property name="hibernate.ogm.datastore.provider" value="INFINISPAN" />
                <property name="hibernate.ogm.datastore.database" value="hike_db" />
                <property name="hibernate.ogm.datastore.create_database" value="true" />
            </properties>
    </persistence-unit>
</persistence>

在这里,我们定义了一个名为“ hike-PU”的持久性单元。 Infinispan是一个完全事务性的数据存储,使用JTA作为事务类型可以使持久性单元参与容器管理的事务。 将HibernateOgmPersistence指定为提供程序类将启用Hibernate OGM(而不是Hibernate ORM),它已为设置后端(在这种情况下为INFINISPAN),数据库名称等配置了一些属性。

请注意,实际上,当在Java EE容器(例如WildFly)中运行时,实际上不需要在persistence.xml中指定实体类型。 相反,它们应该被自动拾取。 使用Hibernate OGM时,目前很不幸。 这是一个已知的限制(请参阅OGM-828 ),我们希望尽快解决。

下一步是实现用于访问远足和组织者数据的存储库类。 例如,下面显示了PersonRepository类:

@ApplicationScoped
public class PersonRepository {

    @PersistenceContext
    private EntityManager entityManager;

    public Person create(Person person) {
        entityManager.persist( person );
        return person;
    }

    public Person get(String id) {
        return entityManager.find( Person.class, id );
    }

    public List<Person> getAll() {
        return entityManager.createQuery( "FROM Person p", Person.class ).getResultList();
    }

    public Person save(Person person) {
        return entityManager.merge( person );
    }

    public void remove(Person person) {
        entityManager.remove( person );
        for ( Hike hike : person.getOrganizedHikes() ) {
            hike.setOrganizer( null );
        }
    }
}

实现很简单; 通过@ApplicationScoped批注,该类被标记为应用程序范围的CDI bean(即,该bean的单个实例在应用程序的整个生命周期中都存在)。 它通过依赖注入获得JPA实体管理器,并使用该实体管理器来实现一些简单的CRUD方法(创建,读取,更新,删除)。

请注意, getAll()方法如何使用JP-QL查询返回所有人员对象。 执行后,此查询将转换为等效的Lucene索引查询,该查询将通过Hibernate Search运行。

远足资料库看起来非常相似,因此为简洁起见在此省略。 您可以在GitHub上找到其源代码

公开REST服务

JAX-RS使构建基于REST的Web服务变得轻而易举。 它定义了一个声明式编程模型,您可以在其中注释简单的旧Java类,以提供HTTP端点的GET,POST,PUT等操作的实现。

深入描述JAX-RS超出了本教程的范围,例如,如果您想了解更多信息,请参考Java EE 7教程 。 让我们以资源类管理人员的一些方法为例:

@Path("/persons")
@Produces("application/json")
@Consumes("application/json")
@Stateless
public class Persons {

    @Inject
    private PersonRepository personRepository;

    @Inject
    private ResourceMapper mapper;

    @Inject
    private UriMapper uris;

    @POST
    @Path("/")
    public Response createPerson(PersonDocument request) {
        Person person = personRepository.create( mapper.toPerson( request ) );
        return Response.created( uris.toUri( person ) ).build();
    }

    @GET
    @Path("/{id}")
    public Response getPerson(@PathParam("id") String id) {
        Person person = personRepository.get( id );
        if ( person == null ) {
            return Response.status( Status.NOT_FOUND ).build();
        }
        else {
            return Response.ok( mapper.toPersonDocument( person ) ).build();
        }
    }

    @GET
    @Path("/")
    public Response listPersons() { … }

    @PUT
    @Path("/{id}")
    public Response updatePerson(PersonDocument request, @PathParam("id") String id) { … }

    @DELETE
    @Path("/{id}")
    public Response deletePerson(@PathParam("id") String id) { … }
}

@Path@Produces@Consumes注释由JAX-RS所定义。 它们将资源方法绑定到特定的URL,以期望并创建基于JSON的消息。 @GET@GET @POST@GET @PUT@DELETE配置每个方法负责哪个HTTP动词。

@Stateless注释将此POJO定义为无状态会话Bean。 可以通过基于@Inject的依赖项注入来获取诸如PersonRepository的依赖项。 实现会话bean使您可以通过容器进行透明的事务管理。 对Persons方法的调用将自动包装在一个事务中,并且Hibernate OGM与数据存储区的所有交互都将参与其中。 这意味着您对托管实体所做的任何更改(例如,通过PersonRepository#create()持久保存新人员或修改从实体管理器检索到的Person对象)都将在方法调用返回后提交到数据存储中。

映射模型

请注意,我们的REST服务的方法本身不会返回并接受托管实体类型,而是返回特定的传输结构,例如PersonDocument

public class PersonDocument {

    private String firstName;
    private String lastName;
    private Set<URI> organizedHikes;

    // constructors, getters and setters...
}

这样做的理由是以URI的形式表示关联的元素( Person#organizedHikesHike#organizer ),这使客户端能够根据需要获取这些链接的资源。 例如,对http:// myserver / ogm-demo-part3 / hike-manager / persons / 123的GET调用可能返回如下的JSON结构:

{
    "firstName": "Saundra",
    "lastName": "Johnson",
    "organizedHikes": [
        "http://myserver/ogm-demo-part3/hike-manager/hikes/456",
        "http://myserver/ogm-demo-part3/hike-manager/hikes/789"
    ]
}

内部模型(例如,实体Person )和外部模型(例如PersonDocument )之间的映射可能很快成为一项繁琐而乏味的任务,因此需要一些基于工具的支持。 存在一些用于此工作的工具,其中大多数使用反射或运行时字节代码生成来在不同模型之间传播状态。

MapStruct寻求另一种方法,这是我的一个业余项目,并在编译时(例如,使用Maven或在您的IDE中)通过Java注释处理器生成bean映射器实现。 它生成的代码是类型安全的,快速的(它使用简单的方法调用,没有反射)并且没有依赖关系。 您只需要使用所需的源和目标类型的映射方法声明Java接口,MapStruct就会在编译过程中生成一个实现:

@Mapper(
    // allows to obtain the mapper via @Inject
    componentModel = "cdi",

    // a hand-written mapper class for converting entities to URIs; invoked by the generated
    // toPersonDocument() implementation for mapping the organizedHikes property
    uses = UriMapper.class
)
public interface ResourceMapper {

    PersonDocument toPersonDocument(Person person);

    List<PersonDocument> toPersonDocuments(Iterable<Person> persons);

    @Mapping(target = "date", dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    HikeDocument toHikeDocument(Hike hike);

    // other mapping methods ...
}

然后可以在Persons REST资源中使用生成的实现,以从内部模型映射到外部模型,反之亦然。 如果您想了解有关此模型映射方法的更多信息,请查看GitHub上的完整mapper界面MapStruct参考文档

结语

在本系列教程的这一部分中,您学习了如何将Hibernate OGM添加到WildFly应用程序服务器,并使用它来访问Infinispan作为小型REST应用程序的数据存储。

WildFly是使用Hibernate OGM的应用程序的绝佳运行时环境,因为它提供了现成的大多数必需构建块(例如JPA / Hibernate ORM,JTA,事务管理等),并且紧密集成并可以使用。 我们的模块ZIP可以非常轻松地将Hibernate OGM模块放入组合中,而无需每次在您的应用程序中重新部署它们。 有了WildFly Swarm ,还支持微服务体系结构样式,但是我们将再留一点时间来展示如何将Hibernate OGM与Wildfly Swarm一起使用(当前,WildFly Swarm仍然缺少JPA支持)。

您可以在GitHub上找到该项目的源代码。 要构建项目,请运行mvn clean install (它使用Arquillian本身对一个令人兴奋的主题执行REST服务的集成测试 )。 另外,Maven WildFly插件可用于启动WildFly实例并通过mvn wildfly:run部署应用程序,这非常适合手动测试,例如通过curl或wget发送HTTP请求。

如果您有任何疑问,请在下面的评论中告诉我们,或发送推文到@Hibernate 。 也欢迎您对本教程的后续部分的希望。 敬请关注!

翻译自: https://www.javacodegeeks.com/2015/06/nosql-with-hibernate-ogm-part-three-building-a-rest-application-on-wildfly.html

ogm session

 类似资料: