복붙노트

[SPRING] Spring Boot에서 Heroku Postgres에 연결하기

SPRING

Spring Boot에서 Heroku Postgres에 연결하기

나는 JPA / Hibernate를 사용하는 Spring Boot app에서 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;
    }

}

나는 프로필을 사용하고 있는데, 나는 원했던 부분과 잘 어울리는 것 같다 : Heroku SPRING_PROFILES_ACTIVE는 postgres로 설정되었고, 지역 개발 spring.profiles.active는 H2 메모리 내장 데이터베이스 (여기서는 생략)를 사용하는 h2이다. 이 접근 방식은 정상적으로 작동하는 것 같습니다.

application-postgres.properties (프로필 관련 등록 정보)에서

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

Tomcat의 DataSource는 기본 의존성이 포함되어 있기 때문에 좋은 옵션처럼 보였습니다. Spring Boot 참조 가이드는 다음과 같이 말합니다 :

(나는 또한 봄 부팅과 함께 사용되는 Commons DBCP에서 BasicDataSource를보고있다.하지만 나에게 이것은 Commons DBCP를 기본 의존성이 포함되지 않는 가장 깨끗한 선택처럼 보이지 않는다. 그리고 일반적으로 나는 Apache Commons가 정말로 할 수 있는지, 2015 년에 Postgres에 연결하는 것이 좋습니다 ... 또한 Heroku 문서는 "Spring의 BasicDataSource"시나리오를 제공합니다. Spring 자체에서는 이러한 클래스가 보이지 않기 때문에 Commons DBCP를 참조합니다.)

종속성 :

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

현재 상태 : "driverClassName 속성이 null이므로 JDBC 드라이버를로드하지 않음"으로 실패 함 :

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에 대한 액세스가 null 일 수 없습니다").

내 특정 질문

