【初心者向け】Spring JPA の Entity 作成のサンプル

# はじめに

とりあえず Spring JPA を触って動かすだけのサンプルです。
JPA を使用して、一対多、多対多の Entity（テーブル）を作成します。

サンプルを作成する環境は、Windows ですが、Mac でも何ら変わりません。
DBMS は MySQL を使用します。

＜参考サイト＞
・[デフォルトマッピング（双方向のリレーションシップ）](http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0870/APKC/EU070313.HTM)

# 1. サンプル作成の前準備

## 1-1. データベースの用意

コマンドプロンプトから MySQL を開いて「spring_jpa」というデータベースを作成します（データベース名は何でも構いません）。

```:データベースの作成
mysql> create database spring_jpa;
Query OK, 1 row affected (0.25 sec)
```

次に、アクセス用のユーザーを作成します。
以下は、ユーザー名「yama3」、パスワード「123456」とする場合の例です。

```:ユーザーの作成
mysql> create user 'yama3'@'localhost' identified by '123456';
Query OK, 0 rows affected (1.41 sec)
```

作成したユーザーにデータベース（spring_jpa）に対する全ての権限を与えておきます。

```:ユーザーの権限を設定
mysql> grant all on spring_jpa.* to 'yama3'@'localhost';
Query OK, 0 rows affected (0.32 sec)
```

MySQL のインストールや操作方法などの詳しいことは、[こちらのサイト](https://www.dbonline.jp/mysql/)を参照してください。

## 1-2. プロジェクトの作成

サンプル用に、プロジェクトを作成します。
名前（Name）は適当に「JpaSample」としておきました。
ビルドツールは「Maven」、Java バージョンは「11」ですが、このあたりも適当で大丈夫です。
![2021-08-09 101310.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/f401b98d-411c-5f54-1db0-f15ad977f34d.png)
ライブラリは、「Spring Data JPA」、「Spring Web」、「Thymeleaf」、「MySQL Driver」を選択しておきます。
![2021-08-09 110238.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/1f2200aa-5db1-8b43-f6e3-d217e6649bdf.png)

# 2. 最低限のサンプル

最初に、エンティティ（≒ テーブル）を１つ作るだけの簡素なサンプルです。
`application.properties`（既にあるファイル）には、データベース接続などに必要な事項を記載します。
`User.java`（新ファイル作成）には、エンティティの定義を記述します。
![2021-08-09 113646.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/1b90c2c4-136e-1964-0806-acc2044e5bbe.png)

## 2-1. application.properties のコード

`データベース名`（spring_jpa）、`username`（yama3）、`passwprd`（123456）は、手元の環境に合わせて記載してください。
あとは、そのままで大丈夫です。

```/src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/spring_jpa
spring.datasource.username=yama3
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

spring.thymeleaf.cache=false
```

＜参考＞
[MyISAMの代わりにMysql InnoDBテーブルを作成する](https://www.it-swarm-ja.com/ja/java/hibernate%EF%BC%9Amyisam%E3%81%AE%E4%BB%A3%E3%82%8F%E3%82%8A%E3%81%ABmysql-innodb%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B/967092461/)

## 2-2. User.java のコード

設定するカラム（フィールド変数）は、id、name の２つだけです。

```/src/main/java/com/example/demo/User.java
package com.example.demo;

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

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(name = "user_name", nullable = false, length = 50)
    private String userName;
    
    // 空のコンストラクタ
    public User() {}
    
    // Getter、Setter
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}
```

### 2-2-1. アノテーション

使用しているアノテーションは次のとおりです。

|アノテーション|内容|
|---|---|
|＠Entity|Entity クラスであることを示す|
|＠Id|主キーであることを示す（Unique、Not Null も付与）|
|＠GeneratedValue|値を自動生成する（= Auto Increment）|
|＠Column|カラムの設定を記述（カラム名、Not Null、サイズ など）|

`＠GeneratedValue` の GenerationType には、AUTO、IDENTITY などがありますが、`IDENTITY` を使用しておくのが無難です。

**＠Columnの指定内容**

|項目|内容|
|---|---|
|name|データベースでのカラム名を指定（無ければ変数名から生成）|
|nullable|Null を許可するか否かの指定|
|length|データのサイズを指定|

`name` を書かなければ、フィールド変数名がそのままデータベースのカラム名になります。
例えば、変数名が「userName」であれば、「user_name」とスネークケースに自動的に変換されます（つまり、上記サンプルコードでは、`name` の指定をしなくとも同じ結果になります）。

### 2-2-2. コンストラクタ、Getter、Setter

JPA には原則として`空のコンストラクタ`が必要になるので作成しておきます（[参考記事](https://www.it-swarm-ja.com/ja/java/java-entity%E3%81%AA%E3%81%9C%E7%A9%BA%E3%81%AE%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AA%E3%81%AE%E3%81%A7%E3%81%99%E3%81%8B%EF%BC%9F/1040768695/)）。なお、このサンプルではコンストラクタがなくとも動きます。

`Getter`、`Setter` も必要となるので、STS の自動生成などを使用して作成しておきます。

## 2-3. プロジェクトの実行

プロジェクトを実行します。
![2021-08-09 122604.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/edd0489c-b352-2565-ce8d-5126d6dfb2b6.png)
コンソールの赤枠のところで、CREATE TABLE 文が実行されて、作成した `User エンティティ` の内容に沿ったテーブルが自動で作成されます。

コマンドプロンプトから作成されたテーブルを確認すると次のようになっています。

```:コマンドプロンプト
mysql> show create table user\G
*************************** 1. row ***************************
       Table: user
Create Table: CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.06 sec)
```

# 3. 一対多のサンプル

![2021-08-09 140123.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/6f92137a-de4f-b32f-59a0-3673b9127d00.png)
上記のように、`user` と `message` が、一対多の関係となる形でエンティティを作成します。
`message` エンティティの `user_id` が外部キーとなり、`user` エンティティの主キーである `id` と紐付くことになります。

これは、ユーザーが複数のメッセージを投稿できるというような場合の関係になります。

## 3-1. Message.java のコード

Message.java というクラスファイルを作って次のように Message エンティティを作成します。

```/src/main/java/com/example/demo/Message.java
package com.example.demo;

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

@Entity
public class Message {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer messageId;
    
    private String comment;
    
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    public Message() {}

    public Integer getMessageId() {
        return messageId;
    }
    public void setMessageId(Integer messageId) {
        this.messageId = messageId;
    }
    public String getComment() {
        return comment;
    }
    public void setComment(String comment) {
        this.comment = comment;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
}
```

## 3-2. User.java のコード

User エンティティは、次のように作成（修正）します。

```/src/main/java/com/example/demo/User.java
package com.example.demo;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(name = "user_name", nullable = false, length = 50)
    private String userName;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Message> messages;
    
    public User() {}
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public List<Message> getMessages() {
        return messages;
    }
    public void setMessages(List<Message> messages) {
        this.messages = messages;
    }
}
```

## 3-3. 簡単な説明

### 3-3-1. ＠ManyToOne

Message エンティティは、１つの User エンティティと結びつくため、＠ManyToOne アノテーションを使用します。
フィールド変数として User クラスを単数で指定します。


```java:Messageエンティティ
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
```
＠JoinColumn では、`name = "user_id"` と指定しています。
これは、データベース上のカラム名として反映されます。

### 3-3-2. ＠OneToMany

User エンティティは、複数の Message エンティティと結びつくため、＠OneToMany アノテーションを使用します。
フィールド変数として Message クラスをList型（複数）で指定します。


```java:Userエンティティ
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Message> messages;
```

`mappedBy = "user"` で指定している `user` は、Message から参照する際に使用されます。
例えば、Spring 式言語（SpEL）では、`message.user.user_id` という形で記述されます。

# 3-4. プロジェクトの実行

プロジェクトを実行します。

![2021-08-09 143741.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/0cf78789-6408-bd8c-cb5d-2b99b53a8017.png)

作成されたテーブルを確認します。

```:コマンドプロンプト
mysql> show create table message\G
*************************** 1. row ***************************
       Table: message
Create Table: CREATE TABLE `message` (
  `message_id` int NOT NULL AUTO_INCREMENT,
  `comment` varchar(255) DEFAULT NULL,
  `user_id` int DEFAULT NULL,
  PRIMARY KEY (`message_id`),
  KEY `FKb3y6etti1cfougkdr0qiiemgv` (`user_id`),
  CONSTRAINT `FKb3y6etti1cfougkdr0qiiemgv` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.03 sec)
```

次のところで、外部キーが設定されていることが確認できます。

```:抜粋
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
```

# 4. 多対多のサンプル

![2021-08-09 145536.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/16222750-a9dd-ad8e-4116-f1e74a16df9d.png)
上記のように、`user` と `message` が、多対多となる関係も追加します。
多対多の関係を持たせるために、`user` エンティティの主キーと、`message` エンティティの主キーの２つを外部キーとして持つ結合テーブル（message_user）を作成します。

このような関係は、ユーザーがメッセージに対して「いいね」ボタンを押す機能を実装するような場合に使用できます。

- user は複数のメッセージに対して「いいね」ボタンを押すことができる
- message は複数のユーザーから「いいね」ボタンを押されることができる

なお、結合テーブルの Entity は作成しませんが、データベースのテーブルは作成されます。

## 4-1. Message.java のコード

Message エンティティは、次のように作成（修正）します。

```/src/main/java/com/example/demo/Message.java
package com.example.demo;

import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;

@Entity
public class Message {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer messageId;
    
    private String comment;
    
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToMany
    @JoinTable(name="message_user", joinColumns = @JoinColumn( name = "message_id"),
            inverseJoinColumns = @JoinColumn(name="user_id"))
    private List<User> userList;

    public Message() {}

    public Integer getMessageId() {
        return messageId;
    }
    public void setMessageId(Integer messageId) {
        this.messageId = messageId;
    }
    public String getComment() {
        return comment;
    }
    public void setComment(String comment) {
        this.comment = comment;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public List<User> getUserList() {
        return userList;
    }
    public void setUserList(List<User> userList) {
        this.userList = userList;
    }
}
```

## 4-2. User.java のコード

User エンティティは、次のように作成（修正）します。

```/src/main/java/com/example/demo/User.java
package com.example.demo;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(name = "user_name", nullable = false, length = 50)
    private String userName;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Message> messages;
    
    @ManyToMany( mappedBy = "userList")
    private List<Message> messagelist;    

    public User() {}
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public List<Message> getMessages() {
        return messages;
    }
    public void setMessages(List<Message> messages) {
        this.messages = messages;
    }
    public List<Message> getMessagelist() {
        return messagelist;
    }
    public void setMessagelist(List<Message> messagelist) {
        this.messagelist = messagelist;
    }
}
```

## 4-3. 簡単な説明

### 4-3-1. ＠ManyToMany

複数の Message エンティティが、複数の User エンティティと結びつくため、＠ManyToMany アノテーションを使用します。

この多対多の関係の所有者を Message エンティティとみなして、 Message エンティティ側に、結合テーブルの定義を記載しています。

```java:Messageエンティティ
    @ManyToMany
    @JoinTable(name="message_user", joinColumns = @JoinColumn( name = "message_id"),
            inverseJoinColumns = @JoinColumn(name="user_id"))
    private List<User> userList;
```

```java:Userエンティティ
    @ManyToMany( mappedBy = "userList")
    private List<Message> messagelist;    
```

結合テーブルは、所有者側のエンティティで `＠JoinTable` を使用して定義します。

|項目|内容|
|---|---|
|name|結合テーブルの名前を指定|
|joinColumns|所有者エンティティへの外部キーのカラム名を指定|
|inverseJoinColumns|非所有者エンティティへの外部キーのカラム名を指定|

`mappedBy = "userList"` で指定している `userList` は、Message から参照する際に使用されます。

## 4-4. プロジェクトの実行
プロジェクトを実行します。
![2021-08-09 153137.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/03de507c-521f-539c-929c-447ff381633c.png)
作成されたテーブルを確認します。

```:コマンドプロンプト
mysql> show create table message_user\G
*************************** 1. row ***************************
       Table: message_user
Create Table: CREATE TABLE `message_user` (
  `message_id` int NOT NULL,
  `user_id` int NOT NULL,
  KEY `FKkqjauebfx5xkymdbpv26seckx` (`user_id`),
  KEY `FKfh34sth5igv111to5girgymqu` (`message_id`),
  CONSTRAINT `FKfh34sth5igv111to5girgymqu` FOREIGN KEY (`message_id`) REFERENCES `message` (`message_id`),
  CONSTRAINT `FKkqjauebfx5xkymdbpv26seckx` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
```
意図した形で、message_user テーブル（結合テーブル）が作成されていることが確認できます。

＜参考サイト＞
・[デフォルトマッピング（双方向のリレーションシップ）](http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0870/APKC/EU070313.HTM)
・[@JoinTable](http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0870/APR4/EU260078.HTM)