4

I decided to try out Spring Boot on Heroku and everything works perfectly...well, apart from the database!

I tried many different things to make it work, followed a lot of different approaches I found online and also from questions on StackOverflow, but nothing seems to work for me. I'm going to post only the bare minimum of the app I'm trying to deploy.

The below is desployed fine, but when I'm trying to POST something I get the following as response:

{"timestamp":1413600470146,"error":"Unsupported Media Type","status":415,"message":"Unsupported Media Type"}

And when I'm trying to GET, I get the following exception in the Heroku logs: nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause

org.postgresql.util.PSQLException: ERROR: relation "exampleEntity" does not exist
  Position: 109
  at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2103)
  at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1836)
  at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
  at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:512)
  at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:388)
  at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:273)
  at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
  ....

So, I have the following pom.xml:

<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>com.example</groupId>
    <artifactId>project-name</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.0.2.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <jersey.version>2.8</jersey.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.glassfish.jersey</groupId>
                <artifactId>jersey-bom</artifactId>
                <version>${jersey.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.1-901-1.jdbc4</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
        </dependency>


        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.ext</groupId>
            <artifactId>jersey-spring3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
        </dependency>
        <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.glassfish.jersey.test-framework.providers</groupId>
            <artifactId>jersey-test-framework-provider-inmemory</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>1.5.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.skyscreamer</groupId>
            <artifactId>jsonassert</artifactId>
            <version>1.2.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

    <repositories>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>http://repo.spring.io/libs-release</url>
        </repository>
        <repository>
            <id>org.jboss.repository.releases</id>
            <name>JBoss Maven Release Repository</name>
            <url>https://repository.jboss.org/nexus/content/repositories/releases</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>http://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
</project>

Then my Main class is:

package com.example;

import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.servlet.ServletProperties;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

import com.example.config.JerseyConfig;

@EnableAutoConfiguration
@ComponentScan
@EnableJpaRepositories
public class Main {

    @Bean
    public ServletRegistrationBean jerseyServlet() {
        ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/rest/*");
        // our rest resources will be available in the path /rest/*
        registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
        return registration;
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(Main.class).showBanner(false).run(args);
    }

}

I also have a JerseyConfig (for Jersey configuration):

package com.example.config;

import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;

public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(RequestContextFilter.class);
        packages("com.example");
        register(LoggingFilter.class);
    }
}

And a BasicDataSource for the database:

package com.example.config;

import java.net.URI;
import java.net.URISyntaxException;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SimpleDbConfig {

    @Bean
    public DataSource dataSource() throws URISyntaxException {    
        URI dbUri;
        try {
            String username = "username";
            String password = "password";
            String url = "jdbc:postgresql://localhost/dbname";
            String dbProperty = System.getProperty("database.url");
            if(dbProperty != null) {
                dbUri = new URI(dbProperty);

                username = dbUri.getUserInfo().split(":")[0];
                password = dbUri.getUserInfo().split(":")[1];
                url = "jdbc:postgresql://" + dbUri.getHost() + dbUri.getPath();
            }     

            BasicDataSource basicDataSource = new BasicDataSource();
            basicDataSource.setUrl(url);
            basicDataSource.setUsername(username);
            basicDataSource.setPassword(password);
            return basicDataSource;

        } catch (URISyntaxException e) {
            //Deal with errors here.
            throw e;
        }
    }
}

Finally, I have my REST resource:

package com.example.resource;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.example.service.ExampleService;
import com.example.transport.ExampleEntity;

@Path("/entities")
@Component
public class ExampleResource {

    private ExampleService exampleService;

    @Autowired
    public ExampleResource(ExampleService exampleService) {
        this.exampleService = exampleService;
    }

    @POST
    @Path("/new")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public ExampleEntity createNewEntity(ExampleEntity entity) {
        return exampleService.createNewEntity(entity);
    }

    @GET
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public ExampleEntity getExampleDetails(@PathParam("id") Long id) {
        return exampleService.findEntity(id);
    }

}

A service class:

package com.example.service;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.repositories.ExampleRepository;
import com.example.transport.ExampleEntity;

@Service
public class ExampleService {

    private ExampleRepository repo;

    @Autowired
    public ExampleService(ExampleRepository repo) {
        this.repo = repo;
    }

    @Transactional
    public ExampleEntity createNewEntity(ExampleEntity entity) {
        ExampleEntity savedEntity = repo.save(entity);
        return savedEntity;
    }

    @Transactional
    public ExampleEntity findEntity(Long id) {
        return repo.findOne(id);
    }

}

The entity:

package com.example.transport;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@Entity
public class ExampleEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

And finally the repo interface:

package com.example.repositories;

import org.springframework.data.repository.CrudRepository;

import com.example.transport.ExampleEntity;

public interface MeetingRepository extends CrudRepository<ExampleEntity, Long> {

}

Any help is much appreciated!

Edit:

The Procfile I'm using is the following:

web: java -Dserver.port=$PORT -Ddatabase.url=$DATABASE_URL $JAVA_OPTS -jar target/project-name-0.0.1-SNAPSHOT.jar
1
  • Check out if you are using quoted identifiers exampleEntity is a different table than "exampleEntity": postgresql.org/docs/current/static/… Commented Oct 19, 2014 at 8:41

2 Answers 2

1

It seems you have not created the table exampleEntity yet. I can see you are using postgreSQL, so please use heroku config | grep HEROKU_POSTGRESQL to check if you have one enabled in heroku (also its name and url). If not, enable it in your heroku account manager, it's a free plugin. Use heroku pg:promote HEROKU_POSTGRESQL_<color of your db> to promote it to the main one. You will get some output like:

Promoting HEROKU_POSTGRESQL_BROWN_URL (DATABASE_URL) to DATABASE_URL... done 

Now you should use DATABASE_URL environment variable to connect to your db. Heroku will provide it for you. (Also please post your Procfile too).

Using the url connect to your database and add the required table with a schema that maches your ExampleEntity class.

Side notes

Connecting to heroku db:

If you didn't understand some heroku stuff I wrote, their documentation is really well written:

If I understood you all wrong and you're running your app on localhost and didn't deploy it to heroku yet, then just add the table to your local db instance.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your answer vic. I have a Postgresql db enabled (the result of the above grep is something like: HEROKU_POSTGRESQL_WHITE_URL: postgres://<username>:<password>@ec2-50-17-207-54.compute-1.amazonaws.com:5432/dfjhng7120830i). I'm also using the DATABASE_URL to connect to it (passing it through the Procfile and using it in the SimpleDbConfig class. But I don't know how to create the tables I want to use. I thought they were automatically created. How can I connect to the DB to create them? Can I do it via browser?
Hibernate can automatically drop and recreate your tables if you want it to - just google hibernate autocreate (you don't really want to do this in production). As far as I know you cannot connect to a database using a simple browser, you need to use a the database client. For postgreSQL the default one is called pgadmin and should be provided with posgresql installation (...\PostgreSQL\9.2\bin). My recommended way to go for you is to setup a local database (on your computer), create necessary tables (=relations) then run your app locally.
0

Example I use with automatic table generation

Apart from DATABASE_URL, which is always there, Heroku creates 3 environment variables at Runtime. They are:

JDBC_DATABASE_URL
JDBC_DATABASE_USERNAME
JDBC_DATABASE_PASSWORD

As you may be aware, Spring Boot will automatically configure your database if it finds spring.datasource.* properties in your application.properties file. Here is an example of my 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

In my case I'm using Hibernate (bundled in spring-boot-starter-jpa with PostgreSQL, so I needed the right dependencies in my build.gradle:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.