結論

エンティティとは、1件ずつ見分けて管理する対象のことです。分野によって指すものが変わります。

この記事でわかること

  • 8分野の「エンティティ」の意味の見分け表
  • 例題4エンティティの洗い出し→ER図→SQLの全工程
  • 制約エラー5件・Java の同一性判定・HTML変換の実物

2026年10月22日更新|PostgreSQL 16.15・OpenJDK 21・Python 3.11 で実行確認

エンティティは、現実の「もの・こと」を、データとして1件ずつ見分けられる単位にしたものです。ER図では箱、データベースではテーブル、Java ではクラスとして表れます。

flowchart TD
    A[現実の対象<br>社員・本・貸出] --> B[エンティティ<br>1件ずつ見分ける単位]
    B -->|図| C[ER図の箱]
    B -->|DB| D[テーブル<br>1行が1件]
    B -->|Java| E[Entity<br>クラス]
    D --- F[主キー<br>1件を<br>見分けるID]

「エンティティ」は分野で意味が変わります。どの分野の話かは、一緒に出てくる言葉で見分けます。

分野 エンティティが指すもの 一緒に出てくる言葉
データベース・ER図 管理する対象(社員・注文など) 属性・リレーション・主キー
ドメイン駆動設計(DDD) IDで同一性を持つオブジェクト 値オブジェクト・集約
JPA・ORM テーブルと対応するクラス @Entity・@Id・Hibernate
HTML・XML &amp; のような文字参照 エスケープ・文字化け
自然言語処理 文中の人名・地名・組織名 固有表現抽出・NER
SEO Google が見分ける人・場所・もの ナレッジグラフ
会計・法務 法人・事業体 連結・法人番号
ゲーム開発 画面の中の1つ1つの物体 ECS・コンポーネント

