/jpa-master-course

Master Hibernate and JPA with Spring-Boot

Primary LanguageJava

jpa

JPA Master Course

๐ŸŽฏ ๋ชฉ์ฐจ

  1. Entity Manager, Transaction, Persistence Context
  2. JPA & Hibernate Annotations
  3. JPQL(Java Persistence Query Language)
  4. Native Queries
  5. Named Queries
  6. Fetch Type: Eager vs Lazy
  7. Entity Relationships
  8. Inheritance & Mapping
  9. Transaction Management
  10. First Level Cache & Second Level Cache
  11. Spring Data JPA

๐Ÿš€ JPA Entity Relationships

โœ” ์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ • ์‹œ ์ฃผ์˜ ์‚ฌํ•ญ

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‚ด์˜ ๋ฐ์ดํ„ฐ ์ค‘๋ณต์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํ•œ์ชฝ ์—”ํ‹ฐํ‹ฐ๋งŒ์„ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์œผ๋กœ ์„ค์ •
  • -ToOne ์—ฐ๊ด€ ๊ด€๊ณ„์˜ ๊ธฐ๋ณธ FetchType์€ Eager ์ด๋ฏ€๋กœ ์—ผ๋‘ํ•ด ๋‘๊ธฐ

โœ” @OneToOne

  • ๊ธฐ๋ณธ์ ์œผ๋กœ FetchType ์€ Eager๋กœ ์„ค์ •๋˜์–ด ์žˆ์Œ
  • ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์„ ์„ค์ •ํ•  ๋•Œ, ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์— mappedBy = {๋ณ€์ˆ˜๋ช…}์„ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋จ

Student ํด๋ž˜์Šค

@OneToOne(fetch = FetchType.LAZY)
private Passport passport;

Passport ํด๋ž˜์Šค

@OneToOne(fetch = FetchType.LAZY, mappedBy = "passport")
private Student student;

โœ” @ManyToOne, @OneToMany

  • ๊ธฐ๋ณธ์ ์œผ๋กœ @ManyToOne์˜ FetchType ์€ Eager๋กœ ์„ค์ •๋˜์–ด ์žˆ์Œ
  • ๊ธฐ๋ณธ์ ์œผ๋กœ @OneToMany์˜ FetchType ์€ Lazy๋กœ ์„ค์ •๋˜์–ด ์žˆ์Œ
  • ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ @ManyToOne ์ชฝ์— ์„ค์ •, ์ฆ‰ mappedBy = {๋ณ€์ˆ˜๋ช…}๋Š” @OneToMany ์ชฝ์— ์„ค์ •

Review ํด๋ž˜์Šค

@ManyToOne(fetch = FetchType.LAZY)
private Course course;

Course ํด๋ž˜์Šค

@OneToMany(mappedBy = "course")
private List<Review> reviews = new ArrayList<>();

โœ” @ManyToMany

  • ๊ธฐ๋ณธ์ ์œผ๋กœ @ManyToMany์˜ FetchType ์€ Lazy๋กœ ์„ค์ •๋˜์–ด ์žˆ์Œ
  • ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‘ ์—”ํ‹ฐํ‹ฐ์˜ ์ฃผํ‚ค๋ฅผ ๊ฐ€์ง€๊ณ  ๋‘๊ฐœ์˜ JoinTable์„ ์ƒ์„ฑํ•ด์คŒ (COURSE_STUDENTS, STUDENT_COURSES)
  • ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ์ข‹์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„์ด๋ฏ€๋กœ ๋‘˜ ์ค‘์— ํ•œ์ชฝ์„ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์œผ๋กœ ์„ค์ •ํ•ด์คŒ์œผ๋กœ์จ ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ
  • @JoinTable์€ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ ์ชฝ์— ์„ค์ •
  • joinColumns๋Š” ์ฃผ์ธ ์ชฝ์˜ ์ฃผํ‚ค ์„ค์ •
  • inverseJoinColumns๋Š” ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์˜ ์ฃผํ‚ค ์„ค์ •

Student ํด๋ž˜์Šค(์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ)

@ManyToMany
@JoinTable(
    name = "STUDENT_COURSE",
    joinColumns = @JoinColumn(name = "STUDENT_ID"),
    inverseJoinColumns = @JoinColumn(name = "COURSE_ID")
)
private List<Course> courses = new ArrayList<>();

