我正在寻找使用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:
We prefer the Tomcat pooling DataSource for its performance and
concurrency,so if that is available we always choose it.
(我也从Commons DBCP being used with Spring Boot看到BasicDataSource.但对我来说这似乎不是最干净的选择,因为默认依赖项不包括Commons DBCP.而且一般来说我想知道Apache Commons在2015年是否真的可以连接到Postgres的推荐方法…另外Heroku documentation为这种场景提供了“Spring中的BasicDataSource”;我认为这是指Commons DBCP,因为我在Spring本身没有看到这样的类.)
依赖关系:
当前状态:失败并显示“未加载JDBC驱动程序,因为driverClassName属性为null”:
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(没有它失败,“当’hibernate.dialect’没有设置时,访问DialectResolutionInfo不能为空”).
我的具体问题
>那么,如何让这个工作?我正在设置spring.datasource.driverClassName,那么为什么“不加载JDBC驱动程序作为driverClassName属性为空”?
> Tomcat的DataSource的使用是否正常,或者你会推荐别的吗?
>是否必须使用specific version定义上述postgresql依赖项? (没有这个,我得到“找不到合适的驱动程序”错误.)
>是否有更简单的方法来完成所有这些(同时坚持使用Java代码和/或属性;请不要使用XML)?
> As jny pointed out,我需要明确设置JDBC驱动程序:
> dataSource.setDriverClassName(“org.postgresql.Driver”);
>(原因是我正在定义一个自定义数据源,覆盖Spring的默认值,导致我的spring.datasource.driverClassName属性没有效果.据我所知,由于dynamic nature of Heroku’s DATABASE_URL
,我需要自定义数据源来实现它工作.)
>此后连接工作,但不稳定;我开始得到org.postgresql.util.PsqlException:此连接已关闭.应用程序运行一段时间后.有点令人惊讶的solution(基于this answer)是在Tomcat DataSource上启用某些测试,例如testOnBorrow
:
> dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(真);
dataSource.setTestOnReturn(真);
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: {}",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
现在,我遇到的两个问题可能都是特定于DataSource from Tomcat(org.apache.tomcat.jdbc.pool). Apparently BasicDataSource(Commons DBCP)具有更明智的默认值.但正如问题中提到的,我宁愿使用Spring Boot默认配置的东西,特别是参考指南中的strongly endorsed.
我愿意接受竞争/更简单/更好的解决方案,所以请随意发帖,特别是如果你能在问题的最后解决疑惑2-4!
而是使用JDBC_DATABASE_ *变量
更新:请注意,使用JDBC_DATABASE_ *比上面简单得多,如pointed out in this answer.很长一段时间我的印象是DATABASE_URL应该是首选,但现在我不再那么肯定了.