This short tutorial shows how to create a Quarkus application which exposes a set of JAX-RS endpoints which are backed by a repository of data managed with JPA:

Create a basic Quarkus project first:

mvn io.quarkus:quarkus-maven-plugin:1.4.2.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=quarkus-hibernate \
    -DclassName="org.acme.ExampleResource" \
    -Dpath="/customer"

Depending on the Database you will use, you need to include the appropriate JDBC Driver. Here is the full list of dependencies you will need to run/test your application with hibernate-orm and PostgreSQL:

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>    
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-junit5</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>io.rest-assured</groupId>
  <artifactId>rest-assured</artifactId>
  <scope>test</scope>
</dependency>

Notice we have included also JSON-B dependency. That is required as our Endpoint consumes and produces JSON data. Here is the Endpoint Class which contains CRUD methods coupled with GET/POST/PUT/DELETE HTTP methods:

package org.acme;

import org.jboss.resteasy.annotations.jaxrs.PathParam;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
@Path("/customer")
@Produces("application/json")
@Consumes("application/json")
public class ExampleResource {

    @Inject
    EntityManager entityManager;

    @GET
    public Customer[] get() {
        return entityManager.createNamedQuery("Customers.findAll", Customer.class)
                .getResultList().toArray(new Customer[0]);
    }
    @POST
    @Transactional
    public Response create(Customer customer) {
        if (customer.getId() != null) {
            throw new WebApplicationException("Id was invalidly set on request.", 422);
        }
        System.out.println("Creating "+customer);
        entityManager.persist(customer);
        return Response.ok(customer).status(201).build();
    }

    @PUT
    @Transactional
    public Customer update(Customer customer) {
        if (customer.getId() == null) {
            throw new WebApplicationException("Customer Id was not set on request.", 422);
        }

        Customer entity = entityManager.find(Customer.class, customer.getId());

        if (entity == null) {
            throw new WebApplicationException("Customer with id of " + customer.getId() + " does not exist.", 404);
        }

        entity.setName(customer.getName());

        return entity;
    }

    @DELETE
    @Transactional
    public Response delete(Customer customer) {
        Customer entity = entityManager.find(Customer.class, customer.getId());
        if (entity == null) {
            throw new WebApplicationException("Customer with id of " + customer.getId() + " does not exist.", 404);
        }
        entityManager.remove(entity);
        return Response.status(204).build();
    }
}

Now we will code the Entity class. This class includes an example of @NamedQuery to load the list of Customer objects and the definition of a Sequence which will generate the Customer id. As we will initially add some Customer records with the import.sql script, we will set as initialValue 10:

package org.acme;

import javax.persistence.*;

@Entity
@NamedQuery(name = "Customers.findAll", query = "SELECT c FROM Customer c ORDER BY c.name")
public class Customer {
    private Long id;
    private String name;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "customerSequence")
    @SequenceGenerator(name = "customerSequence", sequenceName = "customerSeq", allocationSize = 1, initialValue = 10)
    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;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Finally, the configuration of the Database connection. The standard configuration file for Quarkus is placed in META-INF/application.properties :

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus
quarkus.datasource.password=quarkus
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkusdb
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2

quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.sql-load-script=import.sql

Please notice that the Hibernate properties can also be specified through the META-INF/persistence.xml file. This is especially useful if you are migrating from a Java EE application:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">

    <persistence-unit name="CustomerPU" transaction-type="JTA">

        <description>My customer entities</description>

        <properties>
            <!-- Connection specific -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL95Dialect"/>

            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>

            <!--
                Optimistically create the tables;
                will cause background errors being logged if they already exist,
                but is practical to retain existing data across runs (or create as needed) -->
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>

            <property name="javax.persistence.validation.mode" value="NONE"/>
        </properties>

    </persistence-unit>
</persistence>


We have included an import.sql file to bootstrap the application with 3 Customer objects:

INSERT INTO Customer(id, name) VALUES (1, 'Batman');
INSERT INTO Customer(id, name) VALUES (2, 'Superman');
INSERT INTO Customer(id, name) VALUES (3, 'Wonder woman');

Here is the full structure of our application:

src
├── main
│   ├── docker
│   │   ├── Dockerfile.jvm
│   │   └── Dockerfile.native
│   ├── java
│   │   └── org
│   │       └── acme
│   │           ├── Customer.java
│   │           └── ExampleResource.java
│   └── resources
│       ├── application.properties
│       ├── import.sql
│       └── META-INF
│           └── resources
│               └── index.html

Now, before starting the application, make sure PostgreSQL Database is available:

docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name quarkus_test -e POSTGRES_USER=quarkus -e POSTGRES_PASSWORD=quarkus -e POSTGRES_DB=quarkusdb  -p 5432:5432 postgres:10.5  

Now we can start the Quarkus application in development mode:

mvn clean install quarkus:dev
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2020-05-17 13:27:06,932 INFO  [io.agr.pool] (Quarkus Main Thread) Datasource '<default>': Initial size smaller than min. Connections will be created when necessary
Hibernate: 
    
    drop table if exists Customer cascade
Hibernate: 
    
    drop sequence if exists customerSeq
Hibernate: create sequence customerSeq start 10 increment 1
Hibernate: 
    
    create table Customer (
       id int8 not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    INSERT INTO Customer(id, name) VALUES (1, 'Batman')
Hibernate: 
    INSERT INTO Customer(id, name) VALUES (2, 'Superman')
Hibernate: 
    INSERT INTO Customer(id, name) VALUES (3, 'Wonder woman')
2020-05-17 13:27:07,341 INFO  [io.quarkus] (Quarkus Main Thread) quarkus-hibernate /hello (powered by Quarkus 1.4.2.Final) started in 2.054s. Listening on: http://0.0.0.0:8080
2020-05-17 13:27:07,343 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020-05-17 13:27:07,343 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, jdbc-postgresql, narayana-jta, resteasy, resteasy-jsonb]

Let's check at first the list of Customer objects:

curl -s http://localhost:8080/customer | jq
[
  {
    "id": 1,
    "name": "Batman"
  },
  {
    "id": 2,
    "name": "Superman"
  },
  {
    "id": 3,
    "name": "Wonder woman"
  }
]

Let's use the POST method to insert a new Customer:

curl -d '{"name":"Spiderman"}' -H "Content-Type: application/json" -X POST http://localhost:8080/customer

{"id":10,"name":"Spiderman"}

Let's use the PUT method to modify an existing Customer:

curl -d '{"id":10,"name":"Hulk"}' -H "Content-Type: application/json" -X PUT http://localhost:8080/customer

{"id":10,"name":"Hulk"}

Let's use the DELETE method to delete an existing Customer:

curl -d '{"id":10,"name":"Hulk"}' -H "Content-Type: application/json" -X DELETE http://localhost:8080/customer

{"id":10,"name":"Hulk"}

Let's check again the list of Customer objects:

curl -s http://localhost:8080/customer | jq
[
  {
    "id": 1,
    "name": "Batman"
  },
  {
    "id": 2,
    "name": "Superman"
  },
  {
    "id": 3,
    "name": "Wonder woman"
  }
]

Great! We have just learnt how to design a basic CRUD application using Quarkus, JPA and JAX-RS

Source code: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/hibernate

0
0
0
s2sdefault