【问题标题】:The application must supply JDBC connections - Deploy Play 2.4.2 (with JPA + Hibernate ) to Heroku应用程序必须提供 JDBC 连接 - 将 Play 2.4.2(使用 JPA + Hibernate)部署到 Heroku
【发布时间】:2015-09-18 10:08:01
【问题描述】:

我正在尝试将 Play Framework (2.4.2) 应用程序部署到 Heroku。我正在使用 JPA(带有 Hibernate 4.3.10 和 Postgres)。我也在运行JDK8。

抱歉,帖子太长了,但我想提供尽可能多的信息。

我在尝试访问应用程序时遇到此错误:

Caused by: java.lang.UnsupportedOperationException: The application must supply JDBC connections
2015-09-18T09:23:46.533143+00:00 app[web.1]:    at org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl.getConnection(UserSuppliedConnectionProviderImpl.java:61) ~[org.hibernate.hibernate-core-4.3.10.Final.jar:4.3.10.Final]
2015-09-18T09:23:46.533144+00:00 app[web.1]:    at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:380) ~[org.hibernate.hibernate-core-4.3.10.Final.jar:4.3.10.Final]
2015-09-18T09:23:46.533146+00:00 app[web.1]:    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:228) ~[org.hibernate.hibernate-core-4.3.10.Final.jar:4.3.10.Final]
2015-09-18T09:23:46.533147+00:00 app[web.1]:    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:171) ~[org.hibernate.hibernate-core-4.3.10.Final.jar:4.3.10.Final]
2015-09-18T09:23:46.533148+00:00 app[web.1]:    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:67) ~[org.hibernate.hibernate-core-4.3.10.Final.jar:4.3.10.Final]
2015-09-18T09:23:46.533149+00:00 app[web.1]:    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:162) ~[org.hibernate.hibernate-core-4.3.10.Final.jar:4.3.10.Final]

我在Hibernate's code and found the UserSuppliedConnectionProviderImpl 类中四处寻找,它似乎完全按照应有的方式工作,因为实现只是抛出了一个异常,这让我相信配置有问题,因为该类没有任何用处,所以我永远不应该到达该代码路径。

我已经关注了how to deploy to heroku上的文档

因此,我的 Procfile 如下:

web: target/universal/stage/bin/myapp -Dhttp.port=${PORT} -DapplyEvolutions.default=false -Ddb.default.driver=org.postgresql.Driver -Ddb.default.url=${DATABASE_URL}

我也尝试过(注意 {})但没有成功

web: target/universal/stage/bin/myapp -Dhttp.port=$PORT -DapplyEvolutions.default=false -Ddb.default.driver=org.postgresql.Driver -Ddb.default.url=$DATABASE_URL

我的 build.sbt 文件包含 postgres 和 hibernate:

libraryDependencies ++= Seq(
  javaJdbc,
  cache,
  javaWs,
  javaJpa,
  "org.hibernate" % "hibernate-entitymanager" % "4.3.10.Final",
  "org.postgresql" % "postgresql" % "9.4-1201-jdbc41" withSources(),
  //other stuff
)

我的application.conf 包括以下内容:

db.default.driver=org.postgresql.Driver
db.default.url=${?JDBC_DATABASE_URL}
db.default.username=${?JDBC_DATABASE_USERNAME}
db.default.password=${?JDBC_DATABASE_PASSWORD}
db.default.jndiName=myDS
jpa.default=myDS

我的 persistence.xml 文件(在 conf/META-INF 中)如下(请阅读下文,了解为什么此文件中没有 url/user/password 属性):

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
         version="2.0">

<persistence-unit name="NoteDS" transaction-type="RESOURCE_LOCAL">
    <description>
        Persistence unit for the Weldnote Project
    </description>
    <provider>org.hibernate.ejb.HibernatePersistence</provider>

     <properties>
        <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />


        <!-- Hibernate Properties -->
        <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />

        <!-- Show SQL statements -->
        <property name="hibernate.show_sql" value="true" />

        <!-- Auto Update the database on Entity change -->
        <!-- <property name="hibernate.hbm2ddl.auto" value="auto" /> -->

    </properties>
</persistence-unit>

根据我从 Heroku 文档(以及 Play 的文档)中读到的内容,Heroku 在环境中提供了DATABASE_URL,这是一种“非标准格式”,但 Play 可以理解。虽然我发现一些报告说它没有直接使用 JPA,因此您必须解析 url 并设置值。我找到了这个 gist explaining how to parse the url 并创建了一个 GlobalSetting 类来做到这一点(我在 application.conf 键下添加了 application.conf application.global=org.example.GlobalSetting

package org.example;

import java.util.StringTokenizer;
import play.Application;
import play.GlobalSettings;

public class GlobalSetting extends GlobalSettings{

@Override
public void beforeStart(Application app) {

    super.beforeStart(app);
    String databaseUrl = System.getenv("DATABASE_URL");
    StringTokenizer st = new StringTokenizer(databaseUrl, ":@/");
    String dbVendor = st.nextToken(); //if DATABASE_URL is set
    String userName = st.nextToken();
    String password = st.nextToken();
    String host = st.nextToken();
    String port = st.nextToken();
    String databaseName = st.nextToken();

    System.out.println(System.getProperty("javax.persistence.jdbc.url"));
    System.out.println(System.getProperty("javax.persistence.jdbc.user"));
    System.out.println(System.getProperty("javax.persistence.jdbc.password"));
    System.out.println(System.getProperty("javax.persistence.jdbc.driver"));

    System.setProperty("javax.persistence.jdbc.url", databaseUrl);
    System.setProperty("javax.persistence.jdbc.user", userName);
    System.setProperty("javax.persistence.jdbc.password", password);
    System.setProperty("javax.persistence.jdbc.driver", "org.postgresql.Driver");
    System.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");

    System.out.println(System.getProperty("javax.persistence.jdbc.url"));
    System.out.println(System.getProperty("javax.persistence.jdbc.user"));
    System.out.println(System.getProperty("javax.persistence.jdbc.password"));
    System.out.println(System.getProperty("javax.persistence.jdbc.driver"));



}

}

我知道该类正在运行,因为 println 出现在 Heroku 的日志中。我尝试了许多不同程度的失败变化。我似乎很清楚,这与数据源/数据库的配置有关,但我无法弄清楚任何帮助将不胜感激。

在本地我有一个工作应用程序,但使用 Play 的标准配置(无 DATABASE_URL),我尝试在本地播放环境中匹配 Heroku 的配置,问题似乎是一样的。

我搜索了很多(并在 stackoverflow 上发现了几个具有相同症状的问题,但在我的特定情况下我无法找到可能导致此问题的原因)

编辑

借助@codefinger 的指点和更多的谷歌搜索,我取得了突破,但我不明白为什么会这样。

在尝试使应用程序在本地运行时,我发现它不适用于 DATABASE_URL,因此我遵循了@codefinger 的建议,问题仍然存在(即运行heroku local web),但如果我有我在 persistence.xml 文件中的属性一切正常(运行activator run)。偶然我在 GlobalSettings 文件中尝试了以下内容:

System.setProperty("javax.persistence.jdbc.url", System.getenv("JDBC_DATABASE_URL"));
System.setProperty("javax.persistence.jdbc.user", System.getenv("JDBC_DATABASE_USERNAME"));
System.setProperty("javax.persistence.jdbc.password", System.getenv("JDBC_DATABASE_PASSWORD"));
System.setProperty("javax.persistence.jdbc.driver", "org.postgresql.Driver");
System.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");

System.setProperty("hibernate.connection.driver_class", "org.postgresql.Driver");
System.setProperty("hibernate.connection.url", System.getenv("JDBC_DATABASE_URL"));
System.setProperty("hibernate.connection.username", System.getenv("JDBC_DATABASE_USERNAME"));
System.setProperty("hibernate.connection.password", System.getenv("JDBC_DATABASE_PASSWORD"));

通过传递javax.persistence.* AND hibernate.connection.* 连接属性显然它现在可以访问数据库。 但是我似乎无法理解在本地运行时只使用javax.persitence.* 文件上的javax.persitence.* 属性它工作正常。

我正在继续我的实验,试图找到合适的配置。

【问题讨论】:

    标签: java hibernate jpa heroku playframework


    【解决方案1】:

    您是否在本地使用 DATABASE_URL 环境变量对此进行了测试?我不确定您对DATABASE_URL 的解析是否正确。这是一个library that will do it for you,您可以将其包含在您的build.sbt 中,如下所示:

    "com.heroku.sdk" % "heroku-jdbc" % "0.1.1"
    

    一般来说,它是这样做的:

    val dbUri = new URI(System.getenv("DATABASE_URL"))
    val username = dbUri.getUserInfo.split(":")(0)
    val password = dbUri.getUserInfo.split(":")(1)
    val dbUrl = s"jdbc:postgresql://${dbUri.getHost}:${dbUri.getPort()}${dbUri.getPath}"
    

    或者,您可以在Global 类中使用JDBC_DATABASE_URL(请注意,下面的代码仅作为示例,因为它不检查nulls):

    System.setProperty("javax.persistence.jdbc.url", System.getenv("JDBC_DATABASE_URL"));
    System.setProperty("javax.persistence.jdbc.user", System.getenv("JDBC_DATABASE_USER"));
    System.setProperty("javax.persistence.jdbc.password", System.getenv("JDBC_DATABASE_PASSWORD"));
    

    我建议在项目根目录中的文件.env 中添加一个DATABASE_URL 条目,如下所示:

    DATABASE_URL=postgres://user:pass@localhost:5432/dbName
    

    然后使用sbt stage 构建您的应用程序并使用heroku local web 运行它。这将使您可以轻松地使用DATABASE_URL 进行测试。

    Heroku DATABASE_URL 实际上遵循 libpq standard for connection strings。但大多数 JDBC 库只能使用 JDBC URL。

    【讨论】:

    • 感谢 codefinger 的回复,根据您的输入,我无法让它工作,但它让我走上了不同的道路。我已经在原帖上发布了我的发现,你知道它会是什么吗?
    猜你喜欢
    • 2017-07-02
    • 2015-12-04
    • 2013-05-05
    • 2014-06-07
    • 2014-07-26
    • 2016-12-04
    • 2016-04-17
    • 2013-06-25
    相关资源
    最近更新 更多