確認環境:Ubuntu 24.04・PostgreSQL 16.15(例題の CREATE TABLE・INSERT と制約エラーを実行)・OpenJDK 21.0.10(Jakarta Persistence 3.2.0 の API を GitHub のタグ 3.2-3.2.0-RELEASE のソースからビルドし、エンティティクラスをコンパイル。PostgreSQL JDBC 42.7.8 で読み込み)・Python 3.11.15(html モジュール)・Chromium 141(HTML の文字参照の解釈)・GiNZA 5.3.0(spaCy 3.8.16、固有表現抽出)。エンティティの定義は Jakarta Persistence 3.2 仕様、Eric Evans「Domain-Driven Design Reference」、Peter Chen の ER モデルの論文、Mermaid・Spring Boot・Google のドキュメントで確かめました(確認日 2026年10月22日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「エンティティとは|意味・ER図・設計手順をDBとJavaで実例解説」

エンティティとは:ITでの意味

この節では、エンティティの基本の意味と、分野をまたいで共通する考え方が分かります。

エンティティ(entity)は、英語で「実体・存在するもの」という意味の言葉です。IT では「ほかと区別して1件ずつ扱える対象」を指します。 読み方は「エンティティ」、複数形は entities(エンティティーズ)です。データベースの分野では「実体」と訳されます。

データベースの分野でこの言葉を広めたのは、Peter Chen が 1976年に発表した ER モデル(Entity-Relationship Model、実体関連モデル)の論文です。英語版 Wikipedia は、エンティティを「独立して存在でき、一意に見分けられ、データを持てるもの」とまとめています。

分野ごとに指すものは違っても、共通するのは次の2つです。

1何を1件と数えるか

社員1人、本1冊、貸出1回。何を「1件」とするかを決めたものがエンティティです。「本」をタイトル単位で数えるか、棚にある1冊ずつで数えるかで、エンティティは別のものになります。

21件を何で見分けるか

社員番号、ISBN と冊番号、貸出番号。ほかの1件と取り違えないための印(識別子)があることが、エンティティの条件です。名前が同じ社員が2人いても、社員番号が違えば別の1件です。

一般の英語・ビジネスでの意味

一般の英語の entity は「存在するもの・独立した組織」という意味です。ビジネスや会計では、会社・支社・子会社などの「法人・事業体」を指して legal entity(法的な実体)と言います。システム開発の会話で「エンティティ」と言えば、ほとんどの場合はデータベースか DDD の意味です。

エンティティとエンティティタイプ

厳密には、「社員」という種類をエンティティタイプ、社員の佐藤さん1人をエンティティ(インスタンス)と呼び分けます。種類の集まりをエンティティセットとも言います。ふだんの設計の会話では、どれも「エンティティ」と呼ぶことが多く、ER図に箱として描くのはエンティティタイプです。

なぜエンティティを決めるのか

この節では、システムを作る前にエンティティを決める理由が分かります。

エンティティは、データベースのテーブル、画面、API、クラスの土台になります。最初に決め間違えると、後の全部に影響します。

1データの重複と食い違いを防ぐ

社員の名前を貸出の記録ごとに書くと、名前が変わったときに全部の行を直すことになります。社員を1つのエンティティにまとめれば、直すのは1行です。

2業務の言葉をそろえる

「本」が1タイトルなのか1冊なのかを決めると、利用者と開発者の会話が食い違いません。エンティティ名は、そのまま共通の用語集になります。

3ルールをデータベースに守らせる

主キー・外部キー・CHECK 制約は、エンティティとその関係が決まって初めて書けます。後の節で、5種類の誤ったデータを PostgreSQL が拒否する様子を見ます。

エンティティの属性とリレーションシップ(ER図の基本)

この節では、ER図に出てくる言葉(属性・識別子・リレーションシップ・カーディナリティ)と描き方が分かります。

ER図は、エンティティとその関係を描く図です。登場する要素は4つです。

要素 意味 例題での例
エンティティ 管理する対象 社員・書籍・蔵書・貸出
属性(アトリビュート) エンティティが持つ項目 社員の名前・メールアドレス
識別子(主キー) 1件を見分ける属性 社員番号、ISBN+冊番号
リレーションシップ エンティティどうしの関係 社員が貸出を「する」

属性とエンティティの違いは、「それ自体を1件ずつ管理するか」です。 社員の「メールアドレス」は社員に付く値なので属性です。一方で「部署」に部署名・内線番号・部長などの情報があり、部署ごとに管理するなら、部署は属性ではなくエンティティにします。

カーディナリティ(1対多・多対多)

リレーションシップには「相手が何件つながるか」があります。これをカーディナリティ(多重度)と呼びます。ER図の記法の1つである IE 記法(鳥の足のような線の先)では、線の端の記号で表します。Mermaid の erDiagram の記号と意味は次のとおりです(Mermaid ドキュメントの表)。

左側の記号 右側の記号 意味
|o o| 0か1
|| || ちょうど1
}o o{ 0以上
}| |{ 1以上

関係の種類は、1対1・1対多・多対多の3つです。多対多(学生と授業など)は、そのままではテーブルにできないので、間に「履修」のようなエンティティを置いて1対多を2本に分けます。

強エンティティと弱エンティティ

弱エンティティは、自分の属性だけでは1件を見分けられず、親のキーと組み合わせて初めて見分けられるエンティティです。 例題の「蔵書」は、冊番号1・2だけではどの本の1冊目か分かりません。ISBN(親の書籍のキー)と冊番号を組み合わせて初めて1件が決まります。親のキーを自分の主キーに含む関係を識別関係、含まない関係を非識別関係と呼びます。

種類 主キー Mermaid の線 例題での例
強エンティティ 自分の属性だけで決まる — 社員・書籍・貸出
弱エンティティ 親のキー+自分の属性 --(実線・識別関係) 蔵書(ISBN+冊番号)
親のキーを属性として持つだけ 自分の属性だけで決まる ..(点線・非識別関係) 貸出の社員番号

ER図の記法の違い

ER図には描き方の流派がいくつかあります。同じ内容を、記号の違いで描き分けます。

記法 エンティティ 多重度の表し方 主に使われる場面
Chen 記法 四角(属性は楕円、関係はひし形) 線に 1・N・M を書く 教科書・概念の説明
IE 記法 四角(中に属性を書く) 線の端の ○・|・鳥の足 業務システムの設計・Mermaid
IDEF1X 記法 角の形で強弱を区別 線の端の黒丸と文字 設計ツール

記号の読み方と描き方は、関連記事「ER図の書き方|記号の読み方からCREATE TABLEまで実行して確認」で扱っています。

エンティティとテーブル・インスタンス・クラスの違い

この節では、混同しやすい用語との違いが分かります。

エンティティは「何を管理するか」という設計上の考え(論理)で、テーブルはそれをデータベースに作った入れ物(物理)です。 多くの場合は1対1に対応しますが、いつも同じではありません。

用語 何か エンティティとの関係
テーブル データベースに作る表 エンティティを物理的に作ったもの。多対多の中間表など、1つのエンティティが複数の表になることも、その逆もある
行(レコード) テーブルの1件 エンティティのインスタンス1件
列(カラム) テーブルの項目 属性を物理的に作ったもの
クラス プログラムの型 JPA ではエンティティクラスがテーブルに対応する
インスタンス・オブジェクト クラスから作った1つの実体 エンティティ1件をメモリ上に読み込んだもの
DTO 画面や API へ渡すためだけの入れ物 ID による同一性もライフサイクルも持たない
混同 1エンティティとテーブルは同じもの
→
実際エンティティは設計の単位、テーブルはそれを物理的に作ったもの

性能のために1つのエンティティを2つの表に分けることも、集計用に表を足すこともある

混同 2画面の項目をそのままエンティティにする
→
実際画面は複数のエンティティを組み合わせて表示している

「貸出一覧」の画面は、社員・書籍・蔵書・貸出の4つを結合して作る

混同 3同じ名前・同じ値なら同じエンティティ
→
実際エンティティは識別子(ID)で見分ける

後の Java の実行結果で、名前とメールが同じでも ID が違えば別と判定される

エンティティの洗い出し手順(例題で設計する)

この節では、要件の文章からエンティティを見つけ、ER図と CREATE TABLE まで作る手順が、例題の実物で分かります。

例題は「社内の書籍の貸し出し」です。要件は次の4文とします。

社員は、社内の本棚にある本を借りられる。
同じ本が複数冊ある場合がある。
1回の貸し出しでは1冊を借り、返却期限は貸した日から2週間後とする。
返したら返却日を記録する。同じ1冊を、同時に2人へ貸すことはできない。
1

要件の文から名詞を書き出す

業務の説明・帳票・画面の案・担当者への聞き取りから、名詞を残さず拾います。この時点では選ばずに全部書きます。

例題:社員・本棚・本・冊・貸し出し・返却期限・返却日

2

エンティティか属性かを分ける

「それ自体を1件ずつ管理し、複数の情報を持つか」で判定します。1つの値で済むものは属性です。

返却期限・返却日は貸出の属性、本棚は蔵書の属性

3

何を1件と数えるかを決める

「本」はタイトル単位(書籍)と1冊単位(蔵書)の2つに分かれます。「同じ本が複数冊ある」という文が手がかりです。

書籍1件に、蔵書が1件以上つながる

4

識別子(主キー)を決める

業務で使う番号があればそれを候補にし、変わらないこと・重複しないことを確かめます。無ければ連番を振ります。

社員番号・ISBN・ISBN+冊番号・貸出番号

5

関係と多重度を決め、ER図に描く

2つずつ組にして「片方1件に、もう片方は何件つながるか」を両方向から考えます。

社員1人に貸出0件以上、蔵書1冊に貸出0件以上

6

業務のルールを制約にして作る

CREATE TABLE を書き、外部キー・CHECK・UNIQUE で要件の文を守らせます。誤ったデータを入れて、拒否されることを確かめます。

「同時に2人へ貸せない」は部分的な UNIQUE で表す

名詞をエンティティと属性に分ける

手順1・2の結果です。判定の理由も残しておくと、後で見直すときに役立ちます。

名詞 判定 理由
社員 エンティティ(社員) 1人ずつ見分け、名前・メールを持つ
本 エンティティ(書籍) タイトル単位で、書名を持つ
冊 エンティティ(蔵書) 1冊ずつ貸し出す。同時に2人へ貸せないのは1冊単位
貸し出し エンティティ(貸出) 1回ずつ記録し、日付を3つ持つ
本棚 蔵書の属性 置き場所の名前だけ。本棚ごとに管理する情報が無い
返却期限・返却日 貸出の属性 貸出1回に1つずつ決まる値

名詞のうち「1件ずつ数えられ、自分の情報を複数持つもの」がエンティティの候補です。 本棚にも「担当者」「階数」などを持たせて管理したくなったら、そのときにエンティティへ昇格させます。

もう1つの見つけ方は、「もの」と「できごと」に分けて探すことです。社員・書籍・蔵書のように長く残る「もの」(マスタ)と、貸出のように日時と一緒に発生する「できごと」(トランザクション)を分けて探すと、できごとの側の漏れに気づきやすくなります。

エンティティ定義の書き方

洗い出したエンティティは、設計書では「エンティティ定義(エンティティ一覧)」として表にまとめます。決まった様式はありませんが、次の項目があると、テーブル定義へ進むときに迷いません。

エンティティ 説明・1件の単位 識別子 主な属性 発生するとき
社員 社員1人 社員番号 名前・メール 入社時に登録
書籍 本のタイトル1つ ISBN 書名 本を買うと決めたとき
蔵書 棚にある本1冊 ISBN+冊番号 本棚 1冊受け入れるたび
貸出 貸し出し1回 貸出番号 貸出日・返却期限・返却日 借りるたび

ER図(Mermaid)

手順5の結果を Mermaid の erDiagram で描きました。蔵書は書籍のキーを主キーに含むので実線(--、識別関係)、貸出は社員と蔵書のキーを属性として持つだけなので点線(..、非識別関係)です。

erDiagram
    BOOK ||--|{ BOOK_COPY : has
    BOOK_COPY ||..o{ LOAN : lent_as
    LOAN }o..|| EMPLOYEE : borrowed_by
    EMPLOYEE {
        int employee_id PK
        text name
        text email UK
    }
    BOOK {
        char isbn PK
        text title
    }
    BOOK_COPY {
        char isbn PK,FK
        int copy_no PK
        text shelf
    }
    LOAN {
        bigint loan_id PK
        int employee_id FK
        char isbn FK
        int copy_no FK
        date due_on
        date returned_on
    }

書いたコードは次のとおりです(描画結果は上の図)。

erDiagram
    BOOK ||--|{ BOOK_COPY : has
    BOOK_COPY ||..o{ LOAN : lent_as
    LOAN }o..|| EMPLOYEE : borrowed_by
    EMPLOYEE {
        int employee_id PK
        text name
        text email UK
    }
    (BOOK・BOOK_COPY・LOAN も同じ形で属性を書く)

CREATE TABLE と実行結果

ER図をそのまま SQL にします。要件の「返却期限は貸した日より後」は CHECK 制約、「同じ1冊を同時に2人へ貸せない」は返却日が空の行だけを対象にした UNIQUE インデックス(PostgreSQL の部分インデックス)で表しました。

CREATE TABLE employee (
  employee_id integer PRIMARY KEY,
  name        text NOT NULL,
  email       text NOT NULL UNIQUE
);
CREATE TABLE book (
  isbn  char(13) PRIMARY KEY,
  title text NOT NULL
);
CREATE TABLE book_copy (
  isbn    char(13) NOT NULL REFERENCES book(isbn),
  copy_no integer  NOT NULL,
  shelf   text     NOT NULL,
  PRIMARY KEY (isbn, copy_no)
);
CREATE TABLE loan (
  loan_id     bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  employee_id integer  NOT NULL REFERENCES employee(employee_id),
  isbn        char(13) NOT NULL,
  copy_no     integer  NOT NULL,
  loaned_on   date NOT NULL,
  due_on      date NOT NULL,
  returned_on date,
  FOREIGN KEY (isbn, copy_no) REFERENCES book_copy(isbn, copy_no),
  CHECK (due_on >= loaned_on)
);
CREATE UNIQUE INDEX loan_one_active_per_copy
  ON loan (isbn, copy_no) WHERE returned_on IS NULL;

社員2人・書籍2件・蔵書3冊・貸出2件を入れて、「いま誰が何を借りているか」と「書籍ごとの冊数」を出しました。

INSERT INTO employee VALUES (1, '佐藤', 'sato@example.com'), (2, '鈴木', 'suzuki@example.com');
INSERT INTO book VALUES ('9784000000001', 'SQL入門'), ('9784000000002', 'Java入門');
INSERT INTO book_copy VALUES ('9784000000001', 1, 'A-1'), ('9784000000001', 2, 'A-1'), ('9784000000002', 1, 'B-3');
INSERT INTO loan (employee_id, isbn, copy_no, loaned_on, due_on)
VALUES (1, '9784000000001', 1, '2026-10-22', '2026-11-05'),
       (2, '9784000000001', 2, '2026-10-22', '2026-11-05');
$ psql -d s283db -c "SELECT b.title, c.copy_no, e.name, l.due_on FROM loan l JOIN employee e USING (employee_id) JOIN book_copy c USING (isbn, copy_no) JOIN book b USING (isbn) WHERE l.returned_on IS NULL ORDER BY c.copy_no;"
  title  | copy_no | name |   due_on   
---------+---------+------+------------
 SQL入門 |       1 | 佐藤 | 2026-11-05
 SQL入門 |       2 | 鈴木 | 2026-11-05
(2 rows)

$ psql -d s283db -c "SELECT isbn, title, (SELECT count(*) FROM book_copy c WHERE c.isbn=b.isbn) AS copies FROM book b ORDER BY isbn;"
     isbn      |  title   | copies 
---------------+----------+--------
 9784000000001 | SQL入門  |      2
 9784000000002 | Java入門 |      1
(2 rows)

同じ「SQL入門」を2人が同時に借りられているのは、書籍と蔵書を別のエンティティに分けたからです。 「本」を1つのエンティティにしていたら、1タイトル1冊しか表せませんでした。

誤ったデータを入れて、制約が拒否するか確かめる

エンティティと関係を制約にしておくと、誤ったデータはデータベースが入口で止めます。5種類を試した結果(エラー原文)です。

試したこと 守っているルール 結果
いない社員3が借りる 外部キー(社員) 拒否
貸出中の1冊目を別の人が借りる 部分 UNIQUE 拒否
返却期限を貸出日より前にする CHECK 拒否
別の社員に同じメールを登録 UNIQUE 拒否
蔵書がある書籍を消す 外部キー(蔵書→書籍) 拒否
ERROR:  insert or update on table "loan" violates foreign key constraint "loan_employee_id_fkey"
DETAIL:  Key (employee_id)=(3) is not present in table "employee".

ERROR:  duplicate key value violates unique constraint "loan_one_active_per_copy"
DETAIL:  Key (isbn, copy_no)=(9784000000001, 1) already exists.

ERROR:  new row for relation "loan" violates check constraint "loan_check"
DETAIL:  Failing row contains (5, 2, 9784000000002, 1, 2026-10-22, 2026-10-01, null).

ERROR:  duplicate key value violates unique constraint "employee_email_key"
DETAIL:  Key (email)=(sato@example.com) already exists.

ERROR:  update or delete on table "book" violates foreign key constraint "book_copy_isbn_fkey" on table "book_copy"
DETAIL:  Key (isbn)=(9784000000001) is still referenced from table "book_copy".

1冊目を返却すると、同じ1冊を別の人が借りられるようになります。

$ psql -d s283db -c "UPDATE loan SET returned_on='2026-10-29' WHERE isbn='9784000000001' AND copy_no=1 RETURNING loan_id, returned_on;"
 loan_id | returned_on 
---------+-------------
       1 | 2026-10-29
(1 row)

UPDATE 1
$ psql -d s283db -c "INSERT INTO loan (employee_id, isbn, copy_no, loaned_on, due_on) VALUES (2, '9784000000001', 1, '2026-10-29', '2026-11-12') RETURNING loan_id;"
 loan_id 
---------
       6
(1 row)

INSERT 0 1

新しい貸出番号が 3 ではなく 6 なのは、拒否された3回の INSERT でも連番が進んだためです。IDENTITY の連番は「重ならないこと」を守る仕組みで、「欠番なく続くこと」は守りません。 業務で欠番の無い番号が要るなら、別の列で管理します。

主キーの選び方(業務の番号を使うか、連番を振るか)は、関連記事「主キー設計の考え方:サロゲートキーとナチュラルキーの違いと選び方」で扱っています。エンティティを分ける基準をさらに形式的に確かめる方法は、関連記事「データベースの正規化をわかりやすく|第1〜第3正規形の例」で扱っています。

エンティティ設計でよくある失敗と直し方

この節では、エンティティを決めるときによく起きる失敗と、その直し方が分かります。

失敗 11つのエンティティに何でも詰め込む(「顧客情報」に契約や対応履歴まで入れる)
→
直し方1件の単位が違うものは分ける。顧客1社・契約1件・対応1回は別のエンティティ

見分け方:同じ行に、件数の違う情報(顧客1件に契約が複数)が並んでいないか

失敗 2タイトルと1冊、商品と在庫のように、粒度の違うものを1つにする
→
直し方「同じ〜が複数ある」という要件があれば、2つのエンティティに分ける

例題では書籍(タイトル)と蔵書(1冊)に分けて、同時に2人へ貸せた

失敗 3名前やメールアドレスを識別子にする
→
直し方変わらず重複しない番号を識別子にし、メールは UNIQUE の属性にする

名前は同じ人がいて、メールは変わる。どちらも1件を見分け続けられない

失敗 4多対多をそのまま2つのテーブルでつなごうとする
→
直し方間に関係を表すエンティティ(履修・注文明細など)を置き、1対多を2本にする

間のエンティティに、日付や数量などの属性を持たせられる

失敗 5状態ごとにエンティティを分ける(「貸出中の本」「返却済みの本」)
→
直し方状態は属性(返却日の有無・ステータス列)で表す

状態が変わるたびに行を移す処理が要らなくなる

エンティティ設計のチェックリスト

設計レビューの前に、次の7つを確かめます。

  • 1エンティティごとに「1件の単位」を一文で言える例:蔵書=棚にある本1冊
  • 2どのエンティティにも、変わらず重複しない識別子がある弱エンティティは、親のキーとの組み合わせになっている
  • 3エンティティ名が業務の言葉と一致している利用者が読んで意味が分かる名前にする
  • 4属性が1つの値だけを持っている「電話番号1・2・3」のような繰り返しは別のエンティティにする
  • 5多対多の関係が残っていない間のエンティティを置いて1対多に分ける
  • 6「もの」と「できごと」の両方を洗い出した貸出・注文・申請のような、日時と一緒に発生するものの漏れが多い
  • 7業務のルールを制約(外部キー・CHECK・UNIQUE)で表せるか確かめた表せないルールは、アプリ側のどこで守るかを決める

DDDのエンティティと値オブジェクトの違い

この節では、ドメイン駆動設計(DDD)でいうエンティティと、値オブジェクトとの違いが、Java の実行結果で分かります。

DDD のエンティティは「属性が変わっても、同じものとして追い続ける必要があるオブジェクト」です。 Eric Evans の「Domain-Driven Design Reference」は、エンティティを "Many objects represent a thread of continuity and identity, going through a lifecycle, though their attributes may change."(多くのオブジェクトは、属性が変わっても、ライフサイクルを通して連続性と同一性を持つ)と説明しています。一方、値オブジェクトは "Many objects have no conceptual identity."(概念上の同一性を持たない)もので、"Treat the value object as immutable."(不変として扱う)とされています。

観点 エンティティ 値オブジェクト
同じかどうかの判定 ID が同じなら同じ すべての値が同じなら同じ
値の変更 変わってよい(名前の変更など) 変えない。新しく作り直す
ライフサイクル 作られ、変わり、消える 使い捨て。交換するもの
例 社員・注文・口座 メールアドレス・金額・期間・住所
Java での書き方 class。equals は ID で比べる record(不変・値で比べる)

Javaで同一性の違いを確かめる

社員(エンティティ)とメールアドレス(値オブジェクト)を書き、PostgreSQL から同じ社員を2回読み込んで比べました。

package com.example.library;

/** 値オブジェクト:IDを持たず、値が同じなら同じもの */
public record Email(String value) {
    public Email {
        if (value == null || !value.contains("@")) {
            throw new IllegalArgumentException("メールアドレスの形ではありません: " + value);
        }
        value = value.toLowerCase();
    }
}
package com.example.library;

import jakarta.persistence.*;
import java.util.Objects;

@Entity
public class Employee {
    @Id
    @Column(name = "employee_id")
    private Integer id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false, unique = true)
    private String email;

    protected Employee() {}                // JPA が使う引数なしコンストラクタ

    public Employee(Integer id, String name, Email email) {
        this.id = id;
        this.name = name;
        this.email = email.value();
    }

    public Integer getId() { return id; }
    public String getName() { return name; }
    public Email getEmail() { return new Email(email); }
    public void changeName(String newName) { this.name = newName; }   // 属性は変わってよい

    // エンティティの同一性は ID で決める
    @Override public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee other)) return false;
        return id != null && id.equals(other.id);
    }
    @Override public int hashCode() { return Objects.hashCode(id); }
    @Override public String toString() { return "Employee{id=" + id + ", name=" + name + "}"; }
}

