Подключение к Heroku Postgres из Spring Boot

я ищу простой, самый чистый способ подключения к Heroku Postgres на Весна Загрузки приложение с помощью JPA / Hibernate.

я не вижу хорошего, полного примера для этого комбо в документации Heroku или Spring Boot, поэтому я хотел бы документировать это при переполнении стека.

я пытаюсь пойти с чем-то вроде этого:

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

}

я использую профили, который кажется хорошим матч для того, что я хочу: на Heroku SPRING_PROFILES_ACTIVE установлено значение postgres, в то время как в местном развитии spring.profiles.active is h2 использовать базу данных в памяти H2 (конфигурация которой здесь опущена). Этот подход, похоже, работает нормально.

на application-postgres.properties (профильные свойства):

spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.driverClassName=org.postgresql.Driver

DataSource из Tomcat показался хорошим вариантом, так как зависимости по умолчанию включают его, и потому что справочник по весенней загрузке говорит:

мы предпочитаем источник данных пула Tomcat для его производительности и параллелизм, поэтому, если это доступно, мы всегда выбираем его.

(я также вижу BasicDataSource из общего DBCP используется с Spring Boot. Но мне это не кажется самым чистым местом в качестве зависимостей по умолчанию не включить Dbcp Commons. И вообще мне интересно, может ли Apache Commons действительно, в 2015, порекомендованный путь к подключиться к Postgres... Также в Heroku документации предложения "BasicDataSource весной " для такого сценария; я предполагаю, что это относится к Dbcp Commons, Так как я не вижу такого класса весной.)

зависимости:

<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>

текущее состояние: сбой с "не загружать драйвер JDBC как свойство driverClassName равно null":

eConfig$$EnhancerBySpringCGLIB$3388c1 : 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 и в использовании (без этого он не работал с "Доступ к DialectResolutionInfo не может быть нулевым, когда" спящий режим.диалект "не установлен").

мои конкретные вопросы

  1. Ну, как заставить это работать? Я!--17-- > am задание spring.datasource.driverClassName, так почему же "не загружать драйвер JDBC как свойство driverClassName равно null"?
  2. использование продукта Tomcat DataSource хорошо или вы порекомендовали бы что-то еще?
  3. это обязательно определить postgresql зависимость, как указано выше с конкретной версии? (Я получал ошибку "нет подходящего драйвера" без этого.)
  4. есть ли более простой способ сделать все это (придерживаясь кода Java и/или свойств; нет XML, пожалуйста)?

6 ответов


чтобы соединение с базой данных работало (стабильным образом), в настройке, которую я описал в вопросе, отсутствовали две вещи:

  • как отметил jny мне нужно установить драйвер JDBC явно:
    • dataSource.setDriverClassName("org.postgresql.Driver");
    • (причина этого в том, что я определяю пользовательский источник данных, переопределяя значение по умолчанию Spring, вызывая my spring.datasource.driverClassName свойство не имеет никакого эффекта. И, насколько я понимаю, из-за динамическая природа Heroku в DATABASE_URL, мне нужен пользовательский источник данных, чтобы он работал.)
  • после этого соединение работало, но оно не было стабильным; я начал получать org.postgresql.util.PSQLException: This connection has been closed. после того, как приложение было запущено некоторое время. Довольно-таки неожиданный решение (исходя из ответ), чтобы включить определенные тесты, такие как testOnBorrow на Tomcat Источник:
    • 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

теперь обе проблемы, которые у меня были, могут быть специфическими для источник данных от Tomcat (org.apache.tomcat.jdbc.pool). видимо BasicDataSource (Commons DBCP) имеет более разумные значения по умолчанию. Но, как упоминалось в вопросе, я скорее использовал то, что приходит с весной Загрузки по умолчанию, тем более что это решительно поддерживает в справочнике.

Я открыт для конкурирующих / более простых / лучших решений, поэтому не стесняйтесь публиковать, особенно если вы можете обратиться к сомнениям 2-4 в конце вопроса!

используя JDBC_DATABASE_* вместо переменных

Update: обратите внимание, что с помощью JDBC_DATABASE_* гораздо проще, чем выше, как указал в этом ответе. Долгое время я был под впечатление, что DATABASE_URL должно быть предпочтительнее, но в настоящее время я больше не уверен.


Простая Весенняя Загрузка / Heroku / Конфигурация Гибернации

помимо DATABASE_URL, который всегда есть, Heroku создает 3 переменные среды во время выполнения. Они:

JDBC_DATABASE_URL
JDBC_DATABASE_USERNAME
JDBC_DATABASE_PASSWORD

как вы знаете, Spring Boot автоматически настроит вашу базу данных, если она найдет spring.datasource.* свойства . Вот пример моего приложения.свойства

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

Спящий Режим / 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')
}

самый простой самый чистый способ для Spring Boot 2.x с Heroku & Postgres

