Skip to content

Latest commit

ย 

History

History
245 lines (164 loc) ยท 17.1 KB

2021-07-08-jpa-jpql.md

File metadata and controls

245 lines (164 loc) ยท 17.1 KB

JPQL ์กฐํšŒ ๋ฐฉ์‹

findById() ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋จผ์ € ์ฐพ๊ณ  ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ๊ฐ’์„ ๋ฐ”๋กœ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ 1์ฐจ ์บ์‹œ๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด JPQL์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋จผ์ € ์กฐํšŒํ•˜์ง€ ์•Š๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— Query ํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์€ ํ๋ฆ„์œผ๋กœ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ €์žฅ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

  1. JPQL์„ ํ˜ธ์ถœํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์šฐ์„ ์ ์œผ๋กœ ์กฐํšŒํ•œ๋‹ค.
  2. ์กฐํšŒํ•œ ๊ฐ’์„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ์„ ์‹œ๋„ํ•œ๋‹ค.
  3. ์ €์žฅ์„ ์‹œ๋„ํ•  ๋•Œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋ฏธ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ(์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ๋Š” ์‹๋ณ„์ž ๊ฐ’์œผ๋กœ ์‹๋ณ„) ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ ์‹ ๊ทœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฒ„๋ฆฐ๋‹ค.

JPQL ์กฐํšŒ ๋ฐฉ์‹ ํ…Œ์ŠคํŠธ

@Entity
@Table(name = "member")
class Member(
    @Column(name = "username", nullable = false)
    var username: String,

    @Column(name = "age", nullable = false)
    var age: Int = 0,

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "team_id", nullable = false)
    var team: Team
) : EntityAuditing()

@Entity
@Table(name = "team")
class Team(
    @Column(name = "name", nullable = false, unique = true)
    var name: String
) : EntityAuditing() {

    @OneToMany(mappedBy = "team")
    var members: MutableList<Member> = mutableListOf()
}

interface TeamRepository : JpaRepository<Member, Long> {

    @Query(
        "select t from Team t inner join fetch t.members where t.name=:name"
    )
    fun findFetchJoinBy(
        @Param("name") name: String
    ): Team

}

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค. Team์€ N ๊ฐœ์˜ Member๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. TeamRepository์˜ findFetchJoinBy ๋ฉ”์„œ๋“œ๋Š” ๋‹จ์ˆœํžˆ ํŒ€ ์ด๋ฆ„์œผ๋กœ Fetch Join ํ•ด์„œ ํ•ด๋‹น Team์— ์†ํ•œ ๋ชจ๋“  Member๋ฅผ ์กฐํšŒํ•˜๋Š” JPQL ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. JPQL์ด ์œ„์—์„œ ์„ค๋ช…ํ•œ ๋ฐฉ์‹๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ์•„๋ž˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ํ™•์ธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@SpringBootTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@Transactional
class JpqlTest(
    private val em: EntityManager,
    private val teamRepository: TeamRepository
) {

    @Test
    fun `JPQL ์กฐํšŒ ํ…Œ์ŠคํŠธ`() {
        //given
        val teamA = Team(name = "teamA")
        em.persist(teamA) // teamA ์ €์žฅ

        // insert into member (id, age, team_id, username) values (null, ?, ?, ?)
        val member1 = Member(username = "member1", age = 10, team = teamA) // member1์— teamA ์—ฐ๊ฒฐํ•ด์„œ ์ €์žฅ
        // insert into member (id, age, team_id, username) values (null, ?, ?, ?)
        val member2 = Member(username = "member2", age = 20, team = teamA) // member2์— teamA ์—ฐ๊ฒฐํ•ด์„œ ์ €์žฅ
        em.persist(member1)
        em.persist(member2)

        //when
        // select team0_.id as id1_1_0_, members1_.id as id1_0_1_, team0_.name as name2_1_0_, members1_.age as age2_0_1_, members1_.team_id as team_id4_0_1_, members1_.username as username3_0_1_, members1_.team_id as team_id4_0_0__, members1_.id as id1_0_0__ from team team0_ inner join member members1_ on team0_.id=members1_.team_id where team0_.name=?
        val team = teamRepository.findFetchJoinBy("teamA")

        //then
        then(team.members).hasSize(2)
    }
}

ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. teamA๋ฅผ ์ €์žฅํ•˜๊ณ , member1, member2์— ๊ฐ๊ฐ teamA๋ฅผ ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Fetch Join์„ ํ†ตํ•ด์„œ ์•„๋ž˜ SQL ๋ฌธ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.

