Spring Boot Inner-loop

The code

Our simple application exposes a CRUD interface for a table (FRUIT) in a relational database by using JPA and Hibernate as the ORM framework. To do so, it uses JPA annotations like @Entity, @Id, etc. You can find the Java code of the JPA entity below.

In order to make the code more portable we have annotated the id attribute so that the value is generated using a sequence named FRUIT_SEQ.
@Entity
public class Fruit {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "FruitSequence")
    @SequenceGenerator(name = "FruitSequence", sequenceName = "FRUIT_SEQ")
    private Integer id;

    private String name;

    public Fruit() {
    }

    public Fruit(String type) {
        this.name = type;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

The actual service is implemented in class FruitController find below a simplified version of the code. Nothing complicated here.

@RestController
@RequestMapping(value = "/api/fruits")
public class FruitController {

    private final FruitRepository repository;

    public FruitController(FruitRepository repository) {
        this.repository = repository;
    }

    @GetMapping("/{id}")
    public Fruit get(@PathVariable("id") Integer id) {
        ...
        return repository.findById(id).get();
    }

    @GetMapping
    public List<Fruit> getAll() {
        ...
        return StreamSupport
                .stream(fruits, false)
                .collect(Collectors.toList());
    }

    @ResponseStatus(HttpStatus.CREATED)
    @PostMapping
    public Fruit post(@RequestBody(required = false) Fruit fruit) {
        ...
        return repository.save(fruit);
    }

    ...
}

Getting the Code

Time to get our hands dirty. Clone or download the code, your choice.

Choose a folder to put the code into and stick to it till the end of the guide. All paths are relative to this folder.
  • Clone the code

  • Download the code

git clone --single-branch --branch master https://github.com/redhat-scholars/java-inner-loop-dev-guide.git
curl -L https://github.com/redhat-scholars/java-inner-loop-dev-guide/archive/master.zip -o java-inner-loop-dev-guide.zip
unzip java-inner-loop-dev-guide.zip
mv java-inner-loop-dev-guide-master java-inner-loop-dev-guide

Change to the dir where the code is.

cd java-inner-loop-dev-guide/apps/spring-boot-app

Maven Profiles

As we stated at the beginning we want to use different databases, H2 to develop quickly then either PostgreSQL or Oracle once the code is deployed in OpenShift. In order to do so we have defined 3 profiles:

  • local for H2

  • openshift-postgresql for PostgreSQL

  • openshift-oracle for Oracle

As usual you can find those profiles at: src/main/resources.
  • local

  • openshift-postgresql

  • openshift-oracle

# H2 settings
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.datasource.platform=h2

spring.jpa.properties.hibernate.hbm2ddl.import_files=import-h2.sql
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create
# PostgreSQL settings
spring.datasource.url=jdbc:postgresql://${SERVICE_DB_HOST}:5432/${SERVICE_DB_NAME}
spring.datasource.username=${SERVICE_DB_USER}
spring.datasource.password=${SERVICE_DB_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.platform=postgresql

spring.jpa.properties.hibernate.hbm2ddl.import_files=import-postgresql.sql
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=create

## To avoid CLOB related error...
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
# Oracle settings
spring.datasource.url=jdbc:oracle:thin:@${SERVICE_DB_HOST}:1521/${SERVICE_DB_NAME}
spring.datasource.username=${SERVICE_DB_USER}
spring.datasource.password=${SERVICE_DB_PASSWORD}
spring.datasource.driver.class=oracle.jdbc.driver.OracleDriver
spring.datasource.platform=oracle

spring.jpa.properties.hibernate.hbm2ddl.import_files=import-oracle.sql
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect
spring.jpa.hibernate.ddl-auto=create

Import files

There are, accordingly, three different import.sql files, since each database has a different way of referring to the next value of a database sequence.

You can find import files here: src/main/resources.
  • import-h2.sql

  • import-postgresql.sql

  • import-oracle.sql

insert into fruit (id, name) values (FRUIT_SEQ.NEXTVAL, 'Cherry');
insert into fruit (id, name) values (FRUIT_SEQ.NEXTVAL, 'Apple');
insert into fruit (id, name) values (FRUIT_SEQ.NEXTVAL, 'Banana');
insert into fruit (id, name) values ( nextval ('FRUIT_SEQ'), 'Cherry');
insert into fruit (id, name) values ( nextval ('FRUIT_SEQ'), 'Apple');
insert into fruit (id, name) values ( nextval ('FRUIT_SEQ'), 'Banana');
insert into fruit (id, name) values (FRUIT_SEQ.NEXTVAL, 'Cherry');
insert into fruit (id, name) values (FRUIT_SEQ.NEXTVAL, 'Apple');
insert into fruit (id, name) values (FRUIT_SEQ.NEXTVAL, 'Banana');

Running the code locally against H2

One decision made for this example was to keep it as SQL-standard as possible so that we can move to different database as easily as possible. I know, sometimes this is not possible or ideal…​ but it’s ok for this example.

As we have said before in this guide you’ll not have to code, instead we’ll focus on running, testing, deploying, etc.

Let’s get started and see if the code works locally and using H2.

mvn clean spring-boot:run -Dspring-boot.run.profiles=local -Plocal

You should get this output.

  • Pay attention to the profile in use: The following profiles are active: local.

  • Also the import.sql file used: Executing import script 'file:/Users/nostromo/…​/import-h2.sql'

...
2021-01-28 INFO 59429 --- [  restartedMain] dev.snowdrop.example.ExampleApplication  : Starting ExampleApplication on nostromo with PID 59429 (/Users/nostromo/java-inner-loop-dev-guide/target/classes started by cvicensa in /Users/nostromo/java-inner-loop-dev-guide)
2021-01-28 INFO 59429 --- [  restartedMain] dev.snowdrop.example.ExampleApplication  : The following profiles are active: local
...
2021-01-28 INFO 59429 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-01-28 INFO 59429 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 42ms. Found 1 JPA repository interfaces.
...
2021-01-28 INFO 59429 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1352 ms
2021-01-28 INFO 59429 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-01-28 INFO 59429 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-01-28 INFO 59429 --- [  restartedMain] o.s.b.a.h2.H2ConsoleAutoConfiguration    : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:testdb'
2021-01-28 INFO 59429 --- [  restartedMain] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-01-28 INFO 59429 --- [  restartedMain] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.21.Final
2021-01-28 INFO 59429 --- [  restartedMain] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2021-01-28 INFO 59429 --- [  restartedMain] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2021-01-28 INFO 59429 --- [  restartedMain] o.h.t.schema.internal.SchemaCreatorImpl  : HHH000476: Executing import script 'file:/Users/nostromo/java-inner-loop-dev-guide/target/classes/import-h2.sql'
2021-01-28 INFO 59429 --- [  restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-01-28 INFO 59429 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
...
2021-01-28 INFO 59429 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-01-28 INFO 59429 --- [  restartedMain] dev.snowdrop.example.ExampleApplication  : Started ExampleApplication in 3.632 seconds (JVM running for 4.2)

Now open a browser and point to http://localhost:8080

You can edit, save, delete to test the functionalities implemented by FruitController

You should see this:

Fruit Service on H2

Now you can stop the process with Ctrl+C and go on with the lab.