ありがとうございます。
ここから 第5段階:MySQL DDL設計 + ER設計 + Entity実装方針 に進みます。
今回は、これまで決定した設計を正式版として扱います。
採用する設計方針:
-
教材ライブラリ方式
-
ContentとCourseを分離
-
ContentVersionによる版管理
-
LessonContentによる教材配置
-
Attachment共通化
-
JPAは更新系
-
MyBatisは検索・一覧・集計系
1. 最終ER構成
全体構造です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 | users
|
|--- user_roles --- roles
users
|
| teacher_id
|
course
|
course_versions
|
sections
|
lessons
|
lesson_contents
|
content
|
content_versions
|
+--- video_contents
|
+--- text_contents
|
+--- quiz_contents
| |
| +--- quiz_choices
|
+--- file_contents
|
+--- attachments
content
|
approval_histories
users
|
enrollments
|
learning_histories
|
2. 共通設計
文字コード
| CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci
|
共通カラム
主要テーブルには以下を追加します。
| created_at
created_by
updated_at
updated_by
|
3. ユーザー関連
users
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
|
roles
| CREATE TABLE roles (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(50) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL
);
|
初期値:
| ADMIN
TEACHER
MENTOR
STUDENT
|
user_roles
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | CREATE TABLE user_roles (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY(
user_id,
role_id
),
FOREIGN KEY(user_id)
REFERENCES users(id),
FOREIGN KEY(role_id)
REFERENCES roles(id)
);
|
4. 教材ライブラリ
content
教材そのもの。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | CREATE TABLE content (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_type VARCHAR(30) NOT NULL,
author_id BIGINT NOT NULL,
current_version_id BIGINT,
created_at DATETIME,
updated_at DATETIME,
FOREIGN KEY(author_id)
REFERENCES users(id)
);
|
content_type
5. Content Version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | CREATE TABLE content_versions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_id BIGINT NOT NULL,
version_no INT NOT NULL,
title VARCHAR(255) NOT NULL,
summary TEXT,
status VARCHAR(30) NOT NULL,
publish_start DATETIME,
publish_end DATETIME,
created_at DATETIME,
updated_at DATETIME,
UNIQUE(
content_id,
version_no
),
FOREIGN KEY(content_id)
REFERENCES content(id)
);
|
status
| DRAFT
REVIEWING
APPROVED
PUBLISHED
REJECTED
ARCHIVED
|
6. 動画コンテンツ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | CREATE TABLE video_contents (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_version_id BIGINT NOT NULL,
video_type VARCHAR(30) NOT NULL,
video_id VARCHAR(255),
video_url VARCHAR(1000),
duration INT,
FOREIGN KEY(content_version_id)
REFERENCES content_versions(id)
);
|
video_type
7. テキストコンテンツ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | CREATE TABLE text_contents (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_version_id BIGINT NOT NULL,
body LONGTEXT NOT NULL,
format VARCHAR(20) NOT NULL,
FOREIGN KEY(content_version_id)
REFERENCES content_versions(id)
);
|
format
8. クイズ
quiz_contents
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | CREATE TABLE quiz_contents (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_version_id BIGINT NOT NULL,
category VARCHAR(100),
question TEXT NOT NULL,
multiple_answer BOOLEAN DEFAULT FALSE,
answer_count INT DEFAULT 1,
explanation TEXT,
FOREIGN KEY(content_version_id)
REFERENCES content_versions(id)
);
|
quiz_choices
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | CREATE TABLE quiz_choices (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
quiz_content_id BIGINT NOT NULL,
choice_text VARCHAR(500),
is_correct BOOLEAN DEFAULT FALSE,
display_order INT,
FOREIGN KEY(quiz_content_id)
REFERENCES quiz_contents(id)
);
|
9. ファイル
file_contents
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | CREATE TABLE file_contents (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_version_id BIGINT NOT NULL,
description TEXT,
FOREIGN KEY(content_version_id)
REFERENCES content_versions(id)
);
|
10. Attachment
共通ファイル管理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 | CREATE TABLE attachments (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_version_id BIGINT NOT NULL,
attachment_type VARCHAR(30),
original_name VARCHAR(255),
storage_type VARCHAR(30),
storage_path VARCHAR(1000),
mime_type VARCHAR(100),
file_size BIGINT,
display_order INT,
created_at DATETIME,
FOREIGN KEY(content_version_id)
REFERENCES content_versions(id)
);
|
attachment_type
| IMAGE
PDF
MATERIAL
SUBTITLE
THUMBNAIL
OTHER
|
storage_type
11. コース管理
course
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | CREATE TABLE courses (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
teacher_id BIGINT NOT NULL,
title VARCHAR(255) NOT NULL,
current_version_id BIGINT,
created_at DATETIME,
updated_at DATETIME,
FOREIGN KEY(teacher_id)
REFERENCES users(id)
);
|
course_versions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | CREATE TABLE course_versions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
course_id BIGINT NOT NULL,
version_no INT,
description TEXT,
status VARCHAR(30),
created_at DATETIME,
FOREIGN KEY(course_id)
REFERENCES courses(id)
);
|
12. Section(無限階層)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | CREATE TABLE sections (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
course_version_id BIGINT NOT NULL,
parent_section_id BIGINT,
title VARCHAR(255),
display_order INT,
FOREIGN KEY(course_version_id)
REFERENCES course_versions(id),
FOREIGN KEY(parent_section_id)
REFERENCES sections(id)
);
|
13. Lesson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | CREATE TABLE lessons (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
section_id BIGINT NOT NULL,
title VARCHAR(255),
display_order INT,
FOREIGN KEY(section_id)
REFERENCES sections(id)
);
|
14. LessonContent
教材配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | CREATE TABLE lesson_contents (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
lesson_id BIGINT NOT NULL,
content_id BIGINT NOT NULL,
display_order INT,
required_flag BOOLEAN DEFAULT TRUE,
FOREIGN KEY(lesson_id)
REFERENCES lessons(id),
FOREIGN KEY(content_id)
REFERENCES content(id)
);
|
15. 受講管理
enrollment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | CREATE TABLE enrollments (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_id BIGINT NOT NULL,
course_id BIGINT NOT NULL,
enrolled_at DATETIME,
FOREIGN KEY(student_id)
REFERENCES users(id),
FOREIGN KEY(course_id)
REFERENCES courses(id)
);
|
learning_histories
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | CREATE TABLE learning_histories (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_id BIGINT,
content_id BIGINT,
started_at DATETIME,
completed_at DATETIME,
progress INT,
last_accessed_at DATETIME,
FOREIGN KEY(student_id)
REFERENCES users(id)
);
|
16. 承認履歴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | CREATE TABLE approval_histories (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_id BIGINT,
applicant_id BIGINT,
approver_id BIGINT,
status VARCHAR(30),
comment TEXT,
approved_at DATETIME
);
|
17. 推奨INDEX
教材検索
| CREATE INDEX idx_content_author
ON content(author_id);
CREATE INDEX idx_content_status
ON content_versions(status);
CREATE INDEX idx_content_type
ON content(content_type);
|
コース表示
| CREATE INDEX idx_section_tree
ON sections(
course_version_id,
parent_section_id,
display_order
);
|
学習履歴
| CREATE INDEX idx_learning_student
ON learning_histories(
student_id,
content_id
);
|
18. JPA管理対象
JPA Entity
CRUD対象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | User
Role
Content
ContentVersion
VideoContent
TextContent
QuizContent
QuizChoice
FileContent
Attachment
Course
CourseVersion
Section
Lesson
LessonContent
Enrollment
LearningHistory
ApprovalHistory
|
19. MyBatis対象
一覧・検索系:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | ContentSearchMapper
・教材一覧
・教材検索
・利用状況
CourseMapper
・コース一覧
・教材ツリー取得
LearningMapper
・進捗一覧
・ランキング
DashboardMapper
・管理者集計
|
ここまででDB設計は完成です。
次の段階では、いよいよ Spring Boot実装用コード設計 に入ります。
次回は以下を作成します。
第6段階:Entity完全実装
内容:
-
BaseEntity(監査カラム共通化)
-
JPA Entityコード
-
Enumコード
-
Repositoryコード
-
Entity関連設定
-
Lazy/Eager設計
-
Cascade設定
-
N+1問題対策
特に今回のような階層構造(Course → Section → Lesson)では、JPA設計を誤ると性能問題が発生するため、慎重に設計します。
ここで、テーブルに挿入するデータの初期値とサンプルデータのSQLをお願いします。
承知しました。
ここで 開発開始時に利用する初期データ(Master)+動作確認用サンプルデータ を作成します。
今回の設計では、以下を投入します。
初期データ
| テーブル |
内容 |
| roles |
権限マスタ |
| users |
管理者・教師・メンター・受講者 |
| user_roles |
権限割当 |
| content_type |
Enum管理のためDB不要(Java Enum) |
サンプルデータ
| データ |
内容 |
| 教師 |
山田先生 |
| コース |
Java基礎講座 |
| 教材 |
Javaとは(動画) |
| 教材 |
変数とは(Markdown) |
| 教材 |
理解度確認クイズ |
| 教材 |
参考PDF |
| セクション |
第1章 Java基礎 |
| レッスン |
Javaとは |
1. roles 初期データ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | INSERT INTO roles
(
code,
name
)
VALUES
(
'ADMIN',
'管理者'
),
(
'TEACHER',
'教師'
),
(
'MENTOR',
'メンター'
),
(
'STUDENT',
'受講者'
);
|
2. users 初期データ
パスワードは開発用です。
Spring SecurityではBCryptを利用します。
以下は例として
をBCrypt化した値です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 | INSERT INTO users
(
username,
email,
password,
enabled,
created_at,
updated_at
)
VALUES
(
'管理者 太郎',
'admin@example.com',
'$2a$10$7EqJtq98hPqEX7fNZaFWoO3k7r0x7QK3v8w6h1M3Z8cW4J8wK',
true,
NOW(),
NOW()
),
(
'教師 山田',
'teacher@example.com',
'$2a$10$7EqJtq98hPqEX7fNZaFWoO3k7r0x7QK3v8w6h1M3Z8cW4J8wK',
true,
NOW(),
NOW()
),
(
'メンター 佐藤',
'mentor@example.com',
'$2a$10$7EqJtq98hPqEX7fNZaFWoO3k7r0x7QK3v8w6h1M3Z8cW4J8wK',
true,
NOW(),
NOW()
),
(
'受講者 鈴木',
'student@example.com',
'$2a$10$7EqJtq98hPqEX7fNZaFWoO3k7r0x7QK3v8w6h1M3Z8cW4J8wK',
true,
NOW(),
NOW()
);
|
3. user_roles
ID想定:
ユーザーID:
の場合。
| INSERT INTO user_roles
(
user_id,
role_id
)
VALUES
(1,1),
(2,2),
(3,3),
(4,4);
|
4. コース登録
教師ID=2
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | INSERT INTO courses
(
teacher_id,
title,
created_at,
updated_at
)
VALUES
(
2,
'Java基礎講座',
NOW(),
NOW()
);
|
5. Course Version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | INSERT INTO course_versions
(
course_id,
version_no,
description,
status,
created_at
)
VALUES
(
1,
1,
'Javaプログラミング初心者向け講座',
'PUBLISHED',
NOW()
);
|
current_version_id更新
| UPDATE courses
SET current_version_id = 1
WHERE id = 1;
|
6. Section登録
第1章
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | INSERT INTO sections
(
course_version_id,
parent_section_id,
title,
display_order
)
VALUES
(
1,
NULL,
'第1章 Java基礎',
1
);
|
第1章 > Java文法
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | INSERT INTO sections
(
course_version_id,
parent_section_id,
title,
display_order
)
VALUES
(
1,
1,
'Java文法',
1
);
|
7. Lesson登録
1
2
3
4
5
6
7
8
9
10
11
12 | INSERT INTO lessons
(
section_id,
title,
display_order
)
VALUES
(
2,
'Javaとは',
1
);
|
8. Content登録
動画教材
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | INSERT INTO content
(
content_type,
author_id,
created_at,
updated_at
)
VALUES
(
'VIDEO',
2,
NOW(),
NOW()
);
|
Markdown教材
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | INSERT INTO content
(
content_type,
author_id,
created_at,
updated_at
)
VALUES
(
'TEXT',
2,
NOW(),
NOW()
);
|
クイズ教材
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | INSERT INTO content
(
content_type,
author_id,
created_at,
updated_at
)
VALUES
(
'QUIZ',
2,
NOW(),
NOW()
);
|
PDF教材
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | INSERT INTO content
(
content_type,
author_id,
created_at,
updated_at
)
VALUES
(
'FILE',
2,
NOW(),
NOW()
);
|
9. Content Version
動画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | INSERT INTO content_versions
(
content_id,
version_no,
title,
summary,
status,
published_at,
created_at,
updated_at
)
VALUES
(
1,
1,
'Javaとは',
'Javaの特徴を学習します',
'PUBLISHED',
NOW(),
NOW(),
NOW()
);
|
Markdown
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | INSERT INTO content_versions
VALUES
(
2,
2,
1,
'Java変数',
'変数とデータ型について',
'PUBLISHED',
NOW(),
NULL,
NOW(),
NOW()
);
|
※実際には列指定INSERT推奨です。
10. Video Content
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | INSERT INTO video_contents
(
content_version_id,
video_type,
video_id,
video_url,
duration
)
VALUES
(
1,
'YOUTUBE',
'abc123xyz',
'https://youtube.com/watch?v=abc123xyz',
600
);
|
11. Text Content
Markdown教材
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | INSERT INTO text_contents
(
content_version_id,
body,
format
)
VALUES
(
2,
'# Javaとは
Javaはオブジェクト指向言語です。
## 特徴
- JVM
- 高い移植性
- 豊富なライブラリ',
'MARKDOWN'
);
|
12. Quiz Content
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | INSERT INTO quiz_contents
(
content_version_id,
category,
question,
multiple_answer,
answer_count,
explanation
)
VALUES
(
3,
'Java基礎',
'Javaの特徴として正しいものを選択してください',
FALSE,
1,
'JavaはJVM上で動作します'
);
|
13. Quiz Choice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | INSERT INTO quiz_choices
(
quiz_content_id,
choice_text,
is_correct,
display_order
)
VALUES
(
1,
'JVM上で動作する',
TRUE,
1
),
(
1,
'OS専用言語である',
FALSE,
2
),
(
1,
'コンパイル不要である',
FALSE,
3
),
(
1,
'C言語の方言である',
FALSE,
4
);
|
14. File Content
| INSERT INTO file_contents
(
content_version_id,
description
)
VALUES
(
4,
'Java基礎資料PDF'
);
|
15. Attachment
PDF登録例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | INSERT INTO attachments
(
content_version_id,
attachment_type,
original_name,
storage_type,
storage_path,
mime_type,
file_size,
display_order,
created_at
)
VALUES
(
4,
'PDF',
'java-basic.pdf',
'R2',
'/contents/java-basic.pdf',
'application/pdf',
1024000,
1,
NOW()
);
|
16. LessonContent配置
教材ライブラリからレッスンへ配置します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | INSERT INTO lesson_contents
(
lesson_id,
content_id,
display_order,
required_flag
)
VALUES
(
1,
1,
1,
TRUE
),
(
1,
2,
2,
TRUE
),
(
1,
3,
3,
TRUE
),
(
1,
4,
4,
FALSE
);
|
結果:
| Javaとは
|
├ 動画
├ Markdown
├ クイズ
└ PDF
|
になります。
17. 受講登録
1
2
3
4
5
6
7
8
9
10
11
12 | INSERT INTO enrollments
(
student_id,
course_id,
enrolled_at
)
VALUES
(
4,
1,
NOW()
);
|
18. 学習履歴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | INSERT INTO learning_histories
(
student_id,
content_id,
started_at,
completed_at,
progress,
last_accessed_at
)
VALUES
(
4,
1,
NOW(),
NULL,
50,
NOW()
);
|
初期投入後の確認イメージ
ログイン:
| teacher@example.com
password
|
教師画面:
1
2
3
4
5
6
7
8
9
10
11
12
13 | 教材ライブラリ
├ Javaとは
│ └ 動画
│
├ Java変数
│ └ Markdown
│
├ 理解度確認
│ └ Quiz
│
└ Java資料
└ PDF
|
↓
コース:
| Java基礎講座
第1章 Java基礎
Javaとは
↓
動画
Markdown
Quiz
PDF
|
↓
受講者:
| Java基礎講座を受講
↓
Lesson表示
↓
教材閲覧
|
次の段階では、このDDLを前提にして、
第6段階:JPA Entity完全実装
へ進めます。
ここでは特に、
-
BaseEntity(作成者・更新者自動設定)
-
@MappedSuperclass
-
@EntityListeners
-
Lazy Loading
-
Cascade設定
-
Section階層Entity
-
ContentVersion関連
を実装レベルで設計します。