将 Spring 数据 JPA 与 GCP Spanner 集成

Integrating Spring Data JPA with GCP Spanner

我正在尝试将现有的 spring 引导应用程序从使用 Postgres 迁移到 GCP Spanner。

我正在使用以下 Cloud Spanner JDBC 驱动程序和 Hibernate 方言:

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
    <version>0.1.0</version>
</dependency>
<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-spanner-jdbc</artifactId>
    <version>1.7.0</version>
</dependency>

我还配置了以下属性:

spring.datasource.url=jdbc:cloudspanner:/projects/YOUR_PROJECT_ID/instances/demo/databases/demo
spring.datasource.driver-class-name=com.google.cloud.spanner.jdbc.JdbcDriver
spring.jpa.database-platform=com.google.cloud.spanner.hibernate.SpannerDialect

然而,当 运行 应用程序在尝试获取池连接时挂起:

o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2729 ms
com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Driver does not support get/set network timeout for connections. (Network timeout is not supported)

HikariCP 支持 GCP Spanner 吗?

它应该开箱即用,我看不出您提供的设置有任何直接错误。所以我的猜测是这里有一些额外的东西导致了问题(依赖性、其他设置等)。您使用的是较旧版本的 Hibernate 方言和 JDBC 驱动程序,但这应该不是问题所在。

一个可能的问题是您的系统没有设置默认的 Google 云凭据。我注意到您的 JDBC URL 不包含任何凭据,这意味着它将回退到环境的默认值。如果找不到任何错误,我会期待一个错误,但这可能是问题的一个可能原因。

我用 Spring 引导创建了一个非常简单的测试项目并进行了试用,它确实有效。您能否也尝试使用这个简单的测试设置并尝试从那里添加以找出问题所在?或者以其他方式提供有关您可能包含在项目中的任何其他依赖项的更多详细信息?

我的有效设置:

pom.xml

<project xmlns="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">
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.google.cloud</groupId>
  <artifactId>spanner-example-runner</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>spanner-example-runner</name>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-spanner-jdbc</artifactId>
      <version>1.15.0</version>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
      <version>1.1.0</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

实体:

package com.google.cloud.example;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "SINGERS")
public class Singer {

  @Id private Long singerId;

  private String firstName;

  private String lastName;

  public Singer() {}

  public Singer(long singerId, String firstName, String lastName) {
    this.singerId = singerId;
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public Long getSingerId() {
    return singerId;
  }

  public void setSingerId(Long singerId) {
    this.singerId = singerId;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

存储库:

package com.google.cloud.example;

import java.util.List;
import org.springframework.data.repository.CrudRepository;

public interface SingerRepository extends CrudRepository<Singer, Long> {

  List<Singer> findByLastName(String lastName);

  Singer findById(long id);
}

Spring 引导应用程序:

package com.google.cloud.example;

import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class AccessingDataJpaApplication {
  private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(AccessingDataJpaApplication.class, args);
  }

  @Bean
  public CommandLineRunner demo(SingerRepository repository) {
    return (args) -> {
      // save a few singers
      repository.save(new Singer(10, "Jack", "Bauer"));
      repository.save(new Singer(20, "Chloe", "O'Brian"));
      repository.save(new Singer(30, "Kim", "Bauer"));
      repository.save(new Singer(40, "David", "Palmer"));
      repository.save(new Singer(50, "Michelle", "Dessler"));

      // fetch all singers
      log.info("Customers found with findAll():");
      log.info("-------------------------------");
      for (Singer customer : repository.findAll()) {
        log.info(customer.toString());
      }
      log.info("");

      // fetch an individual singer by ID
      Singer customer = repository.findById(1L);
      log.info("Customer found with findById(1L):");
      log.info("--------------------------------");
      log.info(customer.toString());
      log.info("");

      // fetch singers by last name
      log.info("Customer found with findByLastName('Bauer'):");
      log.info("--------------------------------------------");
      repository.findByLastName("Bauer").forEach(bauer -> {
        log.info(bauer.toString());
      });
      log.info("");
      // Cleanup
      repository.deleteById(10L);
      repository.deleteById(20L);
      repository.deleteById(30L);
      repository.deleteById(40L);
      repository.deleteById(50L);
    };
  }

  @Bean
  public PhysicalNamingStrategy physical() {
      return new PhysicalNamingStrategyStandardImpl();
  }

  @Bean
  public ImplicitNamingStrategy implicit() {
      return new ImplicitNamingStrategyLegacyJpaImpl();
  }
}

application.properties:

spring.datasource.url=jdbc:cloudspanner:/projects/my-project-id/instances/some-instance/databases/some-db?credentials=/path/to/credentials.json
spring.datasource.driver-class-name=com.google.cloud.spanner.jdbc.JdbcDriver
spring.jpa.database-platform=com.google.cloud.spanner.hibernate.SpannerDialect