编写简单的 MicroProfile 应用程序,第 2 部分

添加持久性

更新 MicroProfile 会议应用程序以添加持久性

Comments

系列内容:

此内容是该系列 4 部分中的第 # 部分: 编写简单的 MicroProfile 应用程序,第 2 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:编写简单的 MicroProfile 应用程序,第 2 部分

敬请期待该系列的后续内容。

概述

在本教程中,对本系列第 1 部分中创建的 MicroProfile 会议应用程序进行了更新,以添加持久性。

在编写本教程时,MicroProfile 尚未定义持久性机制。这不是因为 MicroProfile 未重视持久性,而是表明可以做出许多同样有效的选择来确保微服务中的持久性。在本教程中,使用了 Cloudant,但还有其他一些同样有效的选项,如 JPA、MongoDB 或 Cassandra。

改编自博客文章:“编写简单的 MicroProfile 应用程序:添加持久性”。

前提条件

步骤

1 检出源代码

  1. 从命令行运行以下命令:
    git clone https://github.com/IBM/microprofile-meeting-persistence.git
  2. 在 Eclipse 中,将项目作为现有项目导入。
    1. 在 Eclipse 中,切换至 Git 透视图。
    2. 单击 Git Repositories 视图中的 Clone a Git repository
    3. 输入 URI:https://github.com/IBM/microprofile-meeting-persistence.git
    4. 单击 Next,然后再次单击 Next,接受默认值。
    5. Initial branch 下拉列表中,单击 master
    6. 选择 Import all existing Eclipse projects after clone finishes,然后单击 Finish
    7. 切换至 Java EE 透视图。
    8. 在 Project Explorer 视图中会自动创建会议项目。

2 安装 MongoDB

根据您所使用的平台,安装说明可能会有所不同。对于本练习,您应该从 mongoDB 下载中心获取 MongoDB 的社区版本。

  1. 在安装后,可以使用以下命令运行 MongoDB 数据库守护程序:
    mongod -dbpath <path to database>

数据库必须正在运行,应用程序才能正常工作。如果数据库未在运行,服务器日志中会有很多噪音数据。

3 更新应用程序以根据 MongoDB API 进行编译

要开始编写代码,必须更新 Maven pom.xml,指明对 MongoDB 的依赖:

  1. 打开 pom.xml
  2. 在编辑器上,选择 Dependencies 选项卡。
  3. 在 Dependencies 选项卡上有两个部分,一个部分用于 Dependencies,另一个部分用于 Dependency Management。在 Dependencies 框的右侧,有一个 Add 按钮。单击 Add 按钮。
  4. groupdId 中输入 org.mongodb
  5. artifactId 中输入 mongo-java-driver
  6. version 中输入 2.14.3
  7. scope 下拉列表中,选择 provided。这将允许应用程序进行编译,但会阻止 Maven WAR 打包器将 API 放入 WAR 文件中。稍后将配置构建,使其可供服务器使用。
  8. 单击 OK
  9. 保存 pom.xml

4 更新 MeetingsUtil 以在 MongoDB 与 JSON-Processing 对象之间转换

