QuarkusでDBと接続するWebAPI作ってみた

# 目的
巷で話題の[Quarkus](https://quarkus.io/)の使い勝手を確認した内容のまとめを残します。
Get Started だけは物足りなかったのでローカルで立てたPostgreSQLをHibernateで操作するRESTのWebAPIにしてみました。

# 事前準備
## Mavenプロジェクト
[QUARKUS - CREATING YOUR FIRST APPLICATION](https://quarkus.io/guides/getting-started-guide)を先に完了させておきます。
プロジェクトの状態はこんな感じです。

```
$ tree ./
./
├── pom.xml
├── quarkus_quickstart.iml
└── src
    ├── main
    │   ├── docker
    │   │   └── Dockerfile
    │   ├── java
    │   │   └── com
    │   │       └── dsert
    │   │           └── quickstart
    │   │               ├── GreetingResource.java
    │   │               └── GreetingService.java
    │   └── resources
    │       ├── META-INF
    │       │   ├── microprofile-config.properties
    │       │   └── resources
    │       │       └── index.html
    │       └── application.properties
    └── test
        └── java
            └── com
                └── dsert
                    └── quickstart
                        ├── GreetingResourceTest.java
                        └── NativeGreetingResourceIT.java
```

## PostgreSQL
適当にテーブルとかを作っておきます。
IntelliJで生成したDDLは以下のような感じです。

```sql
create database quarkus	with owner quarkus;

create table if not exists users
(
	id serial not null
		constraint users_pk
			primary key,
	name varchar(255) not null
);
```

# 実装手順
## 依存関係
[QUARKUS - EXTENSIONS](https://quarkus.io/extensions/#data)の項目を参考にしながら、`pom.xml`にHibernateとPostgreSQLの依存関係を追加します。
また、Get Startedのままの依存関係ではJSONでのリクエスト・レスポンスができないので、`quarkus-resteasy`を`quarkus-resteasy-jsonb`に変更します。

```xml:pom.xml
<dependencies>
  ...
  <dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-jsonb</artifactId>
  </dependency>

  <!-- Hibernate ORM specific dependencies -->
  <dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-orm</artifactId>
    <scope>provided</scope>
  </dependency>

  <!-- JDBC driver dependencies -->
  <dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-postgresql</artifactId>
    <scope>provided</scope>
  </dependency>
</dependencies>
```

## 環境値
[QUARKUS - APPLICATION CONFIGURATION GUIDE](https://quarkus.io/guides/application-configuration-guide)に記述されているように、application.propertiesで環境値の管理が可能です。
DB接続に必要な情報を以下のように記述します。

```src/main/resources/application.properties
quarkus.datasource.url: jdbc:postgresql://localhost:5432/quarkus
quarkus.datasource.driver: org.postgresql.Driver
quarkus.datasource.username: quarkus
quarkus.datasource.password: quarkus-pass
```

## ソースコード
### Entity
データをマッピングする、テーブルに合うEntityオブジェクトを作成します。

```src/main/java/com/dsert/quickstart/User.java
package com.dsert.quickstart;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity(name = "users")
public class User {
    private int id;
    private String name;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

### Service
DB操作をするサービスクラスを作ります。

```src/main/java/com/dsert/quickstart/UserService.java
package com.dsert.quickstart;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.util.List;

@ApplicationScoped
public class UserService {
    @Inject
    EntityManager em;

    @Transactional
    public void createUser(String name) {
        User user = new User();
        user.setName(name);
        this.em.persist(user);
    }

    public List<User> getUsers() {
        return this.em.createNativeQuery("select * from users", User.class).getResultList();
    }
}
```

### Resource
HTTPリクエストを処理するリソースクラスを作ります。

```src/main/java/com/dsert/quickstart/UserResource.java
package com.dsert.quickstart;

import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
    @Inject
    UserService service;

    @POST
    public Map<String, String> createUser(User user) {
        this.service.createUser(user.getName());
        return Collections.singletonMap("message", "OK");
    }

    @GET
    public List<User> getUsers() {
        return this.service.getUsers();
    }
}
```

## 動作確認
### アプリケーション起動
気軽に起動したいので、DockerImageではなく、developmentモードで起動します。

```
mvn compile quarkus:dev                                                                                                                                                                    6.2m
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.d-sert:quarkus_quickstart >--------------------
[INFO] Building quarkus_quickstart 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ quarkus_quickstart ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 3 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ quarkus_quickstart ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- quarkus-maven-plugin:0.11.0:dev (default-cli) @ quarkus_quickstart ---
[INFO] Using servlet resources quarkus_quickstart/src/main/resources/META-INF/resources
Listening for transport dt_socket at address: 5005
2019-03-13 08:49:58,673 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
2019-03-13 08:49:59,282 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 609ms
2019-03-13 08:50:05,128 INFO  [io.quarkus] (main) Quarkus 0.11.0 started in 6.638s. Listening on: http://127.0.0.1:8080
2019-03-13 08:50:05,130 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-postgresql, narayana-jta, resteasy, resteasy-jsonb]
```

### HTTPリクエスト
#### POST
```console
$ curl -X POST -H 'Content-Type: application/json' 'http://localhost:8080/users' -d '{"name": "sert"}'
{"message":"OK"}
```

#### GET
```console
$ curl 'http://localhost:8080/users'
[{"id":5,"name":"sert"}]
```

# 最後に
本当はMySQLに繋ぎたかったのですが、MySQLのDriverがExtensionになく、
MariaDBのDriverでも上手く繋げなかったのでPostgreSQLで実施しました。
そのうちMySQLでリベンジしてみるつもりです。