<aside> 💡

For this project, the DB Design phase deals with entity modeling, relationships, fetch strategies, ID generation, and the problem of conflict query. No HTTP, no security, no Spring magic beyond JPA yet

</aside>

<aside> 💡

The standard 5-step sequence for DB Design found in my database (2025 Database course) textbook, and 정보처리기사 was:

Requirement Analysis (요구 조건 분석)

Conceptual Design (개념적 설계)

Logical Design (논리적 설계)

Physical Design (물리적 설계)

Implementation (데이터베이스 구현)

My understanding of these phases is simple. The Conceptual Design deals with E-R Diagrams, which are rather abstract. The Logical Design is where we have to think. For example, Relationships in E-R Diagrams could be shown as a whole table, not just as foreign keys. Another example: we have to think of the constraints for each attribute, in order to sustain the various types of integrity of our database. One final example i will give is normalization.

Concerns like recoverability, security, efficiency, extensibility, and consistency are not handled by the design phases alone. The DBMS provides the underlying mechanisms — transaction logs, access control, indexes, constraint enforcement — and our job during design is to decide which mechanisms to use, how to configure them, and how to shape the schema so they can do their work.

So it is correct to say a DBMS should have such characteristics. That's what makes it a DBMS rather than just a file system. But they do so by providing us with the tools, and we should be able to use those tools.

</aside>

Conceptual Design and Logical Design are typically done in an ER diagram before a single line of code. JPA Mapping decisions are JPA/Hibernate specific.

We need six entities

Below is the code for the six entities we need.

There are some recurring patterns for the

// Semester.java
package com.exmplae.timetable.domain;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.time.LocalDate;

@Entity
@Table(name = "semester")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Semester {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(nullable = false, unique = true, length = 32)
	private String name;
	
	@Column(nullable = false)
	private LocalDate startDate;
	
	@Column(nullable = false)
	private LocalDate endDate;
	
	public Semester(String name, LocalDate startDate, LocalDate endDate) {
		this.name = name;
		this.startDate = startDate;
		this.endDate = endDate;
	}
}
// User.java
package com.example.timetable.domain;

import jakarta.persistence.*;

@Entity
@Table(name="users")
public class User {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(nullable = false, unique = true, length = 64)
	private String username;
	
	@Column(nullable = false)
	private String passwordHash;
	
	protected User() {
	}
	
	public User(String username, String passwordHash) {
		this.username = username;
		this.passwordHash = passwordHash;
	}
	
	public Long getId() {
		return id;
	}
	
	public String getUsername() {
		return username;
	}
	
	public String getPasswordHash() {
		return passwordHash;
	}
}
// Lecture.java
package com.example.timetable.domain;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;

@Entity // JPA
@Table(name = "lecture") // JPA
@Getter // lombok.Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED) // lombok.NoArgsConstructor and lombok.AccessLevel
public class Lecture {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(nullable = false, length = 128)
	private String title;
	
	@Column(length = 128)
	private String instructor;
	
	@Column(nullable = false)
	private int credits;
	
	@ManyToOne(fetch = FetchType.LAZY, optional = false)
	@JoinColumn(name = "semester_id", nullable = false)
	private Semester semester;
	
	@OneToMany(
		mappedBy = "lecture",
		cascade = CascadeType.ALL,
		orphanRemoval = true,
		fetch = FetchType.LAZY
	}
	private List<MeetingTime> meetingTimes = new ArrayList<>();
	
	public Lecture(String title, String instructor, int credits, Semester semester) {
		this.title = title;
		this.instructor = instructor;
		this.credits = credits;
		this.semester = semester;
	}
	
	public void addMeetingTime(MeetingTime mt) {
		meetingTimes.add(mt);
		mt.setLecture(this);
	}
}

the getters and the protected no-arg constructor are required (JPA can’t read fields without getters in many configurations, and Hibernate can’t instantiate the class without the no-arg constructor).

The Lombok version replaces all that with two annotations:

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User { ... }