首页 > 解决方案 > 将 Spring Data JPA 与 GCP Spanner 集成

问题描述

我正在尝试将现有的 Spring Boot 应用程序从使用 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 ?

标签: hibernatespring-bootjdbchikaricpgoogle-cloud-spanner

解决方案


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

一个可能的问题是您的系统未设置默认的 Google Cloud 凭据。我注意到您的 JDBC URL 不包含任何凭据,这意味着它将回退到环境的默认值。如果找不到任何错误,我预计会出现错误,但这可能是导致问题的原因。

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

我的设置有效:

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);
}

春季启动应用程序:

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();
  }
}

应用程序属性:

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

推荐阅读