確かめる側のコードです。JDBC で同じ行を2回読み、別々のオブジェクトを作っています。

package com.example.library;

import java.sql.*;

public class IdentityDemo {
    static Employee find(Connection con, int id) throws SQLException {
        try (PreparedStatement ps = con.prepareStatement(
                "SELECT employee_id, name, email FROM employee WHERE employee_id = ?")) {
            ps.setInt(1, id);
            try (ResultSet rs = ps.executeQuery()) {
                rs.next();
                return new Employee(rs.getInt(1), rs.getString(2), new Email(rs.getString(3)));
            }
        }
    }

    public static void main(String[] args) throws Exception {
        try (Connection con = DriverManager.getConnection(
                "jdbc:postgresql://127.0.0.1:28300/s283db", "postgres", "")) {
            Employee a = find(con, 1);
            Employee b = find(con, 1);
            System.out.println("a = " + a + " / b = " + b);
            System.out.println("a == b      : " + (a == b));
            System.out.println("a.equals(b) : " + a.equals(b));
            b.changeName("佐藤(旧姓 田中)");
            System.out.println("名前を変えた後 a.equals(b) : " + a.equals(b));
            Employee c = new Employee(2, "佐藤", new Email("sato@example.com"));
            System.out.println("名前とメールが同じでも ID が違う a.equals(c) : " + a.equals(c));

            Email e1 = new Email("Sato@Example.com");
            Email e2 = new Email("sato@example.com");
            System.out.println("値オブジェクト e1.equals(e2) : " + e1.equals(e2) + "  " + e1);
            try { new Email("sato.example.com"); }
            catch (IllegalArgumentException ex) { System.out.println("IllegalArgumentException: " + ex.getMessage()); }
        }
    }
}
$ java -cp out:jakarta.persistence-api-3.2.0.jar:postgresql-42.7.8.jar com.example.library.IdentityDemo
a = Employee{id=1, name=佐藤} / b = Employee{id=1, name=佐藤}
a == b      : false
a.equals(b) : true
名前を変えた後 a.equals(b) : true
名前とメールが同じでも ID が違う a.equals(c) : false
値オブジェクト e1.equals(e2) : true  Email[value=sato@example.com]
IllegalArgumentException: メールアドレスの形ではありません: sato.example.com