해결법

  1. ==============================

    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)의 DataSource에 한정 될 수 있습니다. 분명히 BasicDataSource (Commons DBCP)는 더 현명한 기본값을가집니다. 그러나이 질문에서 언급했듯이, 나는 기본적으로 Spring Boot와 함께 제공되는 것을 사용했다. 특히 참조 가이드에서 강력하게지지를 받았다.

    경쟁 / 단순 / 더 나은 솔루션에 개방적입니다. 특히 질문 끝에 2-4 번 문제를 해결할 수 있다면 게시 해주십시오!

    업데이트 : JDBC_DATABASE_ *를 사용하면 위의 답변보다 훨씬 간단하다는 것에 유의하십시오. 오랫동안 나는 DATABASE_URL이 선호되어야한다는 인상하에 있었지만, 요즘에는 더 이상 확신 할 수 없습니다.

  2. ==============================

    2.가장 단순한 Spring Boot / Heroku / Hibernate 설정

    가장 단순한 Spring Boot / Heroku / Hibernate 설정

    항상 거기에있는 DATABASE_URL을 제외하고, Heroku는 런타임에 3 개의 환경 변수를 생성합니다. 그들은:

    JDBC_DATABASE_URL
    JDBC_DATABASE_USERNAME
    JDBC_DATABASE_PASSWORD
    

    아시다시피 스프링 부트는 application.properties 파일에서 spring.datasource. * 속성을 찾으면 데이터베이스를 자동으로 구성합니다. 다음은 내 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 Dependencies

    제 경우에는 Hibernate (PostgreSQL과 함께 spring-boot-starter-jpa에 번들되어 있습니다. 그래서 build.gradle에 올바른 의존성이 필요했습니다 :

    dependencies {
        compile("org.springframework.boot:spring-boot-starter-data-jpa")
        compile('org.postgresql:postgresql:9.4.1212')
    }
    
  3. ==============================

    3.나는 모든 대답을 읽었지만 조닉이 찾고 있던 것을 찾지 못했습니다.

    나는 모든 대답을 읽었지만 조닉이 찾고 있던 것을 찾지 못했습니다.

    대부분의 사람들이 Spring Boot & Heroku에서 사용하기를 원하는 개발 프로세스에는 테스팅 및 빠른 개발 사이클을위한 로컬 H2 인 메모리 데이터베이스와 Heroku에서 스테이징 및 프로덕션을위한 Heroku Postgres 데이터베이스가 포함됩니다.

    단계별로해야 할 일에 대해 살펴 보겠습니다. 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를 클릭하고 설정 탭으로 전환하십시오. 그런 다음 자격 증명보기 ...를 클릭하면 다음과 비슷한 화면이 나타납니다.

    이제 새 브라우저 탭을 열고 Heroku 응용 프로그램의 설정 탭으로 이동하십시오. Reveal Config Vars를 클릭하고 다음 환경 변수를 만듭니다.

    Heroku에서 이것은 다음과 같아야합니다 :

    이제 그게 다야! Heroku 앱은 구성 변수를 변경할 때마다 다시 시작되므로 앱이 로컬에서 H2를 실행해야하고 Heroku에 배포 할 때 PostgreSQL과 연결할 준비가되어 있어야합니다.

    주목할 수 있듯이, 우리는 pom.xml에 tomcat-jdbc 의존성을 추가하고 환경 변수로 SPRING_DATASOURCE_TYPE = org.apache.tomcat.jdbc.pool.DataSource를 구성했습니다. 이 말에 관한 문서에는 약간의 힌트 만 있습니다.

    Spring Boot 2.x 표준 HikariCP 대신 DataSource를 풀링하는 Tomcat으로 다시 전환 한 몇 가지 이유가 있습니다. 이미 설명했듯이 spring.datasource.url을 지정하지 않으면 Spring은 PostgreSQL 대신 내장 된 im-memory H2 데이터베이스를 autowire하려고합니다. 그리고 히카리 문제는 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
    

    그래서 HikariCP와 함께 Heroku & Postgres에서 Spring Boot 2.x를 사용하지는 않았지만 Tomcat JDBC로 - 그리고 나는 또한 선행 기술 된 로컬 H2 데이터베이스를 포함하는 개발 프로세스를 제동하고 싶지 않습니다. 기억하세요 : 우리는 JPA / Hibernate를 사용하는 Spring Boot app에서 Heroku Postgres에 연결하는 가장 단순하고 청결한 방법을 찾고있었습니다!

  4. ==============================

    4.DATABASE_URL을 구문 분석하는 대신 spring.datasource.url로 JDBC_DATABASE_URL을 사용해보십시오.

    DATABASE_URL을 구문 분석하는 대신 spring.datasource.url로 JDBC_DATABASE_URL을 사용해보십시오.

    DATABASE_URL을 구문 분석하는 것이 좋지만 작동시키지 못하면 JDBC_DATABASE_URL이 정상적이어야합니다.

  5. ==============================

    5.이것은 Heroku가 제공하는 샘플 Java 응용 프로그램으로 Postgres 문제를 검색 할 때 가장 많이 사용되는 답변입니다.

    이것은 Heroku가 제공하는 샘플 Java 응용 프로그램으로 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
    

    이것이 내 응용 프로그램의 로컬 복사본을 가져와야 만했습니다. 누구든지 더 나은 방법을 발견한다면 공유하십시오!

  6. ==============================

    6.

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

    7.이 작업을 쉽게 수행 할 수 있도록 라이브러리를 만들었습니다. https://github.com/vic-cw/heroku-postgres-helper

    이 작업을 쉽게 수행 할 수 있도록 라이브러리를 만들었습니다. https://github.com/vic-cw/heroku-postgres-helper

    빌드 스크립트와 응용 프로그램 논리에서 데이터베이스에 액세스해야하는 경우이 방법이 더 유용합니다. 왜 여기 있는지보십시오.

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

    자바 애플리케이션 코드 :

    import com.github.viccw.herokupostgreshelper.HerokuPostgresHelper;
    
    ...
    
    String databaseConnectionString = HerokuPostgresHelper.getDatabaseJdbcConnectionString();
    
  8. from https://stackoverflow.com/questions/33633243/connecting-to-heroku-postgres-from-spring-boot by cc-by-sa and MIT license