Search code examples
springhibernateherokuheroku-postgresmicronaut

setting DataSource programmatically in Micronaut + Hibernate for Heroku Postgres


I have an application which I want to deploy on Heroku. Heroku is updating database credentials from time to time and they automatically inject the connection configuration into one environment variable. This environment variable can be parsed to obtain the user, password and the URL.

I tried to do the DataSource configuration in the way which they recommend.

@Factory
//@Requires(env = Environment.HEROKU)
public class HerokuDataSourceConfig {

    @Bean
    public DataSource dataSource() throws URISyntaxException {
//        URI dbUri = new URI(System.getenv("DATABASE_URL"));

//        String username = dbUri.getUserInfo().split(":")[0];
//        String password = dbUri.getUserInfo().split(":")[1];
//        String dbUrl = "jdbc:postgresql://" + dbUri.getHost() + ':' + dbUri.getPort() + dbUri.getPath() + "?sslmode=require";

        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost:5432/postgres");
        config.setUsername("postgres");
        config.setPassword("");
        config.setDriverClassName("org.postgresql.Driver");
        config.setConnectionTestQuery("SELECT 1");

        return new HikariUrlDataSource(config);
    }
}

The commented lines are from the initial heroku configuration, the uncommented code is just for testing this locally.

When I try to run the application and hit an endpoint which requires Hibernate, I get this

No bean of type [javax.persistence.EntityManager] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).

I activated the tracing as suggested and here is some relevant log data

20:24:50.414 [pool-1-thread-15] DEBUG i.m.context.condition.Condition - Bean [Definition: io.micronaut.configuration.hibernate.jpa.EntityManagerFactoryBean] will not be loaded due to failing conditions:
20:24:50.414 [pool-1-thread-15] DEBUG i.m.context.condition.Condition - * No bean of type [interface org.hibernate.boot.SessionFactoryBuilder] present within context
20:28:04.598 [main] DEBUG i.m.context.condition.Condition - Bean [Definition: io.micronaut.jdbc.spring.DataSourceTransactionManagerFactory] will not be loaded due to failing conditions:
20:28:04.599 [main] DEBUG i.m.context.condition.Condition - * Custom condition [class io.micronaut.jdbc.spring.HibernatePresenceCondition] failed evaluation
20:28:04.895 [main] DEBUG i.m.context.condition.Condition - Bean [Definition: io.micronaut.configuration.hibernate.jpa.EntityManagerFactoryBean] will not be loaded due to failing conditions:
20:28:04.895 [main] DEBUG i.m.context.condition.Condition - * No bean of type [class org.hibernate.boot.MetadataSources] present within context
20:28:04.895 [main] DEBUG i.m.context.condition.Condition - Bean [Definition: io.micronaut.configuration.hibernate.jpa.EntityManagerFactoryBean] will not be loaded due to failing conditions:
20:28:04.896 [main] DEBUG i.m.context.condition.Condition - * No bean of type [interface org.hibernate.boot.SessionFactoryBuilder] present within context

What am I doing wrong? Probably the configuration is good but somehow Micronaut doesn't know about my newly configured data source.

When I am debugging this code, while micronaut is starting, at some point it will enter the factory and create the datasource. The previous logs are printed before the data source factory is being called.


Solution

  • In the meantime I found a solution with the help of Heroku.

    For apps that use a gradle buildpack like mine, Heroku injects into container the following environment variables. I didn't see those at first, all I knew about was was DATABASE_URL

    datasources:
      default:
        url: ${JDBC_DATABASE_URL}
        username: ${JDBC_DATABASE_USERNAME}
        password: ${JDBC_DATABASE_PASSWORD}
        driverClassName: org.postgresql.Driver