SELECT team0_.id AS id1_1_0_,
       members1_.id AS id1_0_1_,
       team0_.name AS name2_1_0_,
       members1_.age AS age2_0_1_,
       members1_.team_id AS team_id4_0_1_,
       members1_.username AS username3_0_1_,
       members1_.team_id AS team_id4_0_0__,
       members1_.id AS id1_0_0__
FROM team team0_
         INNER JOIN member members1_ ON team0_.id=members1_.team_id
WHERE team0_.name=?

์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜๊ณ , ์กฐํšŒ ์ฟผ๋ฆฌ ๋˜ํ•œ ๋ฌธ์ œ๊ฐ€ ์—†๋Š”๋ฐ ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์™œ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•˜๋Š” ๊ฒƒ์ผ๊นŒ ?

์ฃผ์˜! Team ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•  ๋•Œ member1, member2๋ฅผ members ์ปฌ๋ ‰์…˜์— ์ €์žฅํ•˜๋Š” ์–‘๋ฐฉํ–ฅ ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. JPQL์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ํ…Œ์ŠคํŠธํ•ด๋ณด๊ธฐ ์œ„ํ•ด์„œ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ๋ฆ„์„ ์ž์„ธํžˆ ์‚ดํŽด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  1. teamA, member1, memeber2๋ฅผ ์˜์†ํ™”๋ฅผ ์œ„ํ•ด์„œ persist ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ
  2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์˜๊ตฌ์ ์œผ๋กœ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ flush, commit์„ ์ง„ํ–‰
  3. findFetchJoinBy๋ฅผ ํ†ตํ•ด์„œ ์กฐํšŒ๋ฅผ ์ง„ํ–‰, JPQL์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋จผ์ € ๋“ค๋ฆฌ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ์กฐํšŒ
  4. ์กฐํšŒํ•œ ๊ฒฐ๊ณผ๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ ์‹œ๋„, ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ(์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ๋Š” ์‹๋ณ„์ž ๊ฐ’์œผ๋กœ ์‹๋ณ„) ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ ๊ฐ’์„ ๋ฒ„๋ฆผ, ์ฆ‰ member1, memeber2๊ฐ€ ํฌํ•จ๋œ ๋ฐ์ดํ„ฐ๋Š” ๋ฒ„๋ฆฌ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์œผ๋กœ JPQL์ด ๋™์ž‘ํ•˜๋‹ˆ ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์กฐํšŒ ์ง์ „์— ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™” ํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ๊นŒ์š”?

3๋ฒˆ ์กฐํšŒ ์ „์— ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”๋ฅผ ํ•˜๋ฉด ์œ„ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ 3๋ฒˆ์—์„œ ์กฐํšŒํ•œ ๊ฐ’์„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

{
    //when
    // select team0_.id as id1_1_0_, members1_.id as id1_0_1_, team0_.name as name2_1_0_, members1_.age as age2_0_1_, members1_.team_id as team_id4_0_1_, members1_.username as username3_0_1_, members1_.team_id as team_id4_0_0__, members1_.id as id1_0_0__ from team team0_ inner join member members1_ on team0_.id=members1_.team_id where team0_.name=?
    em.clear() // ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”
    val team = teamRepository.findFetchJoinBy("teamA")

    //then
    then(team.members).hasSize(2)
}

em.clear() ๋ฉ”์„œ๋“œ๋กœ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ฆฌ๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด์„œ JPQL ์กฐํšŒ ๋ฐฉ์‹์— ๋Œ€ํ•ด์„œ ๊ฒ€์ฆ์„ ์ง„ํ–‰ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด JPQL์€ ์™œ ์ด๋ ‡๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ผ๊นŒ์š”?

JPQL์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์ด๋ž€ ๋™์‹œ์— ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์ด ์ฒ˜๋ฆฌ๋  ๋•Œ, ํŠน์ • ํŠธ๋žœ์žญ์…˜์ด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์กฐํšŒํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ• ์ง€ ๋ง์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋ง์”€๋“œ๋ฆฌ๋ฉด JPQL ์กฐํšŒ ๋ฐฉ์‹ ๋•Œ๋ฌธ์— Isolation ๋ ˆ๋ฒจ์ด REPEATABLE READ ์ˆ˜์ค€๊นŒ์ง€ ์˜ฌ๋ผ๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ํŠธ๋žœ์žญ์…˜ REPEATABLE READ ๋ณด๋‹ค ๋‚ฎ์€ READ UNCOMMITTED, READ COMMITTED ๊ฒฝ์šฐ์—๋„ REPEATABLE READ์˜ ์ˆ˜์ค€์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ•œ์—์„œ ๋ณด์žฅ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

