今回は、Jakarta EE 10 + Maven + JAX-RS(Jersey) を使って
とてもシンプルな RESTful CRUD(GET / POST / PUT / DELETE) サンプルを作る手順を
記載します。
イメージ
全体構成
JakartaEERestful
└─ src
└─ main
├─ java
│ └─ com.example
│ ├─ config
│ │ └─ RestApplication.java
│ ├─ model
│ │ └─ Item.java
│ ├─ resource
│ │ └─ ItemResource.java
│ └─ service
│ └─ ItemService.java
└─ webapp
└─ WEB-INF
└─ web.xml(※省略可)
JakartaEE公式サイト
JakartaEE公式サイトでプロジェクトを作成していきます。

ライブラリのインストール
Jakarta Restful WS API
Jersey Conatiner Servlet
Jersey Inject HK2
Jersey Media JSON Jackson
Parayaのインストール

https://payara.fish/downloads/payara-platform-community-edition/
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>com.example</groupId>
<artifactId>JakartaEERestful</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>JakartaEERestful</name>
<description>
This is a very simple Jakarta EE application generated by the official Eclipse Starter.
</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.report.sourceEncoding>UTF-8</project.report.sourceEncoding>
<maven.compiler.release>21</maven.compiler.release>
<jakartaee-api.version>10.0.0</jakartaee-api.version>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
<war-plugin.version>3.4.0</war-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>${jakartaee-api.version}</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/jakarta.ws.rs/jakarta.ws.rs-api -->
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>4.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-servlet -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>4.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.inject/jersey-hk2 -->
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>4.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
<build>
<finalName>JakartaEERestful</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${war-plugin.version}</version>
</plugin>
</plugins>
</build>
</project>
GlassFishのインストール
2. JAX-RS アプリケーション設定
RestApplication.java
package com.example.config;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/api")
public class RestApplication extends Application {
// 設定は不要(自動スキャン)
}
👉 これで
http://localhost:8080/JakartaEERestful/api/...
が REST のベースパスになります。
- エンティティ(DTO)クラス
package com.example.model;
public class Item {
private Long id;
private String name;
public Item() {
}
public Item(Long id, String name) {
this.id = id;
this.name = 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;
}
}
■Jackson(JSON変換)は getter/setter があればOK
■アノテーションは不要です。
4. サービス層(擬似DB)
package com.example.service;
import com.example.model.Item;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
public class ItemService {
private static final Map<Long, Item> store = new HashMap<>();
private static final AtomicLong idGenerator = new AtomicLong(1);
public List<Item> findAll() {
return new ArrayList<>(store.values());
}
public Item findById(Long id) {
return store.get(id);
}
public Item create(Item item) {
Long id = idGenerator.getAndIncrement();
item.setId(id);
store.put(id, item);
return item;
}
public Item update(Long id, Item item) {
if (!store.containsKey(id)) {
return null;
}
item.setId(id);
store.put(id, item);
return item;
}
public boolean delete(Long id) {
return store.remove(id) != null;
}
}
5. REST リソース(CRUD 本体)
package com.example.resource;
import com.example.model.Item;
import com.example.service.ItemService;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
@Path("/items")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ItemResource {
private final ItemService service = new ItemService();
// GET /items
@GET
public List<Item> getAll() {
return service.findAll();
}
// GET /items/{id}
@GET
@Path("/{id}")
public Response getById(@PathParam("id") Long id) {
Item item = service.findById(id);
if (item == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.ok(item).build();
}
// POST /items
@POST
public Response create(Item item) {
Item created = service.create(item);
return Response.status(Response.Status.CREATED)
.entity(created)
.build();
}
// PUT /items/{id}
@PUT
@Path("/{id}")
public Response update(@PathParam("id") Long id, Item item) {
Item updated = service.update(id, item);
if (updated == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.ok(updated).build();
}
// DELETE /items/{id}
@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") Long id) {
boolean deleted = service.delete(id);
if (!deleted) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.noContent().build();
}
}
① 依存関係について
Jakarta EE Web プロファイルを使う場合:
GlassFish / Payara を使うなら Jersey は同梱する。
6. HTMLファイル作成
Jakarta EE の場合、HTML はここに置くのが基本です。
src/main/webapp/index.html
アクセスURL:
http://localhost:8080/JakartaEERestful/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>JAX-RS CRUD Sample</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
}
table {
border-collapse: collapse;
width: 400px;
}
th, td {
border: 1px solid #ccc;
padding: 6px;
text-align: center;
}
th {
background-color: #f0f0f0;
}
input {
margin: 4px;
}
button {
margin: 4px;
}
</style>
</head>
<body>
<h1>Item CRUD (JAX-RS)</h1>
<h2>新規作成 / 更新</h2>
ID(更新時のみ):
<input type="number" id="itemId">
<br>
名前:
<input type="text" id="itemName">
<br>
<button onclick="createItem()">作成 (POST)</button>
<button onclick="updateItem()">更新 (PUT)</button>
<h2>一覧</h2>
<button onclick="loadItems()">再読み込み (GET)</button>
<table>
<thead>
<tr>
<th>ID</th>
<th>名前</th>
<th>操作</th>
</tr>
</thead>
<tbody id="itemTableBody"></tbody>
</table>
<script>
const API_BASE = "/JakartaEERestful/api/items";
// GET
async function loadItems() {
const response = await fetch(API_BASE);
const items = await response.json();
const tbody = document.getElementById("itemTableBody");
tbody.innerHTML = "";
items.forEach(item => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${item.id}</td>
<td>${item.name}</td>
<td>
<button onclick="editItem(${item.id}, '${item.name}')">編集</button>
<button onclick="deleteItem(${item.id})">削除</button>
</td>
`;
tbody.appendChild(tr);
});
}
// POST
async function createItem() {
const name = document.getElementById("itemName").value;
await fetch(API_BASE, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name })
});
clearForm();
loadItems();
}
// PUT
async function updateItem() {
const id = document.getElementById("itemId").value;
const name = document.getElementById("itemName").value;
if (!id) {
alert("IDを入力してください");
return;
}
await fetch(`${API_BASE}/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name })
});
clearForm();
loadItems();
}
// DELETE
async function deleteItem(id) {
await fetch(`${API_BASE}/${id}`, {
method: "DELETE"
});
loadItems();
}
// フォームに反映
function editItem(id, name) {
document.getElementById("itemId").value = id;
document.getElementById("itemName").value = name;
}
// フォームクリア
function clearForm() {
document.getElementById("itemId").value = "";
document.getElementById("itemName").value = "";
}
// 初期表示
loadItems();
</script>
</body>
</html>
warファイルを作成
コンソールコマンド画面で下記のコマンドを入力します。
mvn clean package
正常終了すれば target/ フォルダに JakartaEERestful.war が作られるはずです。