エンティティ(Employee)

別のオブジェクト(== は false)でも、ID が同じなので同じ社員

名前を変えても、同じ社員のまま

名前とメールが同じでも、ID が違えば別の社員

値オブジェクト(Email)

record なので、値が同じなら equals が true

作るときに形を確かめ、正しい値しか存在しない

変えたいときは新しい Email を作る

同じ「メールアドレス」でも、ユーザーごとに追い続ける必要があるならエンティティ、値として使うだけなら値オブジェクトです。 どちらになるかは、モデルの中で「同じもの」をどう決めるかで変わります。DDD 全体の進め方は、関連記事「ドメイン駆動設計入門|DDDの基本と小さく始める手順をJavaで解説」で扱っています。

JPAの@Entity(Javaのエンティティクラス)

この節では、Java の JPA(Jakarta Persistence)でいうエンティティクラスの条件と、クラスとテーブルの対応が分かります。

JPA のエンティティは、@Entity を付けてテーブルの1行と対応させたクラスです。 Hibernate などの実装が、このクラスを見て SQL を組み立てます。Jakarta Persistence 3.2 仕様の 2.1 節が定める条件は次のとおりです。

条件(仕様の原文) 意味
must be annotated with the Entity annotation or declared as an entity in the XML descriptor @Entity を付ける(XML で宣言してもよい)
must have a public or protected constructor with no parameters 引数なしのコンストラクタを public か protected で持つ
must be a top-level class or a static inner class 外側のクラスか static な内部クラス
must be non-final クラスに final を付けない
Every method and persistent instance variable … must be non-final メソッドと保存するフィールドにも final を付けない
Every entity must have a primary key.(2.4節) 主キー(@Id)を必ず持つ

