45
54

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

spring boot + spring jpaでデータベースに接続しCRUD操作

Posted at

はじめに

環境

  • macOS Mojave 10.14.6
  • Java openjdk 12.0.1
  • Spring Boot 2.1.7
  • Intellij IDEA CE 2019.1
  • docker 18.09.2
  • docker-compose 1.24.1

取り扱うデータ

  • 簡易版ツイッターのバックエンドサーバのようなイメージで、以下のデータを扱います
項目名 説明
id int ツイートに固有の ID
name String ツイートした人の名前
message String ツイート内容

作成する api のイメージ

url HTTP メソッド 説明
/tweet POST ツイートを保存する
/tweet GET 全ツイートを取得する
/tweet/{id} PUT 指定した id のツイートを更新する
/tweet/{id} DELETE 指定した id のツイートを削除する

パッケージ構成

  • 今回は以下のようなパッケージ構成でコードを作成していきます
src/main/java/com/example/api
.
├── ApiApplication.java
├── model
│   └── Tweet.java
├── repository
│   └── TweetRepository.java
├── service
│   └── TweetService.java
└── controller
    └── TweetController.java
  • model : エンティティ(取り扱うデータそのもの)を表す
  • repository : DB へのアクセスを行う
  • service : ビジネスロジックを実行する
  • controller : クライアントからのリクエスト/レスポンスを処理する

build.gradle の設定

  • データベースとデータのやり取りを行いやすくするためのライブラリを追加します
  • build.gradle の dependencies に以下を追加します
    • org.springframework.boot:spring-boot-starter-data-jpa
      RDB を java で操作するための api(JPA : Java Persistence API)を提供するライブラリ
    • mysql:mysql-connector-java
      java で mysql データベースに接続するための api(JDBC : Java Database Connectivity)を提供するライブラリ
    • org.projectlombok:lombok
      java で getter / setter やコンストラクタ等のよく使うコードを自動生成してくれるライブラリ
      Lombok メモ も参照ください
build.gradle
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'mysql:mysql-connector-java'

	compileOnly 'org.projectlombok:lombok:1.18.8'
	annotationProcessor 'org.projectlombok:lombok:1.18.8'

DB の接続設定

  • src/main/resourcesの下に、application.properties または application.yml を作成し、接続する DB の情報を記載します
application.yml
spring:
  datasource:
    sqlScriptEncoding: UTF-8
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/testdb?useSSL=false&requireSSL=false
    username: root
    password: hoge
  # アプリケーション起動時にデータベースを初期化しないようにする
  jpa:
    hibernate:
      ddl-auto: update
    # spring boot起動時にDBにテーブル作成できない問題の対応
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL57Dialect
logging:
  level:
    sql: debug
  • spring.jpa.properties.hibernate.dialect の設定を入れているのはアプリケーション起動時にテーブルを作成できずに以下のようなエラーが発生したためです。こちらを参考に解決しました
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table tweet (tweet varchar(255) not null, primary key (tweet)) engine=MyISAM" via JDBC Statement
...(略)...
Caused by: java.sql.SQLSyntaxErrorException: Specified key was too long; max key length is 1000 bytes
...(略)...

Create / Read

  • まずは tweet をデータベースに保存/取得する機能を実装します
  • Create 時はリクエストボディ内の namemessage を DB に保存し、保存した内容をそのままレスポンスとして返します
  • Get 時は DB に格納されているデータを全て取得し、レスポンスとして返します

model の作成

  • model パッケージ配下に、ツイート自体を表すクラスを作成します
Tweet.java
package com.example.api.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "tweet")
public class Tweet {
    @Id
    @GeneratedValue
    private int id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String message;
}
  • @Entity : JPA のエンティティ(DB に保存するオブジェクト)であることを表す
  • @Data / @NoArgsConstructor / @AllArgsConstructor : lombok のアノテーション。getter, setter, コンストラクタなどを自動作成する
  • @Table(name = "tweet") : エンティティに対応するテーブル名を指定する
  • @Id : エンティティの主キーであることを表すアノテーション
  • @GeneratedValue : これをつけると DB で自動採番されるようになる
  • @Column(nullable = false) : DB のカラムに付与するアノテーション。nullable オプションで null を許容するかどうかを設定できる

repository の作成

  • repository パッケージ配下にリポジトリクラスを作成します
  • CRUD 操作に必要なメソッドは JpaRepository に含まれているため、JpaRepository を継承したインターフェースを作成するだけで OK です
TweetRepository.java
package com.example.api.repository;

import com.example.api.model.Tweet;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TweetRepository extends JpaRepository<Tweet, Integer> {}

service の作成

  • service パッケージ配下にサービスクラスを作成します
  • tweetRepository に @Autowired を付けることでインスタンスが DI コンテナから渡され、new することなくメソッドを呼び出せるようになります
  • postTweet メソッドで、Tweet を受け取り、tweetRepository の save メソッドを呼び出しています
  • getTweet メソッドで、tweetRepository の findAll メソッドを呼び出しています。
TweetService.java
package com.example.api.service;