Payaraを起動してwarファイルをデプロイ
Admin Console から WAR をデプロイする手順
1.Admin Console にログイン
ブラウザで http://localhost:4848 を開きます。
ユーザー名とパスワードを求められた場合は、Payara domain 作成時に設定したものを入力します。
(例)
| 項目 | 値 |
|---|---|
| ユーザ名 | admin |
| パスワード | なし |
2.Applications メニューを開く
左側のメニューからApplicationsをクリックします。

右側に「Installed Applications」のリストが表示されます👇

新しい画面(Deploy Application/Module)が開きます

WAR ファイルを選択
・「Packaged File to be Uploaded」欄で Choose File / Browse ボタンを押します。
・Maven で生成したtarget/JakartaEERestful.warを選択
・デプロイするアプリケーション名の確認します。
・「Application Name」欄に自動で JakartaEERestful と入っているとおもいます。
しばらく待つとデプロイが完了します。
デプロイ完了の確認
「Applications」リストに JakartaEERestful が追加されているのを確認します。

デプロイ後の動作確認
下記のURLにアクセスします。
http://localhost:8080/JakartaEERestful/
サイト
JAX-RSで高性能RESTful APIを作る!実践的な実装方法と9つのベストプラクティス
Jersey
トラブルシューティング
指定されたパスが見つかりません。
Payara[domain1]を削除して実行したら下記のエラーが出た。
Unable to start server due following issues: java.io.FileNotFoundException:
C:\Payara\payara6\glassfish\domains\domain1\config\domain.xml (指定されたパスが見つかりません。)
これは Payara のサーバドメインdomain1を削除したため、サーバの設定ファイルが無くなった ことを意味しています。
Payara(GlassFish 由来)は、サーバドメインごとに domain.xml という設定ファイルを持っています。これが無いとサーバは起動できません。
どう直すか
1️⃣ 新しいドメインを作成する
コマンドラインから作るのが安全です。
cd C:\Payara\payara6\bin
asadmin create-domain --adminport 4848 <ドメイン名>
2️⃣ サーバを起動
新しいドメインを作ったら:
asadmin start-domain <ドメイン名>
3️⃣ IDE での再設定
NetBeans/Eclipse でサーバ設定が消えている可能性があります
サーバ一覧に新しい domain1 を追加して、プロジェクトのデプロイ先として指定してください
ドメインの削除に失敗
CLI130: Could not create domain, domain1 Command create-domain failed.
原因と対策
1️⃣ 既存の domain1 がまだ残っている
Payara は同名のドメインを作れません。
なので手動で残っているドメインを削除する必要があります。
削除後、下記のコマンドを実行します。
asadmin create-domain --adminport 4848 <ドメイン名>