final を付けた Employee も javac ではコンパイルが通りました。これらの条件はコンパイラではなく、実行時に JPA の実装が確かめるものです。

クラスとテーブルの対応を確かめる

先の Employee と、貸出の Loan を JPA の注釈で書きました。

@Entity
public class Loan {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "loan_id")
    private Long id;

    @ManyToOne(optional = false)
    @JoinColumn(name = "employee_id")
    private Employee employee;

    private String isbn;

    @Column(name = "copy_no")
    private Integer copyNo;

    @Column(name = "loaned_on")
    private LocalDate loanedOn;

    @Column(name = "due_on")
    private LocalDate dueOn;

    @Column(name = "returned_on")
    private LocalDate returnedOn;

    protected Loan() {}
}

クラスに付いた注釈を読み、仕様の既定の規則(エンティティ名はクラス名、テーブル名はエンティティ名、列名はフィールド名。注釈で名前を指定すればそちら)で対応を表示する小さなプログラム(MappingReport、30行)で確かめた結果です。

$ java -cp out:jakarta.persistence-api-3.2.0.jar com.example.library.MappingReport
@Entity Employee -> エンティティ名 Employee / テーブル Employee
  id          Integer    -> 列 employee_id [主キー]
  name        String     -> 列 name [NOT NULL]
  email       String     -> 列 email [NOT NULL] [UNIQUE]