import com.example.api.model.Tweet;
import com.example.api.repository.TweetRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class TweetService {
    @Autowired
    TweetRepository tweetRepository;

    public Tweet postTweet(Tweet tweet) {
        return tweetRepository.save(tweet);
    }

    public List<Tweet> getTweet() {
        return tweetRepository.findAll();
    }
}
  • @Service : spring MVC のサービスクラスであることを表す。DI (Dependency Injection) の対象となる
  • @Transactional : トランザクション制御を行うためのアノテーション
  • @Autowired : DI コンテナのインジェクション対象に付けるアノテーション

controller の作成

  • controllerパッケージ配下にコントローラクラスを作成します
  • @RequestMapping()アノテーションを使い、tweetというパスに対して POST メソッドで呼び出された場合は postTweet メソッドを、GET メソッドで呼び出された場合は getTweet メソッドが呼び出されるようにしています
  • postTweet メソッドでは @RequestBody アノテーションを使って HTTP リクエストのボディを Tweet オブジェクトにマッピングし、tweetService の postTweet メソッドに渡しています。tweetService.postTweet()の実行後、結果を返却しています
TweetController.java
package com.example.api.controller;

import com.example.api.model.Tweet;
import com.example.api.service.TweetService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("tweet")
public class TweetController {
    @Autowired
    TweetService tweetService;

    @RequestMapping(method = RequestMethod.POST)
    Tweet postTweet(@RequestBody Tweet tweet) {
        return tweetService.postTweet(tweet);
    }

    @RequestMapping(method = RequestMethod.GET)
    List<Tweet> getTweet() {
        return tweetService.getTweet();
    }
}
  • @RestController : コントローラクラスに付与するアノテーション
  • @RequestMapping() : HTTP リクエストを受け付けるためのアノテーション。リクエストを受け付けるパスや HTTP メソッドを指定する
  • @RequestBody : リクエストのボディを指定したオブジェクトにマッピングするアノテーション

動作確認

  • アプリケーションを起動し、ターミナルから以下のコマンドを実行します
# post request
$ curl -X POST -H 'Content-Type:application/json' -d '{"name":"名前", "message":"メッセージ"}' localhost:8080/tweet

# response
{"id":1,"name":"名前","message":"メッセージ"}
  • データベースを確認し、リクエストした内容が保存されていれば OK です
mysql> select * from tweet;
+----+-----------------+--------+
| id | message         | name   |
+----+-----------------+--------+
|  1 | メッセージ      | 名前   |
+----+-----------------+--------+
1 row in set (0.01 sec)
  • 次に、データベースの内容が取得できるか確認するため、ターミナルから以下のコマンドを実行します
# get request
curl -X GET -H 'Content-Type:application/json' localhost:8080/tweet

# response
[{"id":1,"name":"名前","message":"メッセージ"}]
  • データベース内のレコードを取得できました!

Update / Delete

  • 続いて、/tweet/{id} で指定された id の tweet を更新/削除する機能を実装します
  • JpaRepository には update/delete に用いるメソッドが既に実装されているため、repository の修正は不要です。(service から呼び出すだけ)

service の修正

  • TweetService.java のクラス内に以下のメソッドを追加します
TweetService.java
    public Tweet updateTweet(Tweet tweet) {
        return tweetRepository.save(tweet);
    }

    public void deleteTweet(Integer id) {
        tweetRepository.deleteById(id);
    }
  • update は create と同じ save()メソッドを用います
  • delete は id で指定したエンティティを削除する deleteById() メソッドを用います

controller の修正

  • TweetController.java のクラス内に以下のメソッドを追加します
TweetController.java
    @RequestMapping(value = "{id}", method = RequestMethod.PUT)
    Tweet putTweet(@PathVariable("id") Integer id, @RequestBody Tweet tweet) {
        tweet.setId(id);
        return tweetService.updateTweet(tweet);
    }

    @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
    void deleteTweet(@PathVariable("id") Integer id) {
        tweetService.deleteTweet(id);
    }
  • @RequestMapping の value オプションに {id} を指定し、/tweet/{id} でリクエストされた場合に該当のメソッドが呼ばれるように設定します
  • @PathVariable アノテーションで URL 内の変数 {id} を Integer の id という変数にマッピングします
  • 更新時は、id を setTweet()メソッドでセットしてから tweetService の updateTweet()メソッドに渡します

動作確認

  • アプリケーションを起動し、ターミナルから以下のコマンドを実行します
# put request
$ curl -X PUT -H 'Content-Type:application/json' -d '{"name":"更新後の名前", "message":"更新後のメッセージ"}' localhost:8080/tweet/1

# response
{"id":1,"name":"更新後の名前","message":"更新後のメッセージ"}
  • データベースを確認し、データが更新されていることを確認します
mysql> select * from tweet;
+----+-----------------------------+--------------------+
| id | message                     | name               |
+----+-----------------------------+--------------------+
|  1 | 更新後のメッセージ          | 更新後の名前       |
+----+-----------------------------+--------------------+
1 row in set (0.01 sec)
  • 最後に、データの削除を確認するため、ターミナルから以下のコマンドを実行します
# delete request
curl -X DELETE -H 'Content-Type:application/json' localhost:8080/tweet/1

# responseは無し
  • データベースを確認し、データが削除されていれば OK です!
mysql> select * from tweet;
Empty set (0.00 sec)

参考

45
54
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
54

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?