π kotlin Spring JAP μ΄λ Έν μ΄ μ
μ΄λ Έν μ΄μ μ μ΄λ κ² μ°λ κ±°μΌ?β
package com.example.notesimple.domain
import jakarta.persistence.*
import java.time.LocalDateTime
@Entity
@Table(name = "notes")
class Note(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
@Column(nullable = false, length = 200)
var title: String,
@Lob // λμ©λ
@Column(nullable = false)
var content: String? = null,
) : BaseEntity()
@MappedSuperclass
abstract class BaseEntity(
@Column(nullable = false, updatable = false)
val createdAt: LocalDateTime = LocalDateTime.now(),
@Column(nullable = false)
var updatedAt: LocalDateTime = LocalDateTime.now()
)
- μ΄λ Έν μ΄μ μ΄ λ₯ν μλλ° μ΄λ κ² μ¬μ©νλ μ΄μ λ λμΌ?
- μ΄λ Έν μ΄μ κ΅¬μ‘°κ° λλ΅μ μΌλ‘ μ΄λ»κ² λμ΄ μλκ±°μΌ?
- import jakarta.persistence.* μ΄λ κ² λͺ¨λ κ±Έ λ€ μν¬νΈ νλ©΄ λ©λͺ¨λ¦¬ μ΅μ νλ μ€ν¨νλκ±° μλ?
- abstract class BaseEntity λ‘ μ μνλ€μ μμνλ μ΄μ λ? κ·Έλ₯ ν΄λμ€λ‘ λ§λ€λ©΄ μλλκ±°μΌ?
-
μ΄λ Έν μ΄μ μ΄ λ₯ν μλλ° μ΄λ κ² μ¬μ©νλ μ΄μ λ λμΌ?
JPAμμ μ΄λ Έν μ΄μ μ μ½λ μ체μ λ©νλ°μ΄ν°λ₯Ό μΆκ°νμ¬, μ½λκ° νΉμ λ°©μμΌλ‘ λμνλλ‘ νλ μμν¬λ λΌμ΄λΈλ¬λ¦¬μ μ§μνλ μν μ ν©λλ€. XML μ€μ νμΌμ μ¬μ©νλ κ²λ³΄λ€ κ°κ²°νκ³ , μ½λμ μ€μ μ΄ ν¨κ» μμ΄ κ°λ μ±κ³Ό μ μ§λ³΄μμ±μ΄ ν₯μλ©λλ€.
κ° μ΄λ Έν μ΄μ μ μν μ μ΄ν΄λ³΄κ² μ΅λλ€:
@Entity: μ΄ ν΄λμ€κ° λ°μ΄ν°λ² μ΄μ€ ν μ΄λΈκ³Ό λ§€νλλ JPA μν°ν°μμ λνλ λλ€. JPA κ΄λ¦¬ νμ μλ κ°μ²΄κ° λ©λλ€.@Table(name = "notes"): μν°ν°κ° λ§€νλ λ°μ΄ν°λ² μ΄μ€ ν μ΄λΈμ μ΄λ¦μnotesλ‘ μ§μ ν©λλ€. μ§μ νμ§ μμΌλ©΄ ν΄λμ€ μ΄λ¦(λμλ¬Έμ κ΅¬λΆ λ°©μμ ꡬν체μ λ°λΌ λ€λ¦)μ λ°λ¦ λλ€.@Id: μ΄ νλκ° μν°ν°μ κΈ°λ³Έ ν€(Primary Key)μμ λνλ λλ€.@GeneratedValue(strategy = GenerationType.IDENTITY): κΈ°λ³Έ ν€μ μμ± μ λ΅μ μ§μ ν©λλ€.IDENTITYλ λ°μ΄ν°λ² μ΄μ€κ° κΈ°λ³Έ ν€ μμ±μ λ΄λΉνλλ‘ μμνλ λ°©μμ λλ€(μ: MySQLμAUTO_INCREMENT, PostgreSQLμSERIAL).@Column(nullable = false, length = 200): νλλ₯Ό λ°μ΄ν°λ² μ΄μ€ 컬λΌμ λ§€νν©λλ€.nullable = false: μ΄ μ»¬λΌμ΄NOT NULLμ μ½ μ‘°κ±΄μ κ°λλ‘ ν©λλ€.length = 200: λ¬Έμμ΄ νμ 컬λΌμ μ΅λ κΈΈμ΄λ₯Ό 200μΌλ‘ μ§μ ν©λλ€.
@Lob: "Large Object"μ μ½μλ‘, λμ©λ λ°μ΄ν°λ₯Ό μ μ₯νλ νλμ μ¬μ©λ©λλ€. λ°μ΄ν°λ² μ΄μ€μμλCLOB(λ¬Έμμ΄) λλBLOB(λ°μ΄λ리) νμ μΌλ‘ λ§€νλ©λλ€.contentνλκ° λ§€μ° κΈ΄ ν μ€νΈλ₯Ό μ μ₯ν μ μμμ λνλ λλ€.@MappedSuperclass: μ΄ μ΄λ Έν μ΄μ μ΄ λΆμ ν΄λμ€λ μν°ν°κ° μλλ©°, λ°μ΄ν°λ² μ΄μ€ ν μ΄λΈκ³Ό μ§μ λ§€νλμ§ μμ΅λλ€. λμ , μ΄ ν΄λμ€λ₯Ό μμνλ μν°ν° ν΄λμ€λ€μ΄ μ΄ ν΄λμ€μ μ μλ νλμ λ§€ν μ 보λ₯Ό μμλ°κ² λ©λλ€. κ³΅ν΅ νλ(μ¬κΈ°μλcreatedAt,updatedAt)λ₯Ό μ¬λ¬ μν°ν°μμ μ¬μ¬μ©νκΈ° μν΄ μ¬μ©λ©λλ€.@Column(nullable = false, updatable = false)(BaseEntityμcreatedAt):updatable = false: μν°ν°κ° νλ² μμ±λ νμλ μ΄ μ»¬λΌμ κ°μ΄ λ°μ΄ν°λ² μ΄μ€μμ μ λ°μ΄νΈλμ§ μλλ‘ ν©λλ€. μ΅μ΄ μμ± μκ°μ κΈ°λ‘νλ μ©λλ‘ μ ν©ν©λλ€.
κ²°λ‘ μ μΌλ‘, μ΄λ Έν μ΄μ μ ORM (Object-Relational Mapping) νλ μμν¬μΈ JPAμκ² ν΄λμ€μ νλλ₯Ό μ΄λ»κ² λ°μ΄ν°λ² μ΄μ€ μ€ν€λ§μ λ§€ννκ³ κ΄λ¦¬ν μ§μ λν "μ§μΉ¨"μ μ 곡νλ κ²μ λλ€.
-
μ΄λ Έν μ΄μ κ΅¬μ‘°κ° λλ΅μ μΌλ‘ μ΄λ»κ² λμ΄ μλκ±°μΌ?
Javaμ Kotlinμμ μ΄λ Έν μ΄μ μ κ·Έ μμ²΄λ‘ νΉλ³ν μ’ λ₯μ μΈν°νμ΄μ€μ λλ€. μ΄λ Έν μ΄μ μ μ μν λλ
@interfaceν€μλλ₯Ό μ¬μ©ν©λλ€ (Java κΈ°μ€, Kotlinμμλ Java μ΄λ Έν μ΄μ μ κ·Έλλ‘ μ¬μ©).μ΄λ Έν μ΄μ μ ꡬ쑰λ₯Ό μ΄ν΄νκΈ° μν μ£Όμ μμλ λ€μκ³Ό κ°μ΅λλ€:
-
μ΄λ Έν μ΄μ μ μ (Annotation Definition):
// μμ: @Column μ΄λ Έν μ΄μ μ κ°λ΅νλ μ μ (μ€μ μ μλ λ 볡μ‘ν¨)
@Target({ElementType.METHOD, ElementType.FIELD}) // μ΄λ Έν μ΄μ μ μ μ©ν μ μλ λμ (λ©μλ, νλ λ±)
@Retention(RetentionPolicy.RUNTIME) // μ΄λ Έν μ΄μ μ 보λ₯Ό μΈμ κΉμ§ μ μ§ν κ²μΈκ° (RUNTIME μμ κΉμ§ μ μ§)
public @interface Column {
String name() default "";
boolean unique() default false;
boolean nullable() default true;
int length() default 255;
// ... κΈ°ν μμ±λ€
} -
λ©ν μ΄λ Έν μ΄μ (Meta-Annotations): μ΄λ Έν μ΄μ μ μ μν λ μ¬μ©λλ μ΄λ Έν μ΄μ μ λλ€.
@Target: ν΄λΉ μ΄λ Έν μ΄μ μ΄ μ΄λμ μ¬μ©λ μ μλμ§λ₯Ό μ§μ ν©λλ€. (μ:ElementType.TYPEμ ν΄λμ€/μΈν°νμ΄μ€,ElementType.FIELDλ νλ,ElementType.METHODλ λ©μλ)@Retention: μ΄λ Έν μ΄μ μ λ³΄κ° μΈμ κΉμ§ μ μ§λ μ§λ₯Ό μ§μ ν©λλ€.RetentionPolicy.SOURCE: μμ€ μ½λμλ§ μ‘΄μ¬νλ©°, μ»΄νμΌ μ μ¬λΌμ§λλ€.RetentionPolicy.CLASS: μ»΄νμΌλ ν΄λμ€ νμΌμλ μ‘΄μ¬νμ§λ§, λ°νμ μ JVMμμλ λ‘λλμ§ μμ΅λλ€. (κΈ°λ³Έκ°)RetentionPolicy.RUNTIME: μ»΄νμΌλ ν΄λμ€ νμΌμλ μ‘΄μ¬νλ©°, λ°νμ μ JVMμ μν΄ λ‘λλμ΄ λ¦¬νλ μ μ ν΅ν΄ μ‘°νν μ μμ΅λλ€. JPA μ΄λ Έν μ΄μ λ€μ λλΆλΆRUNTIMEμΌλ‘ μ§μ λμ΄ JPA ꡬνμ²΄κ° λ°νμμ μ΄ μ 보λ₯Ό μ½μ΄ ORM μμ μ μνν©λλ€.
@Documented: JavaDoc μμ± μ μ΄λ Έν μ΄μ μ 보λ ν¬ν¨νλλ‘ ν©λλ€.@Inherited: λΆλͺ¨ ν΄λμ€μ μ΄λ Έν μ΄μ μ΄ μμ ν΄λμ€μκ²λ μμλλλ‘ ν©λλ€. (@MappedSuperclassμλ λ€λ₯Έ κ°λ μΌλ‘, μ£Όλ‘ ν΄λμ€ λ 벨 μ΄λ Έν μ΄μ μ μ μ©)
-
μ΄λ Έν μ΄μ μμ (Annotation Elements/Attributes): μ΄λ Έν μ΄μ λ΄μ μ μΈλ λ©μλλ€μ λλ€. μ΄ λ©μλλ€μ μ΄λ Έν μ΄μ μ¬μ©μμκ² νλΌλ―Έν°μ²λΌ κ°μ μ λ¬λ°μ μ μκ² ν΄μ€λλ€. μλ₯Ό λ€μ΄
@Column(nullable = false)μμnullableμ΄ μμμ΄κ³falseκ° κ·Έ κ°μ λλ€.defaultν€μλλ₯Ό μ¬μ©νμ¬ κΈ°λ³Έκ°μ μ§μ ν μ μμ΅λλ€.
JPA μ΄λ Έν μ΄μ λ€μ
jakarta.persistenceν¨ν€μ§μ μ μλμ΄ μμΌλ©°, λλΆλΆ@Retention(RetentionPolicy.RUNTIME)μΌλ‘ μ€μ λμ΄ JPA νλ‘λ°μ΄λ(μ: Hibernate)κ° λ°νμμ μ΄ λ©νλ°μ΄ν°λ₯Ό μ½μ΄ λμ μΌλ‘ SQLμ μμ±νκ³ κ°μ²΄-ν μ΄λΈ λ§€νμ μ²λ¦¬ν©λλ€. -
-
import jakarta.persistence.*μ΄λ κ² λͺ¨λ κ±Έ λ€ μν¬νΈ νλ©΄ λ©λͺ¨λ¦¬ μ΅μ νλ μ€ν¨νλκ±° μλ?κ²°λ‘ λΆν° λ§μλ리면,
import jakarta.persistence.*μ κ°μ΄ μμΌλμΉ΄λ(*) μν¬νΈλ₯Ό μ¬μ©νλ€κ³ ν΄μ λ°νμ λ©λͺ¨λ¦¬ μ΅μ νκ° μ§μ μ μΌλ‘ μ€ν¨νκ±°λ λΆνμν ν΄λμ€λ€μ΄ λ©λͺ¨λ¦¬μ λ‘λλλ κ²μ μλλλ€.μ΄μ λ λ€μκ³Ό κ°μ΅λλ€:
- μ»΄νμΌ νμ μ²λ¦¬:
importλ¬Έμ μ»΄νμΌλ¬μκ² ν΄λμ€ μ΄λ¦μ μ°Ύμ κ²½λ‘λ₯Ό μλ €μ£Όλ μ§μμ΄μ λλ€. μμΌλμΉ΄λ μν¬νΈλ μ»΄νμΌλ¬κ° ν΄λΉ ν¨ν€μ§ λ΄μμ νμν ν΄λμ€λ₯Ό μ°Ύμ μ μλλ‘ νλ νΈλ¦¬ν λ°©λ²μΌ λΏμ λλ€. - λ°μ΄νΈμ½λ: μ»΄νμΌμ΄ μλ£λλ©΄, μμ±λ λ°μ΄νΈμ½λμλ μ€μ λ‘ μ½λ λ΄μμ μ¬μ©λ ν΄λμ€λ€μ λν μ°Έμ‘°λ§ ν¬ν¨λ©λλ€.
jakarta.persistence.*λΌκ³ μν¬νΈνλλΌλ, μ½λμμ@Entity,@Idλ κ°λ§ μ¬μ©νλ€λ©΄ λ°μ΄νΈμ½λμλ μ΄ λ ν΄λμ€μ λν μ°Έμ‘°λ§ λ€μ΄κ°λλ€. ν¨ν€μ§ λ΄μ μ¬μ©λμ§ μμ λ€λ₯Έ λͺ¨λ ν΄λμ€λ€μ λ°μ΄νΈμ½λμ ν¬ν¨λμ§ μμ΅λλ€. - ν΄λμ€ λ‘λ©: JVMμ ν΄λμ€κ° μ€μ λ‘ νμν λ (μ: ν΄λΉ ν΄λμ€μ κ°μ²΄λ₯Ό μμ±νκ±°λ μ μ λ©€λ²μ μ κ·Όν λ) ν΄λμ€ λ‘λλ₯Ό ν΅ν΄ λ©λͺ¨λ¦¬μ λ‘λν©λλ€. μμΌλμΉ΄λ μν¬νΈ μμ²΄κ° μ¬μ©νμ§ μλ ν΄λμ€λ€κΉμ§ 미리 λ‘λνμ§λ μμ΅λλ€.
νμ§λ§ μμΌλμΉ΄λ μν¬νΈμλ λ€λ₯Έ κ³ λ €μ¬νμ΄ μμ΅λλ€:
- κ°λ μ± λ° λͺ νμ±: μ΄λ€ ν΄λμ€λ€μ΄ μ¬μ©λλμ§ μν¬νΈ λͺ©λ‘λ§ λ³΄κ³ λͺ νν μκΈ° μ΄λ €μμ§ μ μμ΅λλ€. λ€λ₯Έ ν¨ν€μ§μ λμΌν μ΄λ¦μ ν΄λμ€κ° μμ κ²½μ° μ΄λ¦ μΆ©λμ κ°λ₯μ±λ (μ΄λ‘ μ μΌλ‘λ) μ‘΄μ¬ν©λλ€.
- λΉλ μκ°: μμ£Ό λ§μ ν΄λμ€κ° μλ ν¨ν€μ§λ₯Ό μμΌλμΉ΄λλ‘ μν¬νΈνκ³ κ·Έμ€ μμλ§ μ¬μ©νλ€λ©΄, μ»΄νμΌλ¬κ° μ¬λ³Όμ μ°Ύλ λ° μ½κ°μ μΆκ° μκ°μ΄ μμλ μ μμ§λ§, νλμ μΈ μ»΄νμΌλ¬μμλ μ΄ μν₯μ΄ λ―Έλ―Έν©λλ€.
- IDE μ§μ: νλ IDEλ€μ μ¬μ©νμ§ μλ μν¬νΈλ₯Ό μ 리ν΄μ£Όκ±°λ, λͺ μμ μν¬νΈλ₯Ό μλμΌλ‘ μΆκ°/κ΄λ¦¬ν΄μ£Όλ κΈ°λ₯μ΄ μ λμ΄ μμ΄ κ°λ° νΈμμ±κ³Ό λͺ νμ± μ¬μ΄μμ κ· νμ μ‘μ μ μμ΅λλ€.
λ°λΌμ λ©λͺ¨λ¦¬ μ΅μ ν κ΄μ μμ μμΌλμΉ΄λ μν¬νΈλ ν° λ¬Έμ κ° λμ§ μμ΅λλ€. μ£Όλ‘ μ½λμ λͺ νμ±κ³Ό ν/νμ¬μ μ½λ© 컨벀μ μ λ°λΌ μ¬μ© μ¬λΆλ₯Ό κ²°μ ν©λλ€. Spring Bootλ λ§μ λΌμ΄λΈλ¬λ¦¬μμ κ΄λ‘μ μΌλ‘ JPA μ΄λ Έν μ΄μ μ μ¬μ©ν λ
jakarta.persistence.*μ κ°μ΄ μμΌλμΉ΄λλ‘ μν¬νΈνλ κ²½μ°κ° νν©λλ€. - μ»΄νμΌ νμ μ²λ¦¬:
-
abstract class BaseEntityλ‘ μ μνλ€μ μμνλ μ΄μ λ? κ·Έλ₯ ν΄λμ€λ‘ λ§λ€λ©΄ μλλκ±°μΌ?BaseEntityλ₯Όabstract classλ‘ μ μνκ³@MappedSuperclassμ΄λ Έν μ΄μ μ λΆμ¬ μ¬μ©νλ μ£Όλ μ΄μ λ λ€μκ³Ό κ°μ΅λλ€:-
κ³΅ν΅ νλ μ¬μ¬μ© λ° μ€λ³΅ μ κ±°: μ¬λ¬ μν°ν° ν΄λμ€μμ 곡ν΅μ μΌλ‘ μ¬μ©λλ νλλ€(μ:
createdAt,updatedAtκ°μ μμ±/μμ μκ° μ 보,createdBy,updatedByκ°μ μμ±/μμ μ μ 보)μ ν κ³³μμ μ μνκ³ , μ΄λ₯Ό μμλ°λ λͺ¨λ μν°ν°κ° ν΄λΉ νλμ λ§€ν μ 보λ₯Ό μλμΌλ‘ κ°λλ‘ νκΈ° μν¨μ λλ€. μ΄λ₯Ό ν΅ν΄ μ½λ μ€λ³΅μ μ€μ΄κ³ μΌκ΄μ±μ μ μ§ν μ μμ΅λλ€. -
@MappedSuperclassμ νΉμ±:@MappedSuperclassλ‘ μ§μ λ ν΄λμ€λ κ·Έ μμ²΄λ‘ μν°ν°κ° μλλλ€. μ¦, λ°μ΄ν°λ² μ΄μ€μBaseEntityλΌλ μ΄λ¦μ ν μ΄λΈμ΄ μμ±λμ§ μμ΅λλ€.- μ΄ ν΄λμ€μ μ μλ νλλ€μ μ΄ ν΄λμ€λ₯Ό μμνλ μμ μν°ν°λ€μ ν μ΄λΈμ 컬λΌμΌλ‘ ν¬ν¨λ©λλ€.
- JPAμ μΌλ°μ μΈ μν°ν° μμ(@Inheritance μ΄λ
Έν
μ΄μ
μ¬μ©)κ³Όλ λ€λ¦
λλ€. μν°ν° μμμ λΆλͺ¨ ν΄λμ€λ μν°ν°μ΄κ±°λ, νΉμ μ λ΅(λ¨μΌ ν
μ΄λΈ, μ‘°μΈ ν
μ΄λΈ λ±)μ λ°λΌ ν
μ΄λΈ κ΅¬μ‘°κ° κ²°μ λ©λλ€.
@MappedSuperclassλ λ¨μν λ§€ν μ λ³΄λ§ μμ ν©λλ€.
-
abstractν€μλλ₯Ό μ¬μ©νλ μ΄μ :- λͺ
νν μλ:
BaseEntityλ λ 립μ μΌλ‘ μΈμ€ν΄μ€νλμ΄ μ¬μ©λ λͺ©μ μ΄ μλλΌ, μ€μ§ λ€λ₯Έ μν°ν°λ€μκ² κ³΅ν΅ μμ±μ μ 곡νκΈ° μν "ν νλ¦Ώ"μ΄λ "λΆλΆμ μΈ μ μ"μ μν μ ν©λλ€.abstractν€μλλ μ΄λ¬ν μλλ₯Ό λͺ νν λνλ λλ€. "μ΄ ν΄λμ€λ μ§μ κ°μ²΄λ‘ λ§λ€ μ μκ³ , λ°λμ μμν΄μ μ¬μ©ν΄μΌ νλ€"λ μλ―Έλ₯Ό λΆμ¬ν©λλ€. - κ°μ²΄ μμ± λ°©μ§:
abstractν΄λμ€λ μ§μ new BaseEntity()μ κ°μ΄ μΈμ€ν΄μ€νν μ μμ΅λλ€.@MappedSuperclassμ λͺ©μ μBaseEntityκ°μ²΄λ₯Ό μ§μ μμ±νμ¬ JPA 컨ν μ€νΈμμ κ΄λ¦¬ν νμκ° μμΌλ―λ‘,abstractλ‘ λ§λ€μ΄ μ€μλ‘λΌλ μ§μ μΈμ€ν΄μ€ννλ κ²μ λ§λ κ²μ΄ μ’μ΅λλ€.
- λͺ
νν μλ:
κ·Έλ₯ ν΄λμ€λ‘ λ§λ€λ©΄ μλλκ±°μΌ? (non-abstract class)
λ€, κΈ°μ μ μΌλ‘
BaseEntityλ₯Όabstractκ° μλ μΌλ° ν΄λμ€λ‘ λ§λ€κ³@MappedSuperclassλ₯Ό λΆμ¬λ JPAλ λμΌνκ² λμν©λλ€. μ¦, μμ μν°ν°λ€μBaseEntityμ νλλ€μ μμλ°μ μμ μ ν μ΄λΈμ 컬λΌμΌλ‘ κ°κ² λ©λλ€.νμ§λ§ μ΄λ κ² ν κ²½μ°:
BaseEntityκ° μΌλ° ν΄λμ€μ΄λ―λ‘new BaseEntity()λ‘ κ°μ²΄ μμ±μ΄ κ°λ₯ν΄μ§λλ€. κ·Έλ¬λ μ΄ κ°μ²΄λ JPA μν°ν°κ° μλλ―λ‘ μμμ± μ»¨ν μ€νΈμ μν΄ κ΄λ¦¬λκ±°λ λ°μ΄ν°λ² μ΄μ€μ μ μ₯λ μ μμ΅λλ€. μ΄λ¬ν κ°μ²΄ μμ±μ΄ μλ―Έκ° μκ³ νΌλμ μΌκΈ°ν μ μμ΅λλ€.- ν΄λμ€ μ€κ³μ μλκ° λΆλΆλͺ
ν΄μ§ μ μμ΅λλ€.
abstractλ₯Ό μ¬μ©νλ©΄ "μ΄κ²μ λΆμμ ν μ μμ΄λ©°, μμμ ν΅ν΄ μμ±λμ΄μΌ νλ€"λ λ©μμ§κ° λͺ νν΄μ§λλ€.
λ°λΌμ
@MappedSuperclassλ₯Ό μ¬μ©νλ ν΄λμ€λ κ°λ μ μΌλ‘λ μ€κ³μ μΌλ‘abstractλ‘ μ μΈνλ κ²μ΄ λ μ¬λ°λ₯΄κ³ μΌλ°μ μΈ κ΄λ‘μ λλ€. μ΄λ ν΄λΉ ν΄λμ€κ° λ 립μ μΈ μν°ν°κ° μλλΌ κ³΅ν΅ λ§€ν μ 보λ₯Ό μ 곡νκΈ° μν μμ ν΄λμ€μμ λͺ νν νκΈ° μν¨μ λλ€. -