@Entity Loan -> エンティティ名 Loan / テーブル Loan
  id          Long       -> 列 loan_id [主キー] [自動採番 IDENTITY]
  employee    Employee   -> 列 employee_id [多対一 -> Employee]
  isbn        String     -> 列 isbn
  copyNo      Integer    -> 列 copy_no
  loanedOn    LocalDate  -> 列 loaned_on
  dueOn       LocalDate  -> 列 due_on
  returnedOn  LocalDate  -> 列 returned_on
Email : @Entity なし(エンティティではない)

ER図の LOAN の属性と、Loan クラスのフィールドが1つずつ対応しています。社員との関係は、employee_id という数値ではなく Employee 型のフィールドとして持つのが JPA の書き方です。

既定の名前の決まり方は、Spring Boot を使うと変わります。Spring Boot 4.1.1 のドキュメントは、既定で CamelCaseToUnderscoresNamingStrategy を使い、キャメルケースをアンダースコアに変え、テーブル名を小文字にすると説明しています(例:TelephoneNumber エンティティ → telephone_number テーブル)。この場合、copyNo は @Column を書かなくても copy_no 列になります。

書き方 エンティティ名 テーブル名
@Entity(仕様の既定) クラス名(Employee) エンティティ名(Employee)
@Entity(name = "Staff") Staff(JPQL で使う名前) Staff
@Entity + @Table(name = "employee") Employee employee
Spring Boot で @Entity だけ Employee employee(小文字・スネークケース)

エンティティクラスを画面や API の応答にそのまま返すと、関連の読み込みや不要な項目の公開が起きやすくなります。 返す形は DTO(入れ物のクラス)や record に分けるのが一般的です。Spring Data JPA での読み書きは、関連記事「Spring Data JPA使い方|クエリメソッド命名一覧と@Query実例」で扱っています。

HTMLエンティティ(文字参照)とは

この節では、HTML でいうエンティティ(文字参照)の書き方と、変換の実物が分かります。

HTML のエンティティは、&lt; や &copy; のように、& で始まり ; で終わる形で1文字を表す書き方です。 HTML の仕様での正式な名前は「文字参照(character reference)」です。< や & のように HTML の記号として意味を持つ文字を、文字として表示したいときに使います。

文字 名前で書く 10進数で書く 16進数で書く
< &lt; &#60; &#x3C;
> &gt; &#62; &#x3E;
& &amp; &#38; &#x26;
" &quot; &#34; &#x22;
' &apos; &#39; &#x27;
改行しない空白 &nbsp; &#160; &#xA0;
© &copy; &#169; &#xA9;

Pythonで変換した結果

Python の標準ライブラリ html で、文字列をエンティティに変える(エスケープ)・元に戻す(アンエスケープ)を実行しました。