ISOLATION DIRY READ NOE-REPEATABLE READ PHANTOM READ
READ UNCOMMITTED O O O
READ COMMITTED X O O
REPEATABLE READ X X O(InnoDB๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ)
SERIALIZABLE X X X

๊ฐ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€๋งˆ๋‹ค ๋™์‹œ์— ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์ด ์ฒ˜๋ฆฌ๋  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋†’์€ ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ์ด ํ•„์š”ํ•ด์ง‘๋‹ˆ๋‹ค. ๋จผ์ € ๊ฐ Isolation์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋“ค์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ •๋ฆฌํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

READ UNCOMMITTED

READ UNCOMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜์—์„œ์˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ์ด COMMIT, ROLLBACK ์—ฌ๋ถ€์™€ ์ƒ๊ด€์—†์ด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ์˜ํ–ฅ์„ ์ค๋‹ˆ๋‹ค.

  1. ์‚ฌ์šฉ์ž A๋Š” emmp_no = 5000์˜ Han์„ ํŠธ๋žœ์žญ์…˜ BEGIN ์ดํ›„ INSERT(์•„์ง COMMIT ์™„๋ฃŒ๋˜์ง€ ์•Š์Œ)
  2. ์‚ฌ์šฉ์ž B๋Š” A๊ฐ€ ์•„์ง COMMIT ํ•˜๊ธฐ ์ „ emmp_no = 5000 ์กฐํšŒ, ์•„์ง COMMIT ํ•˜์ง€ ์•Š์€ emmp_no = 5000 ์กฐํšŒ ์„ฑ๊ณต
  3. ์‚ฌ์šฉ์ž A๋Š” COMMIT์„ ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ

3๋ฒˆ์—์„œ COMMIT์„ ์ง„ํ–‰ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์˜๊ตฌ ์ €์žฅํ•˜๋ฉด ๋ฌธ์ œ๋Š” ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ 3๋ฒˆ์—์„œ Rollback์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด ์‚ฌ์šฉ์ž B๋Š” ๋” ์ด์ƒ ์œ ํšจํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ  ํ•ด๋‹น ๋กœ์ง์„ ์ด์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ํ˜„์ƒ์„ ๋”ํ‹ฐ ๋ฆฌ๋“œ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, READ UNCOMMITTED์—์„œ๋Š” ๋”ํ‹ฐ ๋ฆฌ๋“œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ RDBMS ํ‘œ์ค€์—์„œ๋„ ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์œผ๋กœ ์ธ์ •ํ•˜์ง€ ์•Š์„ ์ •๋„๋กœ ์ •ํ•ฉ์„ฑ์— ๋ฌธ์ œ๊ฐ€ ๋งŽ์€ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

READ COMMITTED

READ COMMITTED ๋ ˆ๋ฒจ์—์„œ๋Š” ๋”ํ‹ฐ ๋ฆฌ๋“œ ํ˜„์ƒ์€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ํŠธ๋žœ์žญ์…˜์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ–ˆ๋”๋ผ๋„ COMMIT์ด ์™„๋ฃŒ๋œ ๋ฐ์ดํ„ฐ๋งŒ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ์กฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

  1. ์‚ฌ์šฉ์ž A๋Š” emp_no=5000์ธ ์‚ฌ์›์˜ first_name์„ Han -> Yun์œผ๋กœ ๋ณ€๊ฒฝ ๊ฒฝ
  2. 1๋ฒˆ ๋ณ€๊ฒฝ ์‹œ ์ƒˆ๋กœ์šด ๊ฐ’์ธ Yun์€ ์ฆ‰์‹œ ๊ธฐ๋ก๋˜๊ณ  ์ด์ „ ๊ฐ’์ธ Han์€ ์–ธ๋‘ ์˜์—ญ์œผ๋กœ ๋ฐฑ์—…๋œ๋‹ค.
  3. ์‚ฌ์šฉ์ž A๊ฐ€ ์ปค๋ฐ‹์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์ „์— ์‚ฌ์šฉ์ž B๊ฐ€ emp_no=5000์„ ์กฐํšŒํ•˜๋ฉด Yun์ด ์•„๋‹ˆ๋ผ Han์œผ๋กœ ์กฐํšŒ๋œ๋‹ค. ์ฆ‰ ์–ธ๋‘ ์˜์—ญ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ด๋‹ค.
  4. ์‚ฌ์šฉ์ž A๊ฐ€ ์ตœ์ข… ์ ์œผ๋กœ COMMIT ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์˜๊ตฌ์ ์œผ๋กœ ๋ฐ˜์˜ํ•œ๋‹ค.

READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ๋Š” ์–ด๋–ค ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝํ•œ ๋‚ด์šฉ์ด ์ปค๋ฐ‹ ๋˜๊ธฐ ์ „๊นŒ์ง€ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ๊ทธ๋Ÿฌํ•œ ๋ณ€๊ฒฝ ๋‚ด์šฉ์„ ์กฐํšŒํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž A๊ฐ€ ๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ์„ ์ปค๋ฐ‹ ํ•˜๋ฉด ๊ทธ๋•Œ๋ถ€ํ„ฐ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ๋„ ๋ฐฑ์—…๋œ ์–ธ๋‘ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ์ƒˆ๋กญ๊ฒŒ ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ๋„ NON-REPEATABLE READ๊ฐ€ ๊ฐ€๋Šฅํ•˜์—ฌ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์‚ฌ์šฉ์ž B๊ฐ€ BEGIN ๋ช…๋ น์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•˜๊ณ  first_name='Yun' ๊ฒ€์ƒ‰ํ•˜์—ฌ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์—†์Œ ์‘๋‹ต๋ฐ›์Œ
  2. ์‚ฌ์šฉ์ž A๊ฐ€ ์ด๋ฆ„์„ 'Yun'์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์ปค๋ฐ‹์„ ์‹คํ–‰
  3. ์‚ฌ์šฉ์ž B๋Š” ๋˜‘๊ฐ™์ด SELECT ํ•˜๋ฉด ์ด๋ฒˆ์—๋Š” ๊ฒฐ๊ณผ๊ฐ€ 1๊ฑด์ด ์กฐํšŒ๋œ๋‹ค.

์ด๋Š” ๋ณ„๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ์—†์–ด ๋ณด์ด์ง€๋งŒ, ์‚ฌ์‹ค ์‚ฌ์šฉ์ž B๊ฐ€ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ๋˜‘๊ฐ™์€ SELECT ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ–ˆ์„ ๋•Œ๋Š” ํ•ญ์ƒ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค๋Š” REPEATABLE READ ์ •ํ•ฉ์„ฑ์— ์–ด๊ธ‹๋‚ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž B๊ฐ€ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ๋˜‘๊ฐ™์€ SELECT ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ–ˆ์„ ๋•Œ๋Š” ํ•ญ์ƒ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ REPEATABLE READ ๋ผ๊ณ  ํ•˜๋ฉฐ READ COMMITTED ๋ ˆ๋ฒจ์—์„œ๋Š” NOE-REPEATABLE READ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ์ •์‚ฐ ์‹œ์Šคํ…œ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ๋‹ค์‹œ ํ’€์–ด์„œ ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. (๋ฌผ๋ก  ์•„๋ž˜ ํ๋ฆ„์ฒ˜๋Ÿผ ์ •์‚ฐ ์‹œ์Šคํ…œ์ด ๋™์ž‘ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.)

  1. ์‚ฌ์šฉ์ž B๋Š” A ํŽธ์˜์  ์ง€๊ธ‰ ๊ธˆ์•ก์„ ์กฐํšŒํ•˜์—ฌ ์ง€๊ธ‰ ๊ธˆ์•ก 5,000์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
  2. ์‚ฌ์šฉ์ž A๋Š” A ํŽธ์˜์  ์ง€๊ธ‰ ๊ธˆ์•ก์„ 5,000 -> 10,000์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
  3. ์‚ฌ์šฉ์ž B๋Š” A ํŽธ์˜์  ์ง€๊ธ‰ ๊ธˆ์•ก์„ ๋‹ค์‹œ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. 1๋ฒˆ์—์„œ ์กฐํšŒํ•œ ์ง€๊ธ‰ ๊ธˆ์•ก 5,000์ด ์•„๋‹ˆ๋ผ 10,000์ด ์กฐํšŒ๋ฉ๋‹ˆ๋‹ค.

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

