Google Cloud Platform 发布了针对 Cloud Spanner 数据库的 Hibernate 方言!您现在可以使用 Google Cloud 的完全托管的多区域、水平扩展的关系型数据库,与 Hibernate ORM 和任何 Hibernate 支持的 JPA 应用程序一起使用。

什么是 Cloud Spanner?

Cloud Spanner 是水平扩展的 - 因此,如果您的业务数据增长或事务负载增加,您可以轻松添加一个新的 Cloud Spanner 节点来适应增长。Cloud Spanner 将自动分片您的数据并增加容量,而无需任何手动操作或系统停机时间。

大多数水平扩展的数据库都是 NoSQL 并且最终是一致的。Cloud Spanner 是强一致性和关系型的。这意味着 Cloud Spanner 适用于交易数据,无论是银行、电子商务还是其他最好用强一致性关系型数据表示的工作负载。

对于数据本地性,您可以使用区域 Cloud Spanner 实例,其中数据将在区域内自动分布和复制到多个可用区,以实现高可用性。或者,您可以轻松创建一个多区域实例,该实例具有最高的 99.999% 的可用性,并且数据分布在多个区域。

Cloud Spanner 通过利用 Google 的内部基础设施、网络和 使用 GPS 同步的原子钟 实现这些壮举!通过新的 Cloud Spanner Dialect for Hibernate,您可以使用 Hibernate ORM 轻松利用 Cloud Spanner。

Cloud Spanner 与 Hibernate ORM 和 Quarkus

本博客将展示如何使用 Cloud Spanner、Hibernate ORM 和 Quarkus(在 JVM 模式下)。完整的示例在 GitHub 上

如果您还没有 Google Cloud Platform 账户,您可以在 免费注册并获得 300 美元的信用额度

创建 Cloud Spanner 实例

目前,没有官方的Cloud Spanner模拟器用于本地开发。为了试用Cloud Spanner,您需要创建一个服务器实例。完成本教程,您需要

确保已启用Cloud Spanner API

gcloud services enable spanner.googleapis.com