import html, html.entities

s = '<a href="?q=1&x=2">Tom\'s "本"</a>'
print("escape        :", html.escape(s))
print("escape(quote=False):", html.escape(s, quote=False))
for t in ["&amp;", "&lt;p&gt;", "&copy; 2026", "&#169;", "&#xA9;", "&#x3042;",
          "&nbsp;|", "&hearts;", "&amp;amp;", "&notanentity;"]:
    print(f"unescape {t!r:18} -> {html.unescape(t)!r}")
print("HTML5 の名前付き文字参照の数:", len(html.entities.html5))
print("うち ; で終わるもの:", sum(1 for k in html.entities.html5 if k.endswith(";")))
escape        : &lt;a href=&quot;?q=1&amp;x=2&quot;&gt;Tom&#x27;s &quot;本&quot;&lt;/a&gt;
escape(quote=False): &lt;a href="?q=1&amp;x=2"&gt;Tom's "本"&lt;/a&gt;
unescape '&amp;'            -> '&'
unescape '&lt;p&gt;'        -> '<p>'
unescape '&copy; 2026'      -> '© 2026'
unescape '&#169;'           -> '©'
unescape '&#xA9;'           -> '©'
unescape '&#x3042;'         -> 'あ'
unescape '&nbsp;|'          -> '\xa0|'
unescape '&hearts;'         -> '♥'
unescape '&amp;amp;'        -> '&amp;'
unescape '&notanentity;'    -> '¬anentity;'
HTML5 の名前付き文字参照の数: 2231
うち ; で終わるもの: 2125
2,231個Python 3.11 が持つ HTML5 の名前付き文字参照html.entities.html5 の件数
106個; を付けなくても通る昔からの名前&not・&copy・&amp など
5文字html.escape が変える文字& < > と、既定で " '

気をつける3つの点

1つ目は、; を付け忘れた名前が別の文字になることです。 上の結果で &notanentity; が ¬anentity; になりました。&not(¬ の記号)は、; が無くても文字参照として扱われる昔からの名前の1つだからです。同じ HTML を Chromium 141 に読ませても、表示される文字は同じでした。

入力:&notanentity; &notin; &not &amp;amp; &copy 2026
表示:¬anentity; ∉ ¬ &amp; © 2026

URL の ?a=1&not=2 のような書き方も、HTML の中にそのまま書くと同じことが起きる場合があります。HTML に書く & は、いつも &amp; にすると防げます。

2つ目は、二重に変換することです。 すでに &amp; になっている文字列をもう一度エスケープすると &amp;amp; になり、画面に &amp; と表示されます。エスケープは「HTML に出力する直前に1回だけ」にします。

3つ目は、利用者が入力した文字列をエスケープせずに HTML に出すことです。 < がタグとして解釈され、意図しないスクリプトが動く原因になります(XSS、クロスサイトスクリプティング)。テンプレートエンジンの多くは既定でエスケープします。対策の全体は、関連記事「XSS(クロスサイトスクリプティング)対策|フレームワーク別の注意点」で扱っています。

自然言語処理の固有表現(named entity)

この節では、自然言語処理(NLP)でいう「エンティティ」が何を指すかが、実際の抽出結果で分かります。

自然言語処理では、文章の中の人名・組織名・地名・日付などを「固有表現(named entity)」と呼び、取り出す処理を固有表現抽出(NER)と呼びます。 日本語の解析ライブラリ GiNZA 5.3.0(spaCy 3.8.16 の上で動く)で、次の文から取り出しました。

import spacy
nlp = spacy.load("ja_ginza")
doc = nlp("2026年10月22日、山田商事株式会社の佐藤さんが東京駅でGoogleの担当者と会い、Pythonの研修について話した。")
for ent in doc.ents:
    print(f"{ent.text}\t{ent.label_}\t{ent.start_char}-{ent.end_char}")
2026年10月22日 Date    0-11
山田商事株式会社    Person  12-20
佐藤  Person  21-23
さん  Title_Other 23-25
東京駅 City    26-29
Google  Person  30-36

日付・人名・駅名は取り出せた一方で、会社名の「山田商事株式会社」と「Google」が Person(人名)と判定され、「Python」は取り出されませんでした。 固有表現抽出は統計的なモデルの推定なので、結果は文やモデルによって変わります。業務で使うときは、取り出した結果を辞書や人の確認と組み合わせます。

SEOでいうエンティティ(ナレッジグラフ)

この節では、Web 集客の文脈で出てくる「エンティティ」の意味が分かります。

SEO でいうエンティティは、Google が見分けている「人・場所・もの」のことです。 Google の Knowledge Graph Search API のドキュメントは、ナレッジグラフを "millions of entries that describe real-world entities like people, places, and things"(人・場所・ものなど、現実のエンティティを記述する数百万の項目)と説明し、エンティティの種類として schema.org の Book・Person・Place・Organization・LocalBusiness などを挙げています。

データベースのエンティティと同じく、「1件を見分ける」ことが中心です。会社名・所在地・連絡先の表記をサイトや外部のページでそろえ、構造化データ(schema.org の Organization など)で示すのは、Google に「同じ1件」だと見分けてもらうためです。

よくある質問

Q. エンティティとは簡単に言うと何ですか? A. 1件ずつ見分けて管理する対象のことです。データベースでは社員・本・注文のような管理する対象を、プログラムでは ID で同一性を持つオブジェクトを指します。冒頭の見分け表で、分野ごとの意味をまとめています。