B๋งˆ์ผ“์˜ ์ง€๊ธ‰ ๊ธˆ์•ก์„ 2,000 -> 3,000์œผ๋กœ ๋ณ€๊ฒฝํ•ด๋„ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜ BEGIN์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒ๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด ๊ทธ ์‹œ์ ์˜ ์Šค๋ƒ…์ƒท ๋ฐ์ดํ„ฐ๋กœ ์กฐํšŒํ•˜๋Š” ๊ฒƒ์ด๋ฉฐ ๋ฐ˜๋ณต์ ์ธ ์กฐํšŒ๋ฅผ ํ•ด๋„ ํ•ด๋‹น ์Šค๋ƒ…์ƒท์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋™์ผํ•˜๊ฒŒ ์กฐํšŒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰๋˜๋Š” SELECT ๋ฌธ์žฅ๊ณผ ํŠธ๋žœ์žญ์…˜ ์™ธ๋ถ€์—์„œ ์‹คํ–‰๋˜๋Š” SELECT ๋ฌธ์žฅ์˜ ์ฐจ์ด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ REPEATABLE READ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ SELECT ์ฟผ๋ฆฌ ๋ฌธ์žฅ๋„ ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„ ๋‚ด์—์„œ๋งŒ ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, BEGEN TRANSACTION์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•œ ์ƒํƒœ์—์„œ๋Š” ๋™์ผํ•œ ์ฟผ๋ฆฌ๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ์‹คํ–‰ํ•ด๋ด๋„ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์žฅ๋ฐ›์Šต๋‹ˆ๋‹ค. ์•„๋ฌด๋ฆฌ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ ์ž COMMIT์„ ์‹คํ–‰ํ•œ๋‹ค ํ•˜๋”๋ผ๋„ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์‘๋‹ต๋ฐ›์Šต๋‹ˆ๋‹ค.

REPEATABLE READ

REPEATABLE READ๋Š” MySQL์˜ InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ๋Š” READ COMMITED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ ๋ฐœ์ƒํ•˜๋Š” NON-REPEATABLE READ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์€ ํŠธ๋žœ์žญ์…˜์ด ROLLBACK๋  ๊ฐ€๋Šฅ์„ฑ์— ๋Œ€๋น„ํ•ด ๋ณ€๊ฒฝ๋˜๊ธฐ ์ „ ๋ ˆ์ฝ”๋“œ๋ฅผ ์–ธ๋‘ ๊ณต๊ฐ„์— ๋ฐฑ์—…ํ•ด๋‘๊ณ  ์‹ค์ œ ๋ ˆ์ฝ”๋“œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉฐ ์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ ๋ฐฉ์‹์„ MVCC๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. REPEATABLE READ์™€ READ COMMITTED์˜ ์ฐจ์ด๋Š” ์–ธ๋‘ ์˜์—ญ์— ๋ฐฑ์—…๋œ ๋ ˆ์ฝ”๋“œ์˜ ์—ฌ๋Ÿฌ ๋ฒ„์ „ ๊ฐ€์šด๋ฐ ๋ช‡ ๋ฒˆ์งธ ์ด์ „ ๋ฒ„์ „๊นŒ์ง€ ์ฐพ์•„ ๋“ค์–ด๊ฐ€์•ผ ํ•˜๋Š”์ง€์— ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ชจ๋“  InnodB ํŠธ๋žœ์žญ์…˜์€ ๊ณ ์œ ํ•œ ํŠธ๋žœ์žญ์…˜ ๋ฒˆํ˜ธ(์ˆœ์ฐจ์ ์œผ๋กœ ์ฆ๊ฐ€ํ•˜๋Š” ๊ฐ’)๋ฅผ ๊ฐ€์ง€๋ฉฐ, ์–ธ๋‘ ์˜์—ญ์— ๋ฐฑ์—…๋œ ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ์—๋Š” ๋ณ€๊ฒฝ์„ ๋ฐœ์ƒ์‹œํ‚จ ํŠธ๋žœ์žญ์…˜์˜ ๋ฒˆํ˜ธ๊ฐ€ ํฌํ•จ๋ผ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŠธ๋žœ์žญ์…˜ ๋ฒˆํ˜ธ๋ฅผ ๋ณด๊ณ  ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ค„์ง€ ๊ฒฐ์ •ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

  1. ์ด๋ฏธ TRX-ID: 6 INSERT ๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค.
  2. ์‚ฌ์šฉ์ž A์˜ TRX-ID: 10๋ฒˆ์œผ๋กœ emp_no=5000 ์กฐํšŒ, Han ์‘๋‹ต
  3. ์‚ฌ์šฉ์ž B์˜ TRX-ID: 12๋ฒˆ์œผ๋กœ emp_no=5000 first_name Han -> Yun์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์ตœ์ข… COMMIT, UNDO ์˜์—ญ์˜ ์ด์ „ ๋ฐ์ดํ„ฐ Yun ๋ฐฑ์—…
  4. ์‚ฌ์šฉ์ž B์˜ TRX-ID: 10๋ฒˆ์œผ๋กœ emp_no=5000 ์กฐํšŒ ๋‹ค์‹œ ์กฐํšŒ, 10๋ฒˆ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์‹คํ–‰๋˜๋Š” ๋ชจ๋“  SELECT ์ฟผ๋ฆฌ๋Š” ํŠธ๋žœ์žญ์…˜ ๋ฒˆํ˜ธ๊ฐ€ 10๋ณด๋‹ค ์ž‘์€ ํŠธ๋žœ์žญ์…˜ ๋ฒˆํ˜ธ์—์„œ ๋ณ€๊ฒฝํ•œ ๊ฒƒ๋งŒ ๋ณธ๋‹ค ์ฆ‰, ๋™์ผํ•˜๊ฒŒ Han ์‘๋‹ต.