Course ํด๋ž˜์Šค

@ManyToMany(mappedBy = "courses")
private List<Student> students = new ArrayList<>();

๐Ÿš€ JPA Inheritance Hierarchies and Mappings

โœ” SINGLE_TABLE

๋ถ€๋ชจ ํด๋ž˜์Šค

@Entity
@Getter
@Table(name = "employees")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "EMPLOYEE_TYPE")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class Employee {

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String name;

    public Employee(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return String.format("Employee[%s]", this.name);
    }
}

์ž์‹ ํด๋ž˜์Šค

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PartTimeEmployee extends Employee {

    private BigDecimal hourlyWage;

    @Builder
    public PartTimeEmployee(String name, BigDecimal hourlyWage) {
        super(name);
        this.hourlyWage = hourlyWage;
    }
}
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FullTimeEmployee extends Employee {

    private BigDecimal salary;

    @Builder
    public FullTimeEmployee(String name, BigDecimal salary) {
        super(name);
        this.salary = salary;
    }
}
  • @Inheritance์˜ strategy์˜ ๊ธฐ๋ณธ๊ฐ’์€ SINGLE_TABLE ์ž…๋‹ˆ๋‹ค.
  • SINGLE_TABLE์€ ์ƒ์† ๊ด€๊ณ„์— ์žˆ๋Š” ํ…Œ์ด๋ธ”๋“ค์„ ๋ชจ๋‘ ํ•ฉ์ณ์„œ ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ”๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋ถ€๋ชจ ํ…Œ์ด๋ธ”๋งŒ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ๊ธฐ๋ณธ์ ์œผ๋กœ DTYPE ์ด๋ผ๋Š” ์ƒˆ๋กœ์šด ์นผ๋Ÿผ์ด ์ƒ์„ฑ๋˜๊ณ , ์ด ์นผ๋Ÿผ์€ ์–ด๋– ํ•œ ์ž์‹ ํด๋ž˜์Šค์ธ์ง€ ๊ตฌ๋ถ„ํ•ด์ค๋‹ˆ๋‹ค. ํ•ด๋‹น ์นผ๋Ÿผ๋ช…์€ @DiscriminatorColumn ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์žฌ์ •์˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์ฟผ๋ฆฌ๋Š” ๋งค์šฐ ๋‹จ์ˆœํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ๋ฉด์—์„œ๋Š” ์ข‹์œผ๋‚˜, ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋  ๋•Œ null ๊ฐ’์ด ์ƒ์„ฑ๋˜๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๋ฉด์—์„œ๋Š” ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

โœ” TABLE_PER_CLASS

๋ถ€๋ชจ ํด๋ž˜์Šค

@Entity
@Getter
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class Employee {...}
  • ์ƒ์† ๊ด€๊ณ„์— ์žˆ๋Š” ํด๋ž˜์Šค์— ๋Œ€ํ•ด ๋ชจ๋‘ ๊ฐœ๋ณ„์ ์ธ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ, ์ฆ‰ ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ์กฐํšŒํ•  ๋•Œ UNION์„ ์‚ฌ์šฉํ•˜๊ณ  ์„ฑ๋Šฅ๋„ ์ค€์ˆ˜ํ•œ ํŽธ์ž…๋‹ˆ๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜ ์ž์‹ ํด๋ž˜์Šค๊ฐ€ ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ๋ชจ๋“  ์นผ๋Ÿผ์„ ์ƒ์† ๋ฐ›์œผ๋ฏ€๋กœ ๊ณตํ†ต ์นผ๋Ÿผ์— ๋Œ€ํ•œ ์ค‘๋ณต ํ˜„์ƒ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

โœ” JOINED