Q. エンティティの意味は分野でどう違いますか? A. データベースでは管理する対象、DDD では ID で追い続けるオブジェクト、JPA ではテーブルに対応するクラス、HTML では &amp; のような文字参照、自然言語処理では人名・地名などの固有表現、SEO では Google が見分ける人・場所・ものを指します。

Q. エンティティとテーブルの違いは何ですか? A. エンティティは「何を管理するか」という設計上の単位で、テーブルはそれをデータベースに作った入れ物です。多くは1対1で対応しますが、多対多の中間のテーブルのように、対応がずれることもあります。

Q. エンティティと属性の違いは何ですか? A. それ自体を1件ずつ管理し、複数の情報を持つものがエンティティ、エンティティに付く1つの値が属性です。例題では「本棚」は棚の名前だけなので蔵書の属性にしました。

Q. エンティティとインスタンス・オブジェクトの違いは? A. 「社員」という種類(エンティティタイプ)に対して、佐藤さん1人がインスタンスです。プログラムでは、その1件をメモリに読み込んだものがオブジェクトです。

Q. エンティティの洗い出しはどうやりますか? A. 要件の文から名詞を書き出し、エンティティか属性かを分け、何を1件と数えるかと識別子を決め、関係と多重度を決めます。「エンティティの洗い出し手順」の節で、例題を ER図と CREATE TABLE まで作っています。

Q. エンティティ定義(エンティティ一覧)には何を書きますか? A. エンティティ名・1件の単位・識別子・主な属性・発生するときが基本です。「エンティティ定義の書き方」の節に、例題の4エンティティで書いた表があります。

Q. 弱エンティティとは何ですか? A. 自分の属性だけでは1件を見分けられず、親のキーと組み合わせて見分けるエンティティです。例題の蔵書(ISBN+冊番号)がこれにあたり、ER図では識別関係(実線)でつなぎます。

Q. 基本情報技術者試験ではエンティティの何が問われますか? A. IT用語辞典 e-Words の出題履歴によると、「E-Rモデルにおけるエンティティの特徴はどれか」という問題が、基本情報技術者試験で繰り返し出ています。1件を見分ける識別子を持つこと、属性とリレーションシップで表すこと、エンティティとテーブル・属性の違いが押さえどころです。

Q. DDDのエンティティと値オブジェクトの違いは? A. エンティティは ID が同じなら同じもので、属性が変わってもよいものです。値オブジェクトは値がすべて同じなら同じもので、不変として扱います。Java の実行結果で、equals の違いを確かめています。

Q. JPAのエンティティクラスの条件は? A. @Entity を付け、public か protected の引数なしコンストラクタを持ち、クラス・メソッド・保存するフィールドを final にせず、主キーを持つことです(Jakarta Persistence 3.2 仕様 2.1・2.4 節)。

Q. HTMLエンティティとは何ですか? A. &lt; や &#169; のように、& と ; で1文字を表す書き方で、仕様では文字参照と呼びます。< や & を文字として表示するとき、エスケープとして使います。

Q. SEOのエンティティとは何ですか? A. Google のナレッジグラフが見分けている人・場所・もの・組織のことです。表記をそろえ、構造化データで示すと、同じ1件として見分けてもらいやすくなります。

まとめ

  • エンティティは「1件ずつ見分けて管理する対象」。中心は「何を1件と数えるか」と「1件を何で見分けるか」の2つ。
  • データベースでは、エンティティ・属性・識別子・リレーションシップを ER図に描き、テーブルと制約にする。例題では書籍と蔵書を分けたことで、同じ本を同時に2人へ貸せた。
  • 制約にしておくと、誤ったデータ5種類(いない社員・二重の貸出・期限の逆転・メールの重複・参照中の削除)を PostgreSQL が拒否した。
  • DDD のエンティティは ID で同一性を決め、値オブジェクトは値で決める。Java では、ID が同じ別オブジェクトが equals で true になった。
  • JPA のエンティティは @Entity と @Id を持つクラスで、条件は実行時に確かめられる。HTML のエンティティは文字参照で、; の付け忘れと二重の変換に気をつける。

参考・出典

確認日はいずれも 2026年10月22日です。

  • Jakarta Persistence 3.2 Specification:https://jakarta.ee/specifications/persistence/3.2/jakarta-persistence-spec-3.2
  • Jakarta Persistence(GitHub、タグ 3.2-3.2.0-RELEASE):https://github.com/jakartaee/persistence
  • Eric Evans「Domain-Driven Design Reference」:https://www.domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf
  • Peter Chen「The Entity-Relationship Model - Toward a Unified View of Data」(ACM TODS, 1976):https://doi.org/10.1145/320434.320440
  • Wikipedia「Entity–relationship model」:https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model
  • Mermaid「Entity Relationship Diagrams」:https://mermaid.js.org/syntax/entityRelationshipDiagram.html
  • PostgreSQL 16「Constraints」:https://www.postgresql.org/docs/16/ddl-constraints.html
  • PostgreSQL 16「Partial Indexes」:https://www.postgresql.org/docs/16/indexes-partial.html
  • Spring Boot「Data Access」(Configure Hibernate Naming Strategy):https://docs.spring.io/spring-boot/how-to/data-access.html
  • HTML Standard「Named character references」:https://html.spec.whatwg.org/multipage/named-characters.html
  • Python「html — HyperText Markup Language support」:https://docs.python.org/3/library/html.html
  • GiNZA:https://megagonlabs.github.io/ginza/
  • Google「Knowledge Graph Search API」:https://developers.google.com/knowledge-graph
  • IT用語辞典 e-Words「エンティティ」(出題履歴):https://e-words.jp/w/エンティティ.html

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「エンティティとは|意味・ER図・設計手順をDBとJavaで実例解説」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?