4๋ฒˆ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์กฐํšŒํ•˜์ง€๋งŒ ํŠธ๋žœ์žญ์…˜ ๋ฒˆํ˜ธ๊ฐ€ 12๋ฒˆ์œผ๋กœ ์ž์‹ ์˜ ํŠธ๋žœ์žญ์…˜ ๋ฒˆํ˜ธ 6๋ฒˆ ๋ณด๋‹ค ํฌ๊ธฐ ๋•Œ๋ฌธ์— UNDO ์˜์—ญ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์„ ํƒํ•˜๊ฒŒ ๋˜์–ด ๋™์ผํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ฐ˜๋ณต์ ์ธ ์ฝ๊ธฐ๋ฅผ ํ•˜๋”๋ผ๋„ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์žฅ๋ฐ›์Šต๋‹ˆ๋‹ค. SELECT ... FOR UPDATE ์กฐํšŒ ์‹œ์—๋Š” ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅด๊ฒ ์ง€๋งŒ ์—ฌ๊ธฐ๊นŒ์ง€๋Š” ๋” ์„ค๋ช…ํ•˜์ง€๋Š” ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‹ค์‹œ JPQL

๋‹ค์‹œ JPQL ์กฐํšŒ ๋ฐฉ์‹์œผ๋กœ ๋Œ์•„๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” DIRY READ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ ์•„์ง COMMIT ๋˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด ์˜ค๋”๋ผ๋„ 4๋ฒˆ ํ•ญ๋ชฉ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฒ„๋ฆฌ๊ฒŒ ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” DIRY READ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋™์ผํ•˜๊ฒŒ NON-REPEATABLE READ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ ๋™์ผ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ฐ˜๋ณต ์ฝ๊ธฐ ์‹œ์— UNDO ์˜์—ญ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๋”๋ผ๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” NON-REPEATABLE READ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

MySQL์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ์ด REPEATABLE READ ์ด๊ธด ํ•˜์ง€๋งŒ JPA๊ฐ€ MySQL๋งŒ์„ ์ง€์›ํ•˜์ง€ ์•Š์„๋ฟ๋”๋Ÿฌ ํŠธ๋žœ์žญ์…˜ ์„ค์ •์œผ๋กœ ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ์„ ์ž„์˜๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ฐ€์žฅ ๋‚ฎ์€ ๋ ˆ๋ฒจ์ธ READ UNCOMMITTED์œผ๋กœ ์กฐํšŒํ•˜๋”๋ผ๋„ JPQL์˜ ์กฐํšŒ ๋ฐฉ์‹์œผ๋กœ ์ธํ•ด์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ•œ์—์„œ REPEATABLE READ ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ์„ ๋ณด์žฅ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ด ๋™์ž‘์„ ๋ฏฟ๊ณ  MySQL์—์„œ ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ์€ ๋‚ฎ์ถ”๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์–ด๋””๊นŒ์ง€๋‚˜ ์ด๋Š” ๋™์ผํ•œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ณต์œ ํ•  ๋•Œ๋งŒ ๋™์ž‘ํ•˜๋ฉฐ, Projection๊ณผ ๊ฐ™์ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.

์ฐธ๊ณ