@Entity
@Getter
@Inheritance(strategy = InheritanceType.JOINED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class Employee {...}
  • ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ํ…Œ์ด๋ธ”์€ ์ž์‹ ์˜ ์นผ๋Ÿผ๋งŒ์„ ๊ฐ€์ง€๊ณ , ์ž์‹ ํด๋ž˜์Šค์˜ ํ…Œ์ด๋ธ”์€ ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ID๋ฅผ ์ฐธ์กฐํ•˜๋Š” ์™ธ๋ž˜ํ‚ค๋ž‘ ์ž์‹ ์˜ ์นผ๋Ÿผ๋งŒ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
  • ๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์„ค๊ณ„๋ฉด์—์„œ๋Š” ๊ฐ€์žฅ ํšจ์œจ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  • ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ์กฐํšŒํ•  ๊ฒฝ์šฐ, ๊ฐ๊ฐ์˜ ์ž์‹ ํด๋ž˜์Šค์— ๋Œ€ํ•ด JOIN ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ๋ฉด์—์„œ๋Š” ๋–จ์–ด์ง‘๋‹ˆ๋‹ค.

โœ” Mapped Super Class

  • ๋ถ€๋ชจ ํด๋ž˜์Šค
@Getter
@MappedSuperclass
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class Employee {...}
  • @MappedSuperClass๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ์—”ํ‹ฐํ‹ฐ๋กœ ๋งคํ•‘ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ @Entity ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ์ด๋กœ ์ธํ•ด์„œ ๋ถ€๋ชจ ํด๋ž˜์Šค๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์•ˆ์—์„œ๋Š” ์ž์‹ ํด๋ž˜์Šค์˜ ํ…Œ์ด๋ธ”๋งŒ ์ƒ์„ฑ๋˜๊ณ  TABLE_PER_CLASS์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

โœ” How to choose?

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋””์ž์ธ๊ณผ ๋ฌด๊ฒฐ์„ฑ์„ ๊ณ ๋ คํ•œ๋‹ค๋ฉด JOINED ๋ฐฉ์‹์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์„ฑ๋Šฅ์„ ๋” ๊ณ ๋ คํ•œ๋‹ค๋ฉด SINGLE_TABLE์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด์— TABLE_PER_CLASSํ•˜๊ณ  Mapped Super Class๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๊ฐ ํ…Œ์ด๋ธ”๋งˆ๋‹ค ์ค‘๋ณต๋˜๋Š” ์นผ๋Ÿผ์ด ์ƒ์„ฑ๋˜๋ฏ€๋กœ ๋น„์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

๐Ÿš€ ํŠธ๋žœ์žญ์…˜์ด๋ž€?

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ƒํƒœ๋ฅผ ๋ณ€ํ™˜์‹œํ‚ค๋Š” ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ์ ์ธ ์ž‘์—… ๋‹จ์œ„๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์—ฐ์‚ฐ๋“ค์˜ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด A ๊ณ„์ขŒ์—์„œ B ๊ณ„์ขŒ๋กœ ์ผ์ • ๊ธˆ์•ก์„ ์ด์ฒดํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ์‹œ๋‹ค.

  1. A ๊ณ„์ขŒ์˜ ์ž”์•ก์„ ํ™•์ธํ•œ๋‹ค.
  2. A ๊ณ„์ขŒ์˜ ๊ธˆ์•ก์—์„œ ์ด์ฒดํ•  ๊ธˆ์•ก์„ ๋นผ๊ณ  ๋‹ค์‹œ ์ €์žฅํ•œ๋‹ค.
  3. B ๊ณ„์ขŒ์˜ ์ž”์•ก์„ ํ™•์ธํ•œ๋‹ค.
  4. B ๊ณ„์ขŒ์˜ ๊ธˆ์•ก์—์„œ ์ด์ฒดํ•  ๊ธˆ์•ก์„ ๋”ํ•˜๊ณ  ๋‹ค์‹œ ์ €์žฅํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ๊ณผ์ •๋“ค์ด ๋ชจ๋‘ ํ•ฉ์ณ์ ธ์„œ "๊ณ„์ขŒ ์ด์ฒด" ๋ผ๋Š” ํ•˜๋‚˜์˜ ์ž‘์—… ๋‹จ์œ„๋ฅผ ๊ตฌ์„ฑํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

โœ” ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์€ Commit ๋˜๊ฑฐ๋‚˜ Rollback ๋ฉ๋‹ˆ๋‹ค

Commit ์—ฐ์‚ฐ

ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ์  ๋‹จ์œ„(ํŠธ๋žœ์žญ์…˜)์— ๋Œ€ํ•œ ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๋‹ค์‹œ ์ผ๊ด€๋œ ์ƒํƒœ์— ์žˆ์„ ๋•Œ, ์ด ํŠธ๋žœ์žญ์…˜์ด ํ–‰ํ•œ ๊ฐฑ์‹  ์—ฐ์‚ฐ์ด ์™„๋ฃŒ๋œ ๊ฒƒ์„ ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ์ž์—๊ฒŒ ์•Œ๋ ค์ฃผ๋Š” ์—ฐ์‚ฐ์ž…๋‹ˆ๋‹ค.

Rollback ์—ฐ์‚ฐ

ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๊ฐ€ ๋น„์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ๋˜์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ผ๊ด€์„ฑ์„ ๊นจ๋œจ๋ ธ์„ ๋•Œ, ์ด ํŠธ๋žœ์žญ์…˜์˜ ์ผ๋ถ€๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์—ˆ๋”๋ผ๋„ ํŠธ๋žœ์žญ์…˜์˜ ์›์ž์„ฑ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์ด ํŠธ๋žœ์žญ์…˜์ด ํ–‰ํ•œ ๋ชจ๋“  ์—ฐ์‚ฐ์„ ์ทจ์†Œ(Undo)ํ•˜๋Š” ์—ฐ์‚ฐ์ž…๋‹ˆ๋‹ค.

Rollback ์‹œ์—๋Š” ํ•ด๋‹น ํŠธ๋žœ์žญ์…˜์„ ์žฌ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ํ๊ธฐํ•ฉ๋‹ˆ๋‹ค.

โœ” ํŠธ๋žœ์žญ์…˜์˜ ์„ฑ์งˆ(ACID)

์›์ž์„ฑ(Atomicity), All or Nothing

ํŠธ๋žœ์žญ์…˜์˜ ๋ชจ๋“  ์—ฐ์‚ฐ๋“ค์€ ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰ ์™„๋ฃŒ๋˜๊ฑฐ๋‚˜ ์•„๋‹ˆ๋ฉด ์ „ํ˜€ ์–ด๋– ํ•œ ์—ฐ์‚ฐ๋„ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์€ ์ƒํƒœ๋ฅผ ๋ณด์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ผ๊ด€์„ฑ(Consistency)

ํŠธ๋žœ์žญ์…˜ ์™„๋ฃŒ ํ›„์—๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ผ๊ด€๋œ ์ƒํƒœ๋กœ ์œ ์ง€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋…๋ฆฝ์„ฑ(Isolation)

ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์ด ์‹คํ–‰ํ•˜๋Š” ๋„์ค‘์— ๋ณ€๊ฒฝํ•œ ๋ฐ์ดํ„ฐ๋Š” ์ด ํŠธ๋žœ์žญ์…˜์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ์ฐธ์กฐํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

์ง€์†์„ฑ(Durability)

์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜ํ–‰๋œ ํŠธ๋žœ์žญ์…˜์€ ์˜์›ํžˆ ๋ฐ˜์˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โœ” ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€

๊ฒฉ๋ฆฌ ์ˆ˜์ค€(Isolation Level)์ด๋ž€?

ํŠธ๋žœ์žญ์…˜์—์„œ ์ผ๊ด€์„ฑ์ด ์—†๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ—ˆ์šฉํ•˜๋„๋ก ํ•˜๋Š” ์ˆ˜์ค€

๊ฒฉ๋ฆฌ ์ˆ˜์ค€์˜ ํ•„์š”์„ฑ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ACID ์†์„ฑ์— ๋”ฐ๋ผ ํŠธ๋žœ์žญ์…˜์ด ์›์ž์ ์ด๋ฉด์„œ๋„ ๋…๋ฆฝ์ ์ธ ์ˆ˜ํ–‰์„ ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Locking ์ด๋ผ๋Š” ๊ฐœ๋…์ด ๋“ฑ์žฅํ•˜๊ฒŒ ๋์Šต๋‹ˆ๋‹ค. Locking์€ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃจ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ๊ด€์—ฌํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋ง‰๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฌด์กฐ๊ฑด์ ์ธ Locking ์œผ๋กœ ์ธํ•ด ๋™์‹œ์— ์ˆ˜ํ–‰๋˜๋Š” ๋งŽ์€ ํŠธ๋žœ์žญ์…˜๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„๋˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์„ฑ๋Šฅ์€ ๋–จ์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ์‘๋‹ต์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด Locking ๋ฒ”์œ„๋ฅผ ์ค„์ธ๋‹ค๋ฉด ์ž˜๋ชป๋œ ๊ฐ’์ด ์ฒ˜๋ฆฌ ๋  ์—ฌ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ตœ๋Œ€ํ•œ ํšจ์œจ์ ์ธ Locking ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

โœ” ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์˜ ์ข…๋ฅ˜

Read Uncommitted(Level 0)

  • SELECT ๋ฌธ์žฅ์ด ์ˆ˜ํ–‰๋˜๋Š” ๋™์•ˆ ํ•ด๋‹น ๋ฐ์ดํ„ฐ์— Shared Lock ์ด ๊ฑธ๋ฆฌ์ง€ ์•Š๋Š” ๋ ˆ๋ฒจ
  • ํŠธ๋žœ์žญ์…˜์— ์ฒ˜๋ฆฌ์ค‘์ธ ํ˜น์€ ์•„์ง ์ปค๋ฐ‹๋˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ์ฝ๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์–ด๋–ค ์‚ฌ์šฉ์ž๊ฐ€ A๋ผ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ B๋ผ๋Š” ๋ฐ์ดํ„ฐ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋Š” ์•„์ง ์™„๋ฃŒ๋˜์ง€ ์•Š์€(Uncommited ๋˜๋Š” Dirty) ํŠธ๋žœ์žญ์…˜์ด์ง€๋งŒ ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ์ธ B๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

Read Committed(Level 1)

  • SELECT ๋ฌธ์žฅ์ด ์ˆ˜ํ–‰๋˜๋Š” ๋™์•ˆ ํ•ด๋‹น ๋ฐ์ดํ„ฐ์— Shared Lock์ด ๊ฑธ๋ฆฌ๋Š” ๋ ˆ๋ฒจ
  • ํŠธ๋žœ์žญ์…˜์ด ์ˆ˜ํ–‰๋˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ์ ‘๊ทผํ•  ์ˆ˜ ์—†์–ด ๋Œ€๊ธฐํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ์ฆ‰, Commit์ด ์ด๋ฃจ์–ด์ง„ ํŠธ๋žœ์žญ์…˜๋งŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์–ด๋–ค ์‚ฌ์šฉ์ž๊ฐ€ A ๋ผ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ B ๋ผ๋Š” ๋ฐ์ดํ„ฐ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋Š” ํ•ด๋‹น ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

Repeatable Read(Level 2)

  • ํŠธ๋žœ์žญ์…˜์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ SELECT ๋ฌธ์žฅ์ด ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— Shared Lock ์ด ๊ฑธ๋ฆฌ๋Š” ๋ ˆ๋ฒจ
  • ํŠธ๋žœ์žญ์…˜์ด ๋ฒ”์œ„ ๋‚ด์—์„œ ์กฐํšŒํ•œ ๋ฐ์ดํ„ฐ์˜ ๋‚ด์šฉ์ด ํ•ญ์ƒ ๋™์ผํ•จ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ๊ทธ ์˜์—ญ์— ํ•ด๋‹น๋˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ Commit ์ด ๋˜๊ธฐ ์ „์— ์ˆ˜์ •์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Serializable(Level 3)

  • ํŠธ๋žœ์žญ์…˜์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ SELECT ๋ฌธ์žฅ์ด ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— Shared Lock ์ด ๊ฑธ๋ฆฌ๋Š” ๋ ˆ๋ฒจ
  • ์™„๋ฒฝํ•œ ์ฝ๊ธฐ ์ผ๊ด€์„ฑ ๋ชจ๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๋Š” ๊ทธ ์˜์—ญ์— ํ•ด๋‹น๋˜๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ˆ˜์ • ๋ฐ ์ž…๋ ฅ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

โœ” ๋‚ฎ์€ ๋‹จ๊ณ„์˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€ ์ด์šฉ์‹œ ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ

Dirty Read

  • Commit ๋˜์ง€ ์•Š์€ ์ˆ˜์ • ์ค‘์ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค.
  • ์–ด๋–ค ํŠธ๋žœ์žญ์…˜์—์„œ ์•„์ง ์‹คํ–‰์ด ๋๋‚˜์ง€ ์•Š์€ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ์˜ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ณด๊ฒŒ ๋˜๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค.

Non-Repeatable Read

  • ํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ๋‘ ๋ฒˆ ์ˆ˜ํ–‰ํ•  ๋•Œ ๊ทธ ์‚ฌ์ด์— ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ๊ฐ’์„ ์ˆ˜์ • ๋˜๋Š” ์‚ญ์ œํ•จ์œผ๋กœ์จ ๋‘ ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ๊ฐ€ ์ƒ์ดํ•˜๊ฒŒ ๋‚˜ํƒ€๋‚˜๋Š” ๋น„ ์ผ๊ด€์„ฑ ํ˜„์ƒ์ž…๋‹ˆ๋‹ค.

  • ์˜ˆ์‹œ

  1. ํŠธ๋žœ์žญ์…˜ 1 - SELECT * FROM person WHERE id = 10;
  2. ํŠธ๋žœ์žญ์…˜ 2 - UPDATE person SET age = 30 WHERE id = 10;
  3. ํŠธ๋žœ์žญ์…˜ 1 - SELECT * FROM person WHERE id = 10;

Phantom Read

  • ํ•œ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์ผ์ • ๋ฒ”์œ„์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋‘ ๋ฒˆ ์ด์ƒ ์ฝ์„ ๋•Œ, ์ฒซ ๋ฒˆ์งธ ์ฟผ๋ฆฌ์—์„œ ์—†๋˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ๋‘ ๋ฒˆ์งธ ์ฟผ๋ฆฌ์—์„œ ๋‚˜ํƒ€๋‚˜๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค.

  • ์ด๋Š” ํŠธ๋žœ์žญ์…˜ ๋„์ค‘ ์ƒˆ๋กœ์šด ๋ ˆ์ฝ”๋“œ๊ฐ€ ์‚ฝ์ž…๋˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

  • ์˜ˆ์‹œ

  1. ํŠธ๋žœ์žญ์…˜ 1 - SELECT * FROM person WHERE age BETWEEN 5 AND 55;
  2. ํŠธ๋žœ์žญ์…˜ 2 - INSERT INTO person VALUES(13, 'Alex', 25);
  3. ํŠธ๋žœ์žญ์…˜ 1 - SELECT * FROM person WHERE age BETWEEN 5 AND 55;

โœ” How to choose?

Serializable ์„ ์„ ํƒํ•˜๊ฒŒ ๋˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ํ˜„์ƒ๋“ค์€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์ง€๋งŒ ์„ฑ๋Šฅ์ด ๊ต‰์žฅํžˆ ๋–จ์–ด์ง‘๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋ณ‘๋ ฌ์ ์œผ๋กœ ํŠธ๋žœ์žญ์…˜์ด ์‹คํ–‰๋˜๋”๋ผ๋„ ๋ชจ๋‘ Commit ์ด ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผํ•˜๋ฏ€๋กœ ์‚ฌ์‹ค์ƒ ์ง๋ ฌ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค๊ณ  ๋ณผ ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” Read Committed ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Locking ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ๋ฉด์—์„œ๋„ ์ค€์ˆ˜ํ•œ ํŽธ์ด๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ผ๊ด€์„ฑ์— ๋Œ€ํ•ด์„œ๋„ ์ค€์ˆ˜ํ•œ ํŽธ์ž…๋‹ˆ๋‹ค.

โœ” Spring @Transactional vs JPA @Transactional

๋งŒ์•ฝ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์—์„œ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ MQ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด JPA์˜ @Transactional์„ ์‚ฌ์šฉํ•ด๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์—์„œ ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ MQ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ˆ˜์ •์ด๋‚˜ ์ฝ๊ธฐ ์—ฐ์‚ฐ ๋“ฑ์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด Spring์˜ @Transactional ์„ ์‚ฌ์šฉํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฒฉ๋ฆฌ ์ˆ˜์ค€ ์„ค์ •

@Transactional(isolation = Isolation.READ_COMMITTED)

๋˜๋Š”

  • READ_UNCOMMITTED = 1
  • READ_COMMITTED = 2
  • REPEATABLE_READ = 4
  • SERIALIZABLE = 8

spring.jpa.properties.hibernate.connection.isolation=2

๐Ÿš€ 1์ฐจ ์บ์‹œ์™€ 2์ฐจ ์บ์‹œ

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ(Persistence Context)์˜ ๋‚ด๋ถ€์—๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ์ €์žฅ์†Œ๊ฐ€ ์žˆ๋Š”๋ฐ ์ด๊ฒƒ์„ 1์ฐจ ์บ์‹œ(First Level Cache)๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. 1์ฐจ ์บ์‹œ๋Š” ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘ํ•˜๊ณ  ์ข…๋ฃŒํ•  ๋•Œ๊นŒ์ง€๋งŒ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„์˜ ์บ์‹œ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๋กœ ๋ณด๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ ํšŸ์ˆ˜๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ์ค„์ด์ง€๋Š” ๋ชปํ•ฉ๋‹ˆ๋‹ค.

JPA ๊ตฌํ˜„์ฒด๋“ค์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ์œ„์˜ ์บ์‹œ๋ฅผ ์ง€์›ํ•˜๋Š”๋ฐ ์ด๊ฒƒ์„ ๊ณต์œ  ์บ์‹œ ๋˜๋Š” 2์ฐจ ์บ์‹œ(Second Level Cache) ๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. 2์ฐจ ์บ์‹œ๋ฅผ ์ ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์กฐํšŒ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โœ” 1์ฐจ ์บ์‹œ

1์ฐจ ์บ์‹œ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๋‚ด๋ถ€์— ์žˆ์Šต๋‹ˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋กœ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•˜๋Š” ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋Š” 1์ฐจ ์บ์‹œ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜์„ Commit ํ•˜๊ฑฐ๋‚˜ Flush ๋ฅผ ํ•˜๊ฒŒ ๋˜๋ฉด 1์ฐจ ์บ์‹œ์— ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋“ค์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.

JPA๋ฅผ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ ๊ฐ™์€ ์ปจํ…Œ์ด๋„ˆ ์œ„์—์„œ ์‹คํ–‰ํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•  ๋•Œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜์„ ์ข…๋ฃŒํ•  ๋•Œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

1์ฐจ ์บ์‹œ๋Š” ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜์ด ์•„๋‹ˆ๊ณ  ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ์ž์ฒด๊ฐ€ ์‚ฌ์‹ค์ƒ 1์ฐจ ์บ์‹œ์ž…๋‹ˆ๋‹ค.

โœ” 2์ฐจ ์บ์‹œ

2์ฐจ ์บ์‹œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ์œ„์˜ ์บ์‹œ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•  ๋•Œ๊นŒ์ง€ ์บ์‹œ๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. 2์ฐจ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์šฐ์„  2์ฐจ ์บ์‹œ์—์„œ ์ฐพ๊ณ  ์—†์œผ๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ฐพ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ 2์ฐจ ์บ์‹œ๋ฅผ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ ํšŸ์ˆ˜๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2์ฐจ ์บ์‹œ๋Š” ๋™์‹œ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๋ ค๊ณ  ์บ์‹œํ•œ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ  ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ค์–ด์„œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์บ์‹œํ•œ ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ๋™์‹œ์— ์ˆ˜์ •ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ๊ฐ์ฒด์— ๋ฝ์„ ๊ฑธ์–ด์•ผ ํ•˜๋Š”๋ฐ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋™์‹œ์„ฑ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฝ์— ๋น„ํ•˜๋ฉด ๊ฐ์ฒด๋ฅผ ๋ณต์‚ฌํ•˜๋Š” ๋น„์šฉ์€ ์•„์ฃผ ์ €๋ ดํ•˜๋ฏ€๋กœ 2์ฐจ ์บ์‹œ๋Š” ๋ณต์‚ฌ๋ณธ์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

โœ” Hibernate & EhCache

Hibernate ๊ฐ€ ์ง€์›ํ•˜๋Š” ์บ์‹œ๋Š” ํฌ๊ฒŒ 3๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ์—”ํ‹ฐํ‹ฐ ์บ์‹œ

  • ์—”ํ‹ฐํ‹ฐ ๋‹จ์œ„๋กœ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค. ์‹๋ณ„์ž(ID)๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ์ปฌ๋ ‰์…˜์ด ์•„๋‹Œ ์—ฐ๊ด€ ๊ด€๊ณ„์— ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

2. ์ปฌ๋ ‰์…˜ ์บ์‹œ

  • ์—”ํ‹ฐํ‹ฐ์™€ ์—ฐ๊ด€๋œ ์ปฌ๋ ‰์…˜์„ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ปฌ๋ ‰์…˜์ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ด๊ณ  ์žˆ์œผ๋ฉด ์‹๋ณ„์ž ๊ฐ’๋งŒ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค.

3. ์ฟผ๋ฆฌ ์บ์‹œ

  • ์ฟผ๋ฆฌ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉํ•ด์„œ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๊ฐ€ ์—”ํ‹ฐํ‹ฐ๋ฉด ์‹๋ณ„์ž ๊ฐ’๋งŒ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค.

โœ” ํ™˜๊ฒฝ์„ค์ •ํ•˜๊ธฐ

์˜์กด์„ฑ ์„ค์ •

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-ehcache</artifactId>
</dependency>

ํ”„๋กœํผํ‹ฐ ์„ค์ •

spring.jpa.properties.hibernate.cache.use_second_level_cache = true ๐Ÿ’จ 2์ฐจ ์บ์‹œ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. spring.jpa.properties.hibernate.cache.region.factory_class ๐Ÿ’จ 2์ฐจ ์บ์‹œ๋ฅผ ์ฒ˜๋ฆฌํ•  ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. spring.jpa.properties.hibernate.generate_statistics = true ๐Ÿ’จ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ํ†ต๊ณ„์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•˜๊ฒŒ ํ•ด์ฃผ๋Š”๋ฐ ์บ์‹œ ์ ์šฉ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • L2C puts: 2์ฐจ ์บ์‹œ์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
  • L2C hits: 2์ฐจ ์บ์‹œ์˜ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ
  • L2C miss: 2์ฐจ ์บ์‹œ์— ํ•ด๋‹น ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹คํŒจ -> ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ
spring:
  jpa:
    properties:
      hibernate:
        generate_statistics: true
        format_sql: true

        cache:
          use_second_level_cache: true
          region:
            factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory

      javax:
        persistence:
          sharedCache:
            mode: ENABLE_SELECTIVE
            
logging:
  level:
    net:
      sf:
        ehcache: debug

์—”ํ‹ฐํ‹ฐ ์บ์‹œ์™€ ์ปฌ๋ ‰์…˜ ์บ์‹œ

@Cacheable ๐Ÿ’จ ์—”ํ‹ฐํ‹ฐ ์บ์‹œ ์ ์šฉ์‹œ ์‚ฌ์šฉํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜

@Cache ๐Ÿ’จ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์ „์šฉ์ž…๋‹ˆ๋‹ค. ์บ์‹œ์™€ ๊ด€๋ จ๋œ ๋” ์„ธ๋ฐ€ํ•œ ์„ค์ •์„ ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ปฌ๋ ‰์…˜ ์บ์‹œ๋ฅผ ์ ์šฉํ•  ๋•Œ์—๋„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Entity
public class Course {

    @Id 
    @GeneratedValue
    private Long id;
    
    @Column(nullable = false, length = 100)
    private String name;

    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    @OneToMany(mappedBy = "course")
    private List<Review> reviews = new ArrayList<>();
    ...
}

โœ” @Cache

CacheConcurrencyStrategy ์†์„ฑ

READ_ONLY ๐Ÿ’จ ์ž์ฃผ ์กฐํšŒํ•˜๊ณ  ์ˆ˜์ • ์ž‘์—…์„ ํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

READ_WRITE ๐Ÿ’จ ์กฐํšŒ ๋ฐ ์ˆ˜์ • ์ž‘์—…์„ ํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. Phantom Read ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ SERIALIZABLE ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

NONSTRICT_READ_WRITE ๐Ÿ’จ ๊ฑฐ์˜ ์ˆ˜์ • ์ž‘์—…์„ ํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.