在更新应用程序时,需要在 MongoDB 数据表示和 JSON-Processing 数据表示之间进行转换。可以将其放入 MeetingsUtil 类中,而不是分散放置:

  1. 通过 meetings > Java Resources > src/main/java > net.wasdev.samples.microProfile.meetings > MeetingsUtil.java,打开 MeetingsUtil
  2. 需要的第一个方法采用 MongoDB DBObject,并返回 JsonObject。在文件一开头的类定义之后,添加方法声明:
    public static JsonObject meetingAsJsonObject(DBObject obj) {
           // method body will go here
  3. 这引入了一个新类,即 DBObject 类,它位于 com.mongodb 包中:
    import com.mongodb.DBObject;
  4. 创建新 JsonObject 的第一步是使用 JSON 类创建 JsonObjectBuilder。所有这些都已在类中使用,因此不需要新导入内容。将以下行添加到方法的开头:
    public static JsonObject meetingAsJsonObject(DBObject obj) {
           JsonObjectBuilder builder = Json.createObjectBuilder();
  5. 会议的 JSON 对象使用名为 id 的字段。MongoDB 对同一个函数使用 _id,因此 JSON 中的 id 将映射至 MongoDB 中的 _id。要从 DBObject 中抽取 id 字段,并将其添加到 JSON,可将以下行添加到方法中:
    builder.add("id", (String) obj.get("_id"));
  6. title 字段可以直接从 JSON 映射到 MongoDB 对象。title 是 String,但为确保调用正确的 add 方法,将其强制转换为 String。添加以下行:
    builder.add("title", (String) obj.get("title"));
  7. duration 字段是 Long。与前面一样,需要将其强制转换为 Long,确保调用正确的 add 方法:
    builder.add("duration", (Long) obj.get("duration"));
  8. 如果会议正在运行,它将具有一个 meetingURL 字段。如果会议未在运行,它将返回 null。如果没有 meetingURL 字段,我们不会希望将其添加到返回的会议,所以仅当 meetingURL 不为 null 时,我们才希望进行添加。添加以下内容:
    String meetingURL = (String) obj.get("meetingURL");
       if (meetingURL != null)
           builder.add("meetingURL", meetingURL);
  9. 最后,我们需要返回 JsonObject。可以通过在 JsonObjectBuilder 上调用 build 方法来获取。添加以下内容:
    return builder.build();
  10. 接下来,我们需要一个相反的方法,从 JsonObject 映射到 DBObject。此方法可实现两个目的:它将创建新的 DBObject,并将 JsonObject 合并到现有的 DBObject 中,使它具有两个参数而非一个。在先前方法的末尾添加以下内容:
    public static DBObject meetingAsMongo(JsonObject json, DBObject mongo) {
           // method body will go here
       }
  11. 在此新方法中需执行的第一个操作是检查传入的 DBObject 是否为 null。如果为 null,将创建新的 DBObjectDBObject 是抽象类,因此将实例化 BasicDBObject。在方法的开头添加以下内容(此后会出现编译错误,但不用担心,很快就会修复):
    if (mongo == null) {
           mongo = new BasicDBObject();
  12. 接下来,需要将 idJsonObject 移至新的 DBObject。仅当创建新的 DBObject 时才应执行此操作,因为非这种情况下,URLJsonObject 之间断开连接可能会导致 id 被错误覆盖。添加以下内容:
    mongo.put("_id", json.getString("id"));
       }
  13. 这引入了一个新类 BasicDBObject,它位于 com.mongodb 包中,但需要导入:
    import com.mongodb.BasicDBObject;
  14. title 字段是从 JsonObjectDBObject 的直接映射,但 JsonObject 包含需要转换为 String 的 JsonStringtoString 方法不能用于此项,因为它会用引号括起 String 字面值,而在此处则不需要。幸运的是,JsonObject 为此提供了便捷的 getString 方法:
    mongo.put("title", json.getString("title"));
  15. duration 字段也是直接映射,但它是 JsonObject 中的 JsonNumber,需要转换为 Long 才能进入 DBObject 中:
    mongo.put("duration", ((JsonNumber) json.get("duration")).longValue());
  16. 这引入了需要导入的 JsonNumber
    import javax.json.JsonNumber;
  17. 我们希望获取 meetingURL,但它可能并不存在,所以不能使用 getString 方法,因为如果具有该名称的字段不存在,此方法会抛出 NullPointerException。要解决此问题,可使用 get 方法,该方法将返回 JsonString。然后可以执行 null 检查,仅当它不为 null 时,才会将其添加到 JSON。因为 toString 会用引号括起字符串,所以必须使用 getString 方法:
    JsonString jsonString = json.getJsonString("meetingURL");
        if (jsonString != null) {
            mongo.put("meetingURL", jsonString.getString());
        }
  18. 这引入了需要导入的 jsonString
    import javax.json.JsonString;
  19. 最后,返回 mongo 对象并保存文件。
    return mongo;

5 更新 MeetingManager

MeetingManager 当前使用 ConcurrentMap 来存储会议。所有与此项集成的代码都必须更新。在本部分中,这会一步步完成;因此,在您结束之前会出现编译错误:

  1. 通过 meetings > Java Resources > src/main/java > net.wasdev.samples.microProfile.meetings > MeetingManager.java 打开 MeetingManager 类。
  2. 在类定义后面,从文件中删除以下行:
    private ConcurrentMap<String, JsonObject> meetings = new ConcurrentHashMap<();
  3. 要与 MongoDB 交互,就必须注入 DB 的实例。可以使用 @Resource 注解(该注解可以识别从 JNDI 注入哪些资源)完成此操作。添加(在先前步骤中)除去了 ConcurrentMap 的代码:
    @Resource(name="mongo/sampledb")
       private DB meetings;
  4. 这引入了两个新类:MongoDB DB 类和 Java EE @Resource 注解。需要导入这些内容。
    import javax.annotation.Resource;
       import com.mongodb.DB;
  5. MongoDB 将集合中的条目存储在数据库中。在写或读时,要执行的第一个操作是选择集合。为了稍后简化代码,让我们创建一个方便的方法来获取集合:
    public DBCollection getColl() {
           return meetings.getCollection("meetings");
       }
  6. 这引入了一个新类,即 DBCollection 类,它位于 com.mongodb 包中。需要导入该类。
    import com.mongodb.DBCollection;
  7. 查找并编辑 add 方法:
    1. add 方法中除去方法主体。这将被替换以更新数据库。
      public void add(JsonObject meeting) {
                // code will be added here
            }
    2. 先获取集合。
      DBCollection coll = getColl();
    3. 为此方法提供了 JsonObject,而我们需要为 MongoDB 提供 DBObject,因此需要转换。API 可以从数据库中获取现有条目,因此首先让我们看看是否可以使用 findOne 方法从数据库中找到一些内容:
      DBObject existing = coll.findOne(meeting.getString("id"));
    4. 这引入了一个新类,即 DBObject 类,它位于 com.mongodb 包中。需要导入该类。
      import com.mongodb.DBObject;
    5. 接下来,调用 MeetingsUtil 便捷方法,以从 JsonObject 转换为 DBObject
      DBObject obj = MeetingsUtil.meetingAsMongo(meeting,  existing);
    6. 最后,将新的或更改过的 DBObject 重新保存到数据库中:
      coll.save(obj);
  8. 查找并更新 get 方法:
    1. get 方法中除去方法主体(这将被替换以从数据库中获取的):
      public JsonObject get(String id) {
                // code will be added here
            }
    2. 要获取单个条目,必须获取集合,根据 id 找到条目,然后使用之前创建的实用方法转换为 JsonObject。将以下行添加到 get 方法:
      return MeetingsUtil.meetingAsJsonObject(getColl().findOne(id));
  9. 查找并更新 list 方法。更新 list 方法稍微有些复杂。一般结构保持相同,但 for 循环将发生变化。为了对集合中的条目进行迭代,将使用 DBCursor,并返回 DBObject。然后,需要将 DBObject 转换为 JsonObject。将类似如下的现有循环:
    for (JsonObject meeting : meetings.values()) {
           results.add(meeting);
       }

    替换为:

    for (DBObject meeting : getColl().find()) {
           results.add(MeetingsUtil.meetingAsJsonObject(meeting));
       }
  10. 查找并更新 startMeeting 方法:

    此更改将从根本上简化代码,因为不需要创建和合并多个 JsonObject,而只需将 meetingURL 添加到现有 DBObject 中。仍需要从 JsonObject 中访存 idurl

    1. startMeeting 方法中除去以下行:
      JsonObject existingMeeting = meetings.get(id);
            JsonObject updatedMeeting = MeetingsUtil.createJsonFrom(existingMeeting).add("meetingURL", url).build();
            meetings.replace(id, existingMeeting, updatedMeeting);
    2. 在访存 idurl 之后,将除去的代码替换为以下四行以获取集合,查找会议条目,设置 meetingURL,然后将其重新保存到数据库中:
      DBCollection coll = getColl();
            DBObject obj = coll.findOne(id);
            obj.put("meetingURL", url);
            coll.save(obj);
  11. 保存该文件。

MeetingManager 现在能够持久存储到 MongoDB 数据库中并返回。但仍需要配置服务器才能将其启用。这包含两部分:第一部分是服务器配置,第二部分是设置服务器运行时。

6 更新服务器配置

服务器配置是项目的一部分,我们先来对此进行配置:

  1. 通过 src > main > liberty > config > server.xml 打开 server.xml
  2. mongodb-2.0 添加为新功能。它应类似如下:
    <featureManager>
           <feature>microProfile-2.0</feature>
           <feature>mongodb-2.0</feature>
       </featureManager>
  3. 需要定义共享库,以供应用程序和运行时用于定义 MongoDB 资源:
    <library id="mongodriver">
         <file name="${shared.resource.dir}/mongo-java-driver.jar"/>
       </library>
  4. 接下来,需要定义 mongo。这将告知服务器 mongo 服务器实例正在何处运行:
    <mongo id="mongo" libraryRef="mongodriver">
         <ports>27017</ports>
         <hostNames>localhost</hostNames>
       </mongo>
  5. 接下来,定义数据库:
    <mongoDB databaseName="meetings" jndiName="mongo/sampledb" mongoRef="mongo"/>
  6. 最后,配置应用程序,使其可以查看 MongoDB 类:
    <webApplication location="meetings-${project.version}.war">
         <classloader commonLibraryRef="mongodriver"/>
       </webApplication>
  7. 保存该文件。

下一步是配置 Maven 构建,确保所有资源最终都处于正确的位置。

7 更新 Maven POM

必须更新 Maven POM 以完成一些事项:需要将 MongoDB Java 驱动程序复制到 Liberty 服务器中,定义其他引导程序属性,将应用程序复制到其他位置,并确保安装了 mongodb-2.0 功能:

  1. 在项目的根目录下打开 pom.xml
  2. 在编辑器中选择 pom.xml 选项卡。

复制 MongoDB Java 驱动程序

  1. 在文件中搜索字符串 maven-dependency-plugin,您应该会在文件中看到以下内容:
    <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-dependency-plugin</artifactId>
       <version>2.10</version>
       <executions>
           <execution>
               <id>copy-server-files</id>
               <phase>package</phase>
               <goals>
                   <goal>copy-dependencies</goal>
               </goals>
               <configuration>
                   <includeArtifactIds>server-snippet</includeArtifactIds>
                   <prependGroupId>true</prependGroupId>
                   <outputDirectory>${project.build.directory}/wlp/usr/servers/${wlpServerName}/configDropins/defaults</outputDirectory>
               </configuration>
           </execution>
       </executions>

    这会将服务器片段从依赖项复制到服务器配置目录中。我们将向其添加 mongo-java-driver 下载说明,将其复制到 usr/shared/resources 文件夹,然后从 JAR 文件名中删除版本。最后一部分意味着每次升级依赖版本时,我们都不必记得更新 server.xml

  2. 要添加这些额外的说明,可在 </execution> 结束标记后添加以下行:
    <execution>
           <id>copy-mongodb-dependency</id>
           <phase>package</phase>
           <goals>
               <goal>copy-dependencies</goal>
           </goals>
           <configuration>
               <includeArtifactIds>mongo-java-driver</includeArtifactIds>
               <outputDirectory>${project.build.directory}/wlp/usr/shared/resources/</outputDirectory>
               <stripVersion>true</stripVersion>
           </configuration>
       </execution>

project.version 放入 bootstrap.properties

server.xml 按工件名称和版本引用 WAR。使用必须向服务器提供的变量来引用版本。可以使用 bootstrap.properties 轻松完成此操作:

  1. pom.xml 文件中搜索字符串 <bootstrapProperties>。您应该会在文件中看到以下内容:
    <bootstrapProperties>
           <default.http.port>${testServerHttpPort}</default.http.port>
           <default.https.port>${testServerHttpsPort}</default.https.port>
       </bootstrapProperties>
  2. 在结束标记前添加以下行:
    <project.version>${project.version}</project.version>

将应用程序复制到 apps 文件夹

Maven POM 可将应用程序部署到 Liberty 服务器的 dropins 文件夹中,但这不允许使用共享库。因此,需要将应用程序复制到 apps 文件夹:

  1. pom.xml 中搜索字符串 dropins,您应该会看到以下内容:
    <goals>
           <goal>copy-resources</goal>
       </goals>
       <configuration>
         <outputDirectory>${project.build.directory}/wlp/usr/servers/${wlpServerName}/dropins</outputDirectory>
  2. 将 dropins 一词更新为 apps。它应类似如下:
    <configuration>
         <outputDirectory>${project.build.directory}/wlp/usr/servers/${wlpServerName}/apps</outputDirectory>

从 Liberty 存储库安装 mongodb-2.0 功能

mongodb-2.0 功能没有位于 Maven 存储库中所存储的 Liberty 服务器安装中,因此需要在构建时从 Liberty 存储库中进行下载:

  1. pom.xml 中搜索字符串 package-server,您应该会看到以下内容:
    <execution>
             <id>package-app</id>
             <phase>package</phase>
             <goals>
                 <goal>package-server</goal>
             </goals>
         </execution>
       </executions>
  2. </executions> 标记前添加以下行,以安装 mongodb-2.0 功能:
    <execution>
           <id>install-feature</id>
           <phase>package</phase>
           <goals>
               <goal>install-feature</goal>
           </goals>
           <configuration>
               <features>
                   <acceptLicense>true</acceptLicense>
                   <feature>mongodb-2.0</feature>
               </features>
           </configuration>
       </execution>
  3. 保存 pom.xml

8 运行应用程序

Eclipse WDT

可以使用两种方法从 WDT 中运行应用程序:

  • 第一种方法是使用 Maven 来构建和运行项目:
    1. 运行 Maven install 目标来构建和测试项目:右键单击 meetings 项目中的 pom.xml,单击 Run As… > Maven Build…,然后在 Goals 字段中输入 install,并单击 Run。第一次运行此目标时,可能会花几分钟时间来下载 Liberty 依赖项。
    2. liberty:start-server goal 运行 Maven 构建:右键单击 pom.xml,单击 Run As… > Maven Build,然后在 Goals 字段中输入 liberty:start-server,并单击 Run。这会在后台启动服务器。
    3. 打开应用程序(地址为 http://localhost:9080/meetings/)。
    4. 要再次停止服务器,可运行 liberty:stop-server 构建目标。
  • 第二种方法是右键单击 meetings 项目,并选择 Run As… > Run on Server,但如果这样做,须注意几点。WDT 不会像您期望的那样自动添加 MicroProfile 功能,因此您需要手动添加这些功能。此外,除非添加 include,否则不会选取 src/main/liberty/config 中发生的任何配置更改。

了解有关 MicroProfile 和 WebSphere Liberty 的更多信息。

IBM Cloud

您可以使用 Cloud Foundry 在 IBM Cloud 上运行应用程序。

  1. 登录到您的 IBM Cloud 帐户
    ibmcloud cf login
  2. 将您的应用程序推送到 IBM Cloud(指定不启动)
    ibmcloud cf push --no-start <yourappname> -p wlp/usr/servers/meetingsServer
  3. 在应用部署时,在 IBM Cloud 上创建 Compose MongoDB 实例。将服务实例命名为 mongo/sampledb,并保留所有其他默认配置。单击 Create
  4. 从 IBM Cloud Dashboard 中查找已部署的应用,然后单击该应用。
  5. 在左侧,单击 Connections。然后单击 Connect existing
  6. 查找 mongo/sampledb 并单击此项。在提示您重新载入应用时,单击 Restage
  7. 在应用完成重新载入之后,可以重新访问路由/url。确保在路由的末尾添加 /meetings,以访问应用的主页。

本文翻译自 :Write a simple MicroProfile application, Part 2: Add persistence(2019-01-29)


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=1065203
ArticleTitle=编写简单的 MicroProfile 应用程序,第 2 部分: 添加持久性
publish-date=03192019