我正在尝试使用gradle,java,spring boot,postgres和hikari创建一个heroku应用程序。
我正在关注文档:
Getting Started with Gradle on Heroku
Connecting to Relational Databases on Heroku with Java
我在没有修改的情况下使用上面第一个链接中的示例git repo:
https://github.com/heroku/gradle-getting-started.git
当我将应用程序部署到heroku时,它正常运行。但当我尝试在本地运行它时,它失败了Failed to get driver instance for jdbcUrl=jdbc:postgresql://username@localhost
。
我试过用设置,配置或环境变量来思考我能想到的每一种方式,但到目前为止我无法让它工作。
我知道我做错了什么?我是否需要在本地部署JDBC?
如果我没有设置环境变量JDBC_DATABASE_URL
,那么这个命令......
./gradlew clean build
......成功完成。如果我这样做......
heroku local web
...服务器成功启动。当我浏览到下面的端点(它击中数据库)...
http://localhost:5000/db
...它显示此错误:
dataSource or dataSourceClassName or jdbcUrl is required.
相比之下,远程heroku服务器上的相同端点......
https://my_project_name.herokuapp.com/db
...显示此输出:
Database Output
Read from DB: 2019-04-27 19:48:38.764037
Read from DB: 2019-04-27 19:58:30.007374
如果我做...
export JDBC_DATABASE_URL=jdbc:postgresql://username@localhost
...然后gradlew clean build
与Failed to get driver instance for jdbcUrl=jdbc:postgresql://username@localhost
失败。这是完整的输出:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:118)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:175)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:157)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'herokuApplication': Unsatisfied dependency expressed through field 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in com.example.heroku.HerokuApplication: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Circular reference involving containing bean 'herokuApplication' - consider declaring the factory method as static for independence from its containing instance. Factory method 'dataSource' threw exception; nested exception is java.lang.RuntimeException: Failed to get driver instance for jdbcUrl=jdbc:postgresql://username@localhost
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1378)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:575)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
... 49 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in com.example.heroku.HerokuApplication: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Circular reference involving containing bean 'herokuApplication' - consider declaring the factory method as static for independence from its containing instance. Factory method 'dataSource' threw exception; nested exception is java.lang.RuntimeException: Failed to get driver instance for jdbcUrl=jdbc:postgresql://username@localhost
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:456)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1288)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1244)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1164)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
... 67 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Circular reference involving containing bean 'herokuApplication' - consider declaring the factory method as static for independence from its containing instance. Factory method 'dataSource' threw exception; nested exception is java.lang.RuntimeException: Failed to get driver instance for jdbcUrl=jdbc:postgresql://username@localhost
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622)
... 80 more
Caused by: java.lang.RuntimeException: Failed to get driver instance for jdbcUrl=jdbc:postgresql://username@localhost
at com.zaxxer.hikari.util.DriverDataSource.<init>(DriverDataSource.java:112)
at com.zaxxer.hikari.pool.PoolBase.initializeDataSource(PoolBase.java:336)
at com.zaxxer.hikari.pool.PoolBase.<init>(PoolBase.java:109)
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:108)
at com.zaxxer.hikari.HikariDataSource.<init>(HikariDataSource.java:81)
at com.example.heroku.HerokuApplication.dataSource(HerokuApplication.java:84)
at com.example.heroku.HerokuApplication$$EnhancerBySpringCGLIB$$f42e4dc5.CGLIB$dataSource$2(<generated>)
at com.example.heroku.HerokuApplication$$EnhancerBySpringCGLIB$$f42e4dc5$$FastClassBySpringCGLIB$$aca4e89.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
at com.example.heroku.HerokuApplication$$EnhancerBySpringCGLIB$$f42e4dc5.dataSource(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 81 more
Caused by: java.sql.SQLException: No suitable driver
at java.sql/java.sql.DriverManager.getDriver(DriverManager.java:298)
at com.zaxxer.hikari.util.DriverDataSource.<init>(DriverDataSource.java:104)
... 96 more
我正在使用上面提到的示例git repo中的所有代码和配置文件,而不进行修改。
这里是文件src/main/java/com/example/heroku/HerokuApplication.java
的内容:
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.heroku;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Map;
@Controller
@SpringBootApplication
public class HerokuApplication {
@Value("${spring.datasource.url}")
private String dbUrl;
@Autowired
private DataSource dataSource;
public static void main(String[] args) throws Exception {
SpringApplication.run(HerokuApplication.class, args);
}
@RequestMapping("/")
String index() {
return "index";
}
@RequestMapping("/db")
String db(Map<String, Object> model) {
try (Connection connection = dataSource.getConnection()) {
Statement stmt = connection.createStatement();
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ticks (tick timestamp)");
stmt.executeUpdate("INSERT INTO ticks VALUES (now())");
ResultSet rs = stmt.executeQuery("SELECT tick FROM ticks");
ArrayList<String> output = new ArrayList<String>();
while (rs.next()) {
output.add("Read from DB: " + rs.getTimestamp("tick"));
}
model.put("records", output);
return "db";
} catch (Exception e) {
model.put("message", e.getMessage());
return "error";
}
}
@Bean
public DataSource dataSource() throws SQLException {
if (dbUrl == null || dbUrl.isEmpty()) {
return new HikariDataSource();
} else {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(dbUrl);
return new HikariDataSource(config);
}
}
}
这里是文件src/main/resources/application.properties
的内容:
spring.datasource.url: ${JDBC_DATABASE_URL:}
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=10
#spring.datasource.driverClassName=org.postgresql.Driver
spring.thymeleaf.mode=HTML
logging.level.org.springframework=INFO
spring.profiles.active=production
server.port=${PORT:5000}
这里是文件build.gradle
的内容:
buildscript {
ext {
springBootVersion = '2.1.2.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '1.0'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
runtimeOnly 'org.postgresql:postgresql'
runtimeOnly 'org.webjars:jquery:3.3.1-1'
runtimeOnly 'org.webjars:jquery-ui:1.12.1'
runtimeOnly 'org.webjars:bootstrap:4.1.3'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
在疯狂的故障排除日之后,我开始工作了。就像我所知,命令gradlew build
和heroku local web
只有在数据库用户有密码的情况下才会感到高兴。
默认情况下,当您在ubuntu上安装postgres时,最终会得到一个与您的Linux帐户同名的postgres帐户,并且没有密码。
在我上面的OP中描述的原始尝试中,我在环境变量DATABASE_URL
和JDBC_DATABASE_URL
的值的格式化方面存在一些问题。一旦我修复了,我开始得到错误The server requested password-based authentication, but no password was provided
,我通过设置密码修复。
这是我遵循的步骤:
1)在postgres中设置密码:
$psql
#alter user username with password 'password';
#\q
2)设置环境变量:
$export DATABASE_URL=postgres://username:password@localhost:5432/username
$export JDBC_DATABASE_URL=jdbc:postgresql://localhost:5432/username?user=username\&password=password
(我正在指定一个与我的用户名同名的数据库)。 (第二个export
命令包含&
字符,我必须用反斜杠逃脱)。
3)建立项目:
$./gradlew clean build
(你可能通常不需要clean
部分,但至少今天我发现clean
部分需要重置一些状态)。
4)启动heroku本地:
$heroku local web
5)浏览到触发对数据库的调用的端点:
http://localhost:5000/db
现在显示希望的输出:
Database Output
Read from DB: 2019-04-27 19:48:38.764037
Read from DB: 2019-04-27 19:58:30.007374
噢!