在美国中部1区创建一个新的单节点Cloud Spanner实例。(您可以查找其他可用的区域

gcloud spanner instances create sample --nodes=1 --config=regional-us-central1 \
  --description="Sample Database"

如果您已经有了数据库模式,您也可以通过命令行导入它。在这个例子中,我们将让Hibernate创建模式。

Cloud Spanner实例可以托管多个数据库。创建一个数据库来存储通过Hibernate生成的表。创建一个名为people的数据库

gcloud spanner databases create people --instance=sample

创建一个新的Quarkus应用程序

使用Maven创建一个新的Quarkus应用程序

mvn io.quarkus:quarkus-maven-plugin:1.0.1.Final:create \
    -DprojectGroupId=com.example \
    -DprojectArtifactId=spanner-example \
    -DclassName="com.example.PersonResource"
    -Dpath="/person"

构建应用程序

cd spanner-example
./mvnw package

将Hibernate ORM扩展添加到项目中

./mvnw quarkus:add-extension -Dextension=quarkus-hibernate-orm

这将在pom.xml中添加quarkus-hibernate-orm依赖

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-hibernate-orm</artifactId>
</dependency>

Cloud Spanner JDBC驱动Cloud Spanner方言依赖项添加到pom.xml

<!-- The Hibernate dialect for Spanner dependency -->
<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
  <version>1.0.0</version>
</dependency>

<!-- The Spanner JDBC driver dependency -->
<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner-jdbc</artifactId>
  <version>1.11.0</version>
</dependency>

配置Quarkus以使用JDBC驱动程序,以及Cloud Spanner Hibernate方言

src/main/resources/application.properties
quarkus.datasource.driver=com.google.cloud.spanner.jdbc.JdbcDriver
quarkus.datasource.url=jdbc:cloudspanner:/projects/${PROJECT_ID}/instances/sample/databases/people
quarkus.hibernate-orm.dialect=com.google.cloud.spanner.hibernate.SpannerDialect
quarkus.hibernate-orm.database.generation=drop-and-create

${PROJECT_ID}替换为您的Google Cloud项目ID。

当在本地运行应用程序(不在Google Cloud上运行)时,要连接到您的Spanner数据库,您需要设置GOOGLE_APPLICATION_CREDENTIALS环境变量。

您现在可以创建新的JPA实体。通常,实体使用自增数字ID作为代理键。但是,当使用Cloud Spanner时,这并不是最佳选择。Cloud Spanner会根据数字顺序的键自动组织和分配数据到节点,因此您不应该使用任何单调增加/减少的键(通常是自增ID)。这样的键会在Cloud Spanner中创建热点,并在数据写入和ID生成期间引入不必要的瓶颈,这最终会降低性能并限制可扩展性。Cloud Spanner建议使用UUIDv4作为主键,因此我们也将使用它为实体。

让我们创建一个Person实体

src/main/java/com/example/Person.java
package com.example;

import java.util.UUID;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Person {
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Type(type = "uuid-char")
  @Id
  private UUID id;

  private String name;

  public UUID getId() {
    return id;
  }

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

  public String getName() {
    return name;
  }

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

实体创建后,其余就很简单了!只需使用JPA实体管理器对Cloud Spanner执行CRUD操作!创建一个JAX-RS REST资源以使用JPA实体管理器保存新条目

src/main/java/com/example/PersonResource.java
package com.example;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/person")
public class PersonResource {
    @Inject EntityManager entityManager;

    @POST
    @Transactional
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Person create(Person person) {
        entityManager.persist(person);
        return person;
    }

}

请注意,create方法被标注为@Transactional。这是因为没有自动提交,写入必须参与事务。Cloud Spanner是完全事务性的,Cloud Spanner JDBC驱动程序公开了JTA语义。事务注解将按JPA用户期望的方式工作。

create方法还期望接收一个Person对象作为JSON有效载荷。您需要添加RestEasy JSONB扩展,以便Quarkus可以将JSON有效载荷转换为POJO

./mvnw quarkus:add-extension -Dextension=quarkus-resteasy-jsonb

以开发模式运行此应用程序

./mvnw quarkus:dev

一旦应用程序启动并运行(以Quarkus的超级原子速度运行!),使用curl向应用程序发送JSON有效载荷

curl -XPOST -H"Content-type: application/json" -d'{"name": "Ray"}' \
  https://127.0.0.1:8080/person

为了验证数据是否已写入Cloud Spanner,您可以导航到Google Cloud Platform控制台,浏览到Cloud Spanner数据库实例,并查看表中的行。

使用Hibernate ORM Panache

Hibernate ORM Panache 是一种非常简单的方式,用于创建 DAO (数据访问对象) 来封装比 EntityManager 提供的更复杂的查询和操作。你可以使用 Panache 与 Cloud Spanner 一起使用,因为它在后台简单构建相同的 Hibernate 查询。因此,你可以使用 Panache 与 Cloud Spanner Dialect 支持的任何 Hibernate ORM 功能。

首先,将 Hibernate ORM Panache 扩展添加到你的 Quarkus 应用程序中

./mvnw quarkus:add-extension -Dextension=quarkus-hibernate-orm-panache

然后,创建名为 PersonRepository 的 DAO 存储库

src/main/java/com/example/PersonRepository.java
package com.example;

import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import java.util.UUID;
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class PersonRepository implements PanacheRepositoryBase<Person, UUID> {
  public Person findByName(String name){
       return find("name", name).firstResult();
   }
}

PanacheRepositoryBase 期望 2 个泛型类型参数。第一个参数是实体类型,即 Person。第二个参数是主键类型,我们使用 UUID

现在你可以注入存储库以执行 CRUD 操作,你还可以在存储库类中实现额外的操作(如 findByName)。

PersonResource 类中,添加一个新的按名称查找的 REST API

src/main/java/com/example/PersonResource.java
package com.example;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.persistence.EntityManager;

import java.util.UUID;

@Path("/person")
public class PersonResource {
    @Inject EntityManager entityManager;

    @Inject PersonRepository personRepository;

    @GET
    @Path("/")
    @Produces(MediaType.APPLICATION_JSON)
    public Person getByName(@QueryParam("name") String name) {
      return personRepository.findByName(name);
    }

    @GET
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Person getById(@PathParam("id") String id) {
      return personRepository.findById(UUID.fromString(id));
    }

    @POST
    @Transactional
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Person create(Person person) {
      Person p = new Person();
      p.setName(person.getName());
      entityManager.persist(p);
      return p;
    }
}

额外奖励 - 部署到 Google App Engine!

你可以在任何地方使用 Cloud Spanner - 无论你的应用程序是在本地、Google Cloud Platform 还是其他云上。如果你想在这个平台上运行它,你可以尝试使用新的 App Engine Standard for Java 11 运行时。这个 App Engine 运行时允许你在完全管理的无服务器平台上运行任何基于 JAR 的服务,而不需要任何专有 API。你可以通过几个命令轻松地部署 Quarkus 应用程序(以 JVM 模式)。

首先,构建 Quarkus JAR

./mvnw package

然后简单地部署它!

gcloud app deploy target/spanner-example-1.0-SNAPSHOT-runner.jar

清理

Cloud Spanner 的计费基于配置的节点和存储的数据。如果你在自己的 Google Cloud Platform 账户中遵循上述说明进行测试,请完成操作后清理并删除 Cloud Spanner 实例,以避免不必要的费用!

gcloud spanner instances delete sample

但不必担心,创建新的实例非常快,所以如果你将来需要再次测试,只需创建另一个实例即可。

如果你已部署到 App Engine,请按照 App Engine 禁用应用程序 文档完全停止你的 App Engine 应用程序。

接下来是什么?

你可以在 Google Cloud Platform 文档网站 上找到官方文档。Cloud Spanner Hibernate Dialect 工程团队非常希望得到你的反馈。如果你有任何评论、想法或发现了错误,请使用 GitHub 问题 联系我们。


回到顶部