я прочитал все ответы, но не нашел, что Jonik ищут:

я ищу самый простой и чистый способ подключения к Heroku Postgres в весеннем загрузочном приложении с использованием JPA / Hibernate

процесс разработки большинство людей хотят использовать с Spring Boot & Heroku включает локальную базу данных H2 в памяти для тестирования и быстрого циклы развития - и база данных Heroku Postgres для постановки и производства на Heroku.

  • во - первых-вам не нужно использовать профили весны для этого!
  • во-вторых: вам не нужно писать/изменять любой код!

давайте посмотрим, что мы должны делать шаг за шагом. У меня есть пример проекта, который обеспечивает полностью рабочее развертывание Heroku и конфигурацию для Postgres-только ради полноты, если вы хотите проверить это сами:github.com/jonashackt/spring-boot-vuejs.

пом.в XML

нам нужны следующие depencencies:

    <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

в среде Heroku переменные называются Config Vars. вы правильно расслышали, все мы должны сделать это, чтобы настроить переменные среды! нам просто нужны правильные. Поэтому отправляйтесь в https://data.heroku.com/ (я предполагаю, что уже есть база данных Postgres, настроенная для вашего приложения Heroku, что является поведением по умолчанию).

Теперь нажмите на соответствующие приложения Datastore и переключиться на Settings tab. Затем нажмите на View Credentials..., который должен выглядеть примерно как это:

heroku-datastore-credentials-postgres

теперь откройте новую вкладку браузера и перейдите в свои приложения Heroku Settings tab также. Нажмите на Reveal Config Vars и создайте следующие переменные среды:

  • SPRING_DATASOURCE_URL = С JDBC: postgresql:YourPostgresHerokuHostNameHere:5432/YourPostgresHerokuDatabaseNameHere (разум ведущих jdbc: и ql дополнение к postgres!)
  • SPRING_DATASOURCE_USERNAME = YourPostgresHerokuUserNameHere
  • SPRING_DATASOURCE_PASSWORD = YourPostgresHerokuPasswordHere
  • SPRING_DATASOURCE_DRIVER-CLASS-NAME= org.postgresql.Driver (это не всегда требуется поскольку Spring Boot может вывести его для большинства баз данных из 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-spring-boot-app-postgres-config

теперь все, что вам нужно сделать! ваше приложение Heroku перезапускается каждый раз, когда вы изменяете переменную конфигурации-поэтому ваше приложение должно теперь запускать H2 локально и должно быть готово к подключению к PostgreSQL при развертывании В Heroku.

просто если вы спрашиваете: почему мы настраиваем Tomcat JDBC вместо Hikari

как вы могли заметить, мы добавили tomcat-jdbc зависимость от нашего pom.xml и настроен SPRING_DATASOURCE_TYPE=org.apache.tomcat.jdbc.pool.DataSource как переменная окружения. Theres только небольшой намек в документах об этом слова

вы можете полностью обойти этот алгоритм и определить связи бассейн для использования, установив пружину.источник.свойство тип. Это особенно важно, если вы запускаете приложение в контейнере Tomcat,...

есть несколько причин, по которым я вернулся к источнику данных пула Tomcat вместо использования Spring Boot 2.X стандартный HikariCP. Как Я!--130-->уже объяснял здесь, если вы не указать spring.datasource.url, Spring попытается автоматически подключить встроенную базу данных im-memory H2 вместо нашей PostgreSQL. И проблема с Hikari в том, что он поддерживает только spring.datasource.jdbc-url.

во-вторых, если я попробовать чтобы использовать конфигурацию Heroku, как показано для Hikari (так что оставляя SPRING_DATASOURCE_TYPE и изменение SPRING_DATASOURCE_URL to 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 & Postgres с HikariCP, но с Tomcat JDBC - и я также не хочу тормозить процесс разработки, содержащий локальную базу данных H2, описанную заранее. Помните: мы были ищет самый простой и чистый способ подключения к Heroku Postgres в весенней загрузке приложение с помощью JPA / Hibernate!


попробуйте использовать JDBC_DATABASE_URL в своем spring.datasource.url вместо разбора DATABASE_URL.

рекомендуется проанализировать DATABASE_URL, но если вы не можете заставить его работать, jdbc_database_url должен быть в порядке.


Это лучший ответ на проблемы с googling Postgres с образцом Java-приложения, которое предоставляет Heroku.

это шаги, которые я сделал, чтобы заставить его работать (Win 7).

1.) Приложение производственного сервера.файл свойств будет содержать системные среды (убедитесь, что этот файл был зафиксирован)

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.) Измените локальное приложение.свойства быть жестко. Вы можете увидеть необработанные значения по бег!--3-->

spring.datasource.url=jdbc://..
spring.datasource.username=XYZ
spring.datasource.password=ABC

это то, что я должен был получить локальную копию моего заявления на работу. Если кто-то нашел лучший способ, пожалуйста, поделитесь!


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