はじめに
とりあえず Spring JPA を触って動かすだけのサンプルです。
JPA を使用して、一対多、多対多の Entity(テーブル)を作成します。
サンプルを作成する環境は、Windows ですが、Mac でも何ら変わりません。
DBMS は MySQL を使用します。
<参考サイト>
・デフォルトマッピング(双方向のリレーションシップ)
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 のインストールや操作方法などの詳しいことは、こちらのサイトを参照してください。
1-2. プロジェクトの作成
サンプル用に、プロジェクトを作成します。
名前(Name)は適当に「JpaSample」としておきました。
ビルドツールは「Maven」、Java バージョンは「11」ですが、このあたりも適当で大丈夫です。
ライブラリは、「Spring Data JPA」、「Spring Web」、「Thymeleaf」、「MySQL Driver」を選択しておきます。
2. 最低限のサンプル
最初に、エンティティ(≒ テーブル)を1つ作るだけの簡素なサンプルです。
application.properties
(既にあるファイル)には、データベース接続などに必要な事項を記載します。
User.java
(新ファイル作成)には、エンティティの定義を記述します。
2-1. application.properties のコード
データベース名
(spring_jpa)、username
(yama3)、passwprd
(123456)は、手元の環境に合わせて記載してください。
あとは、そのままで大丈夫です。
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テーブルを作成する
2-2. User.java のコード
設定するカラム(フィールド変数)は、id、name の2つだけです。
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 には原則として空のコンストラクタ
が必要になるので作成しておきます(参考記事)。なお、このサンプルではコンストラクタがなくとも動きます。
Getter
、Setter
も必要となるので、STS の自動生成などを使用して作成しておきます。
2-3. プロジェクトの実行
プロジェクトを実行します。
コンソールの赤枠のところで、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. 一対多のサンプル
上記のように、user
と message
が、一対多の関係となる形でエンティティを作成します。
message
エンティティの user_id
が外部キーとなり、user
エンティティの主キーである id
と紐付くことになります。
これは、ユーザーが複数のメッセージを投稿できるというような場合の関係になります。
3-1. Message.java のコード
Message.java というクラスファイルを作って次のように Message エンティティを作成します。
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 エンティティは、次のように作成(修正)します。
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 エンティティは、1つの User エンティティと結びつくため、@ManyToOne アノテーションを使用します。
フィールド変数として User クラスを単数で指定します。
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@JoinColumn では、name = "user_id"
と指定しています。
これは、データベース上のカラム名として反映されます。
3-3-2. @OneToMany
User エンティティは、複数の Message エンティティと結びつくため、@OneToMany アノテーションを使用します。
フィールド変数として Message クラスをList型(複数)で指定します。
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Message> messages;
mappedBy = "user"
で指定している user
は、Message から参照する際に使用されます。
例えば、Spring 式言語(SpEL)では、message.user.user_id
という形で記述されます。
3-4. プロジェクトの実行
プロジェクトを実行します。
作成されたテーブルを確認します。
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. 多対多のサンプル
上記のように、user
と message
が、多対多となる関係も追加します。
多対多の関係を持たせるために、user
エンティティの主キーと、message
エンティティの主キーの2つを外部キーとして持つ結合テーブル(message_user)を作成します。
このような関係は、ユーザーがメッセージに対して「いいね」ボタンを押す機能を実装するような場合に使用できます。
- user は複数のメッセージに対して「いいね」ボタンを押すことができる
- message は複数のユーザーから「いいね」ボタンを押されることができる
なお、結合テーブルの Entity は作成しませんが、データベースのテーブルは作成されます。
4-1. Message.java のコード
Message エンティティは、次のように作成(修正)します。
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 エンティティは、次のように作成(修正)します。
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 エンティティ側に、結合テーブルの定義を記載しています。
@ManyToMany
@JoinTable(name="message_user", joinColumns = @JoinColumn( name = "message_id"),
inverseJoinColumns = @JoinColumn(name="user_id"))
private List<User> userList;
@ManyToMany( mappedBy = "userList")
private List<Message> messagelist;
結合テーブルは、所有者側のエンティティで @JoinTable
を使用して定義します。
項目 | 内容 |
---|---|
name | 結合テーブルの名前を指定 |
joinColumns | 所有者エンティティへの外部キーのカラム名を指定 |
inverseJoinColumns | 非所有者エンティティへの外部キーのカラム名を指定 |
mappedBy = "userList"
で指定している userList
は、Message から参照する際に使用されます。
4-4. プロジェクトの実行
プロジェクトを実行します。
作成されたテーブルを確認します。
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 テーブル(結合テーブル)が作成されていることが確認できます。
<参考サイト>
・デフォルトマッピング(双方向のリレーションシップ)
・@JoinTable