我正在寻找最简单的,使用JPA / Hibernate在 Spring Boot 应用中连接到Heroku Postgres的最干净方式。
我没有在Heroku或Spring Boot文档中看到这个组合的完整示例,所以我想在Stack Overflow上记录这个。
我想尝试这样的事情:
@Configuration
public class DataSourceConfig {
Logger log = LoggerFactory.getLogger(getClass());
@Bean
@Profile("postgres")
public DataSource postgresDataSource() {
String databaseUrl = System.getenv("DATABASE_URL")
log.info("Initializing PostgreSQL database: {}", databaseUrl);
URI dbUri;
try {
dbUri = new URI(databaseUrl);
}
catch (URISyntaxException e) {
log.error(String.format("Invalid DATABASE_URL: %s", databaseUrl), e);
return null;
}
String username = dbUri.getUserInfo().split(":")[0];
String password = dbUri.getUserInfo().split(":")[1];
String dbUrl = "jdbc:postgresql://" + dbUri.getHost() + ':'
+ dbUri.getPort() + dbUri.getPath();
// fully-qualified class name to distuinguish from javax.sql.DataSource
org.apache.tomcat.jdbc.pool.DataSource dataSource
= new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setUrl(dbUrl);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
我正在使用Profiles,这似乎与我想要的完全匹配:在Heroku SPRING_PROFILES_ACTIVE
设置为postgres
,而在本地开发spring.profiles.active
是h2
使用H2内存数据库(此处省略其配置)。这种方法似乎运作良好。
在application-postgres.properties
(profile-specific properties)中:
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.driverClassName=org.postgresql.Driver
来自Tomcat的 DataSource
似乎是一个不错的选择,因为默认依赖包含它,因为Spring Boot reference guide says:
我们更喜欢Tomcat池化DataSource的性能和 并发,所以如果可以,我们总是选择它。
(我也看到了来自Commons DBCP being used with Spring Boot的BasicDataSource
。但对我来说,这似乎不是最干净的选择,因为默认依赖项不包括Commons DBCP。总的来说,我想知道2015年的Apache Commons是否真的,是推荐的连接Postgres的方式......另外Heroku documentation提供"在Spring" BasicDataSource
对于这种情况;我认为这是指Commons DBCP,因为我在Spring中看不到这样的类。)
依赖关系:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1205-jdbc42</version>
</dependency>
当前状态:使用&#34失败;未加载JDBC驱动程序,因为driverClassName属性为null&#34;:
eConfig$$EnhancerBySpringCGLIB$$463388c1 : Initializing PostgreSQL database: postgres:[...]
j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
[...]
o.a.tomcat.jdbc.pool.PooledConnection : Not loading a JDBC driver as driverClassName property is null.
o.a.tomcat.jdbc.pool.PooledConnection : Not loading a JDBC driver as driverClassName property is null.
[...]
org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
在日志中,我看到我的postgresDataSource
被调用就好了
PostgreSQLDialect 正在使用(没有这个&#34;当&#39; hibernate.dialect&#39;未设置&#34;)时,对DialectResolutionInfo的访问不能为空。
我的具体问题
spring.datasource.driverClassName
,为什么&#34;没有加载JDBC驱动程序作为driverClassName属性为null&#34;?DataSource
是好还是你会推荐其他东西? postgresql
依赖项? (我得到了#34;找不到合适的驱动程序&#34;没有这个错误。)答案 0 :(得分:8)
为了使数据库连接正常工作(以稳定的方式),我在问题中描述的设置中缺少两件事:
dataSource.setDriverClassName("org.postgresql.Driver");
spring.datasource.driverClassName
属性无效。据我所知,由于{ {3}},我需要自定义数据源才能使其正常工作。)org.postgresql.util.PSQLException: This connection has been closed.
。有点令人惊讶的dynamic nature of Heroku's DATABASE_URL
(基于solution)是为了在Tomcat DataSource上启用某些测试,例如this answer:
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);
dataSource.setTestOnReturn(true);
dataSource.setValidationQuery("SELECT 1");
所以,我的DataSourceConfig的固定版本:
@Configuration
public class DataSourceConfig {
Logger log = LoggerFactory.getLogger(getClass());
@Bean
@Profile("postgres")
public DataSource postgresDataSource() {
String databaseUrl = System.getenv("DATABASE_URL")
log.info("Initializing PostgreSQL database: {}", databaseUrl);
URI dbUri;
try {
dbUri = new URI(databaseUrl);
}
catch (URISyntaxException e) {
log.error(String.format("Invalid DATABASE_URL: %s", databaseUrl), e);
return null;
}
String username = dbUri.getUserInfo().split(":")[0];
String password = dbUri.getUserInfo().split(":")[1];
String dbUrl = "jdbc:postgresql://" + dbUri.getHost() + ':'
+ dbUri.getPort() + dbUri.getPath();
org.apache.tomcat.jdbc.pool.DataSource dataSource
= new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl(dbUrl);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);
dataSource.setTestOnReturn(true);
dataSource.setValidationQuery("SELECT 1");
return dataSource;
}
}
只有application-postgres.properties
:
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
现在,我遇到的两个问题都可能是testOnBorrow
(org.apache.tomcat.jdbc.pool
)所特有的。 DataSource from Tomcat BasicDataSource(Commons DBCP)具有更明智的默认值。但正如问题中提到的,我宁愿使用默认情况下Spring Boot附带的东西,尤其是参考指南中的Apparently。
我愿意接受竞争/更简单/更好的解决方案,所以请随意发帖,特别是如果你能在问题的最后解决疑惑2-4!
JDBC_DATABASE_*
变量更新:请注意,使用JDBC_DATABASE_*
比strongly endorsed简单得多。很长一段时间我都认为应该首选DATABASE_URL
,但现在我不再那么肯定了。
答案 1 :(得分:5)
最简单的Spring Boot / Heroku / Hibernate配置
除了始终存在的DATABASE_URL
之外,Heroku在运行时创建了3个环境变量。他们是:
JDBC_DATABASE_URL
JDBC_DATABASE_USERNAME
JDBC_DATABASE_PASSWORD
您可能知道,如果在spring.datasource.*
文件中找到application.properties
属性,Spring Boot将自动配置您的数据库。这是我的application.properties
spring.datasource.url=${JDBC_DATABASE_URL}
spring.datasource.username=${JDBC_DATABASE_USERNAME}
spring.datasource.password=${JDBC_DATABASE_PASSWORD}
spring.jpa.show-sql=false
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
Hibernate / Postgres依赖
在我的情况下,我使用Hibernate(spring-boot-starter-jpa
与PostgreSQL捆绑在一起,所以我需要build.gradle
中的正确依赖:
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile('org.postgresql:postgresql:9.4.1212')
}
答案 2 :(得分:1)
我阅读了所有答案,但没有找到Jonik正在寻找的内容:
我正在寻找最简单,最干净的Heroku连接方式 使用JPA / Hibernate的Spring Boot应用程序中的Postgres
大多数人想要使用Spring Boot&amp; amp; Heroku包括一个用于测试的本地H2内存数据库。快速开发周期 - 以及Heroku Postgres database用于Heroku上的分段和生产。
让我们一步一步看看我们必须做些什么。我有一个示例项目,为Postgres提供完全可用的Heroku部署和配置 - 只是为了完整性,如果你想自己测试它:github.com/jonashackt/spring-boot-vuejs。
我们需要以下依赖性:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- In-Memory database used for local development & testing -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- Switch back from Spring Boot 2.x standard HikariCP to Tomcat JDBC,
configured later in Heroku (see https://stackoverflow.com/a/49970142/4964553) -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
<!-- PostgreSQL used in Staging and Production environment, e.g. on Heroku -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.2</version>
</dependency>
这里有一个棘手的问题是使用tomcat-jdbc
,但我们将在一秒钟内介绍它。
在Heroku环境中,变量名为Config Vars
。 您听说没错,我们所要做的就是配置环境变量!我们只需要正确的。因此请转到https://data.heroku.com/(我假设已经为您的Heroku应用程序配置了Postgres数据库,这是默认行为)。
现在点击应用程序的相应Datastore
,然后切换到Settings
标签。然后点击View Credentials...
,它应该看起来像这样:
现在打开一个新的浏览器标签,然后转到Heroku应用程序的Settings
标签。单击Reveal Config Vars
并创建以下环境变量:
SPRING_DATASOURCE_URL
= jdbc :postgres ql : YourPostgresHerokuHostNameHere :5432 / YourPostgresHerokuDatabaseNameHere (介意领先jdbc:
和ql
添加到postgres
!)SPRING_DATASOURCE_USERNAME
= YourPostgresHerokuUserNameHere SPRING_DATASOURCE_PASSWORD
= YourPostgresHerokuPasswordHere SPRING_DATASOURCE_DRIVER-CLASS-NAME
= org.postgresql.Driver
(这并不总是需要since Spring Boot can deduce it for most databases from the url,只是为了完整性)SPRING_JPA_DATABASE-PLATFORM
= org.hibernate.dialect.PostgreSQLDialect
SPRING_DATASOURCE_TYPE
= org.apache.tomcat.jdbc.pool.DataSource
SPRING_JPA_HIBERNATE_DDL-AUTO
= update
(这将根据您的JPA实体自动创建表格,这非常棒 - 因为您不需要跨越CREATE
SQL语句或DDL文件)在Heroku中,这应该是这样的:
现在就是你所要做的一切!每次更改配置变量时都会重启你的Heroku应用程序 - 所以你的应用程序现在应该在本地运行H2,并且应该准备好与PostgreSQL连接时部署在Heroku上。
您可能已经注意到,我们将tomcat-jdbc
依赖项添加到我们的pom.xml中,并将SPRING_DATASOURCE_TYPE=org.apache.tomcat.jdbc.pool.DataSource
配置为环境变量。只有a slight hint in the docs about this说
您可以完全绕过该算法并指定连接 通过设置spring.datasource.type属性来使用的池。这是 如果您在Tomcat容器中运行应用程序,这一点尤为重要......
有几个原因我切换回Tomcat汇集DataSource而不是使用Spring Boot 2.x标准HikariCP。正如我already explained here,如果你没有指定spring.datasource.url
,Spring会尝试自动装入嵌入式内存H2数据库,而不是我们的PostgreSQL数据库。 Hikari的问题是,它只支持spring.datasource.jdbc-url
。
其次,如果我尝试使用Hikari所示的Heroku配置(因此省略SPRING_DATASOURCE_TYPE
并将SPRING_DATASOURCE_URL
更改为SPRING_DATASOURCE_JDBC-URL
),我会遇到以下异常:
Caused by: java.lang.RuntimeException: Driver org.postgresql.Driver claims to not accept jdbcUrl, jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
所以我没有让Spring Boot 2.x在Heroku&amp; amp;使用HikariCP进行Postgres,但使用Tomcat JDBC - 我也不想制定包含前面描述的本地H2数据库的开发过程。记住:我们正在寻找使用JPA / Hibernate在Spring Boot应用程序中连接到Heroku Postgres的最简单,最干净的方法!
答案 3 :(得分:0)
尝试使用JDBC_DATABASE_URL作为spring.datasource.url
,而不是解析DATABASE_URL。
建议解析DATABASE_URL,但是如果你不能使它工作,那么JDBC_DATABASE_URL应该没问题。
答案 4 :(得分:0)
这是使用Heroku提供的示例Java应用程序Google搜索Postgres问题的最佳答案。
这些是我为使其发挥作用而采取的步骤(Win 7)。
1。)生产服务器application.properties文件将包含系统环境(确保已提交此文件)
spring.datasource.url=${JDBC_DATABASE_URL}
spring.datasource.username=${JDBC_DATABASE_USERNAME}
spring.datasource.password=${JDBC_DATABASE_PASSWORD}
2。)现在做git update-index --assume-unchanged .\src\main\resources\application.properties
3.。)将本地application.properties更改为硬编码。您可以通过运行heroku run env
spring.datasource.url=jdbc://..
spring.datasource.username=XYZ
spring.datasource.password=ABC
这是我必须获取我的应用程序的本地副本才能工作的内容。如果有人找到更好的方法请分享!
答案 5 :(得分:0)
@Configuration
@Component
public class HerokuConfigCloud {
private static final Logger logger =
LoggerFactory.getLogger(HerokuConfigCloud .class);
@Bean()
//@Primary this annotation to be used if more than one DB Config was used. In that case,
// using @Primary would give precedence to a the particular "primary" config class
@Profile("heroku")
public DataSource dataSource(
@Value("${spring.datasource.driverClassName}") final String driverClass,
@Value("${spring.datasource.url}") final String jdbcUrl,
@Value("${spring.datasource.username}") final String username,
@Value("${spring.datasource.password}") final String password
) throws URISyntaxException {
return DataSourceBuilder
.create()
.username(username)
.password(password)
.url(url)
.driverClassName(driverClass)
.build();
}
}
答案 6 :(得分:0)
我建立了一个库来简化此过程:https://github.com/vic-cw/heroku-postgres-helper
如果您需要同时在构建脚本和应用程序逻辑中访问数据库,这将更加有用。了解为什么here。
build.gradle:
// If using connection string in build script:
buildscript {
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
classpath 'com.github.vic-cw:heroku-postgres-helper:0.1.0'
}
}
import com.github.viccw.herokupostgreshelper.HerokuPostgresHelper;
// Use connection string in build script:
flyway {
url = HerokuPostgresHelper.getDatabaseJdbcConnectionString()
driver = 'org.postgresql.Driver'
}
// If using connection string inside application logic:
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
compile group: 'com.github.vic-cw', name: 'heroku-postgres-helper', version: '0.1.0'
}
Java应用程序代码:
import com.github.viccw.herokupostgreshelper.HerokuPostgresHelper;
...
String databaseConnectionString = HerokuPostgresHelper.getDatabaseJdbcConnectionString();