将 Spring Data JPA 与 GCP Spanner 集成

Posted

技术标签:

【中文标题】将 Spring Data JPA 与 GCP Spanner 集成【英文标题】:Integrating Spring Data JPA with GCP Spanner 【发布时间】:2020-07-05 18:04:15 【问题描述】:

我正在尝试将现有的 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 吗?

【问题讨论】:

【参考方案1】:

它应该开箱即用,在您提供的设置中我看不出有任何直接错误。所以我的猜测是这里有一些额外的东西导致了问题(依赖性,其他设置等)。您使用的是较旧版本的 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);

Spring Boot 应用程序:

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

【讨论】:

感谢您的回复。我没有在 JDBC url 中包含凭据,因为我已经使用 gcloud:cloud.google.com/spanner/docs/getting-started/set-up 进行了身份验证。我通过备用 gcp spring 数据云库连接没有问题。 这应该是一种完全有效的身份验证方式,所以我认为这不是问题所在。您是否仅通过一个简单的应用程序就可以运行它?您能否以其他方式分享有关您可能包含的任何其他依赖项的更多信息?您是否也可以尝试升级您正在使用的 JDBC 驱动程序和 Hibernate 方言的版本? 好的,谢谢。一定是版本不正确。现在可以使用了。 好答案。你能推荐我的spring应用程序在本地环境中使用的任何扳手模拟器吗

以上是关于将 Spring Data JPA 与 GCP Spanner 集成的主要内容,如果未能解决你的问题,请参考以下文章

无法将 Spring Data MongoDB + Spring Data JPA 与 Spring Boot 一起使用

将 spring data jpa 与 ninja java 一起使用

将 Hibernate Sessions 功能与 Spring Data JPA 一起使用

如何将多个日期之间的搜索与 Spring Data JPA 的 CrudRepository 结合起来?

使用 Spring Data JPA 将 sql 查询的结果映射到 pojo

spring-data-jpa 和 spring-boot-starter-data-jpa 的区别