【问题标题】:Set up a PostgreSQL database on Heroku (required entityManagerFactory)在 Heroku 上设置 PostgreSQL 数据库(需要 entityManagerFactory)
【发布时间】:2018-12-21 05:34:24
【问题描述】:

我想在 Spring Boot 中实现的 Heroku 上创建一个 Web 应用程序。最后,我设法部署了一个带有“Whitelabel error page 404”的普通项目,确认了部署的正确性。

我跟着(几乎一样):https://devcenter-assets0.herokucdn.com/articles/spring-boot-memcache

不幸的是,我在 Spring Data JPA 中只遇到了一个(我希望是)问题(在添加服务(例如 Spring Security 和 Lombok 正常工作)之后),这使我无法构建应用程序和使用 PostgreSQL:

Description:
Parameter 0 of constructor in io.github.plkpiotr.fifabackend.controller.TaskController required a bean named 'entityManagerFactory' that could not be found.

Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>io.github.plkpiotr</groupId>
    <artifactId>fifa-backend</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>fifa-backend</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!--<dependency>-->
        <!--<groupId>org.springframework.boot</groupId>-->
        <!--<artifactId>spring-boot-starter-security</artifactId>-->
        <!--</dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties:

spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.maxActive=10
spring.datasource.maxIdle=5
spring.datasource.minIdle=2
spring.datasource.initialSize=5
spring.datasource.removeAbandoned=true
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

Task.java:

package io.github.plkpiotr.fifabackend.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;

@Entity
public class Task {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @NotNull
    private String name;

    public Task() {}

    public Task(String name) {
        this.name = name;
    }

    public Long getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return String.format("Task[id=%d, name='%s']", this.id, this.name);
    }

}

TaskRepository.java:

package io.github.plkpiotr.fifabackend.repository;

import io.github.plkpiotr.fifabackend.model.Task;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TaskRepository extends CrudRepository<Task, Long> {}

TaskController.java:

package io.github.plkpiotr.fifabackend.controller;

import javax.validation.Valid;
import java.lang.Iterable;

import io.github.plkpiotr.fifabackend.model.Task;
import io.github.plkpiotr.fifabackend.repository.TaskRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/")
public class TaskController {

    @Autowired
    private TaskRepository taskRepo;

    @Autowired
    public TaskController(TaskRepository repo) {
        this.taskRepo = repo;
    }

    @RequestMapping(method = RequestMethod.GET)
    public String showAllTasks(ModelMap model) {
        Iterable<Task> tasks = this.taskRepo.findAll();
        model.addAttribute("tasks", tasks);
        model.addAttribute("newTask", new Task());
        return "task";
    }

    @RequestMapping(method = RequestMethod.POST)
    public String newTask(ModelMap model,
                          @ModelAttribute("newTask") @Valid Task task,
                          BindingResult result) {
        if (!result.hasErrors()) {
            this.taskRepo.save(task);
        }
        return showAllTasks(model);
    }

    @RequestMapping(method = RequestMethod.DELETE)
    public String deleteTask(ModelMap model, @RequestParam("taskId") Long id) {
        this.taskRepo.deleteById(id);
        return showAllTasks(model);
    }
}

我试过了:

1)。添加差异化依赖项(h2、hibernate-core、...)

2)。删除 /.m2/ 文件夹并重新下载依赖项

3)。不要在其他尝试失败后哭泣...

如果您对克隆回购感兴趣: https://github.com/plkpiotr/fifa-backend

除此之外,如果我在 TaskController.java 中删除,我会得到:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: At least one JPA metamodel must be present!

如何解决问题?

【问题讨论】:

  • 这个设置看起来很可疑:spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 你试过删除那行吗?基本上,您是在告诉 Spring 不要自动配置您的数据源,所以如果我没记错的话,这里没有 EntityManagerFactory 是有道理的。
  • 如果我查看已成功部署到 Heroku 的 Spring-Boot 应用程序,我发现除了您的属性之外,我还有以下行:spring.datasource.url=jdbc:postgresql:// localhost:5432/your_database 请注意,Heroku 将这个 url 替换为它自己的一个,但也许该属性需要存在?
  • 此外,您还可以摆脱 FifaBackendApplication 类中的所有内容,您只需要在这样一个简单的应用程序上使用 @SpringBootApplication 即可。由于您定义了 spring-boot-starter-data-jpa 依赖项,因此 Spring 会发现您自己拥有实体。
  • 尝试添加这些属性: spring.datasource.username=your_user spring.datasource.password=some_password 这些也被 Heroku 替换,所以它们可能是随机的
  • 您是否删除了 FifaBackendApplication 类上的所有注释?你能把你目前拥有的东西推送到你的 git 仓库,我会看看它

标签: java postgresql jpa heroku spring-data-jpa


【解决方案1】:

@EntityScan@EnableJpaRepositories@ComponentScan 不支持通配符语法。例如您当前使用的:

@EntityScan("io.github.plkpiotr.*")`

但它应该只是基本包名

@EntityScan("io.github.plkpiotr")

【讨论】:

  • 它导致:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: javassist/bytecode/ClassFile 但是,添加注释是由于寻找解决方案,老实说 - 不是故意的。
  • 添加Javassit 的预期版本,看看是否有帮助。如果是这样调试它为什么丢失,你的pom.xml 看起来不错。