findById() ๊ฐ์ ๊ฒฝ์ฐ๋ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋จผ์ ์ฐพ๊ณ ์์์ฑ ์ปจํ ์คํธ์ ํด๋น ์ํฐํฐ๊ฐ ์์ผ๋ฉด ๊ทธ ๊ฐ์ ๋ฐ๋ก ๋ฆฌํดํฉ๋๋ค. ์ด๋ฅผ 1์ฐจ ์บ์๋ผ๊ณ ๋งํฉ๋๋ค. ๋ฐ๋ฉด JPQL์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋จผ์ ์กฐํํ์ง ์๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ Query ํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์๋์ ๊ฐ์ ํ๋ฆ์ผ๋ก ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ ์ฅ์ ์๋ํฉ๋๋ค.
- 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์ ๋์ ๋ฐฉ์์ ํ ์คํธํด๋ณด๊ธฐ ์ํด์ ์์ฑํ์ต๋๋ค.
์์์ฑ ์ปจํ ์คํธ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ๋ฆ์ ์์ธํ ์ดํด ๋ณด๊ฒ ์ต๋๋ค.
- teamA, member1, memeber2๋ฅผ ์์ํ๋ฅผ ์ํด์ persist ๋ฉ์๋๋ฅผ ํตํด์ ์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์๊ตฌ์ ์ผ๋ก ์ ์ฅํ๊ธฐ ์ํด์ flush, commit์ ์งํ
- findFetchJoinBy๋ฅผ ํตํด์ ์กฐํ๋ฅผ ์งํ, JPQL์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋จผ์ ๋ค๋ฆฌ๋ ๊ฒ์ด ์๋๋ผ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ์กฐํ
- ์กฐํํ ๊ฒฐ๊ณผ๋ฅผ ์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅ ์๋, ์ด๋ฏธ ์กด์ฌํ๋ ๊ฒฝ์ฐ(์์์ฑ ์ปจํ ์คํธ์์๋ ์๋ณ์ ๊ฐ์ผ๋ก ์๋ณ) ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ ๊ฐ์ ๋ฒ๋ฆผ, ์ฆ 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 ์กฐํ ๋ฐฉ์ ๋๋ฌธ์ 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 ๊ฒฉ๋ฆฌ ์์ค์์๋ ํธ๋์ญ์ ์์์ ๋ณ๊ฒฝ ๋ด์ฉ์ด COMMIT, ROLLBACK ์ฌ๋ถ์ ์๊ด์์ด ๋ค๋ฅธ ํธ๋์ญ์ ์ ์ํฅ์ ์ค๋๋ค.
- ์ฌ์ฉ์ A๋ emmp_no = 5000์ Han์ ํธ๋์ญ์ BEGIN ์ดํ INSERT(์์ง COMMIT ์๋ฃ๋์ง ์์)
- ์ฌ์ฉ์ B๋ A๊ฐ ์์ง COMMIT ํ๊ธฐ ์ emmp_no = 5000 ์กฐํ, ์์ง COMMIT ํ์ง ์์ emmp_no = 5000 ์กฐํ ์ฑ๊ณต
- ์ฌ์ฉ์ A๋ COMMIT์ ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ
3๋ฒ์์ COMMIT์ ์งํํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์๊ตฌ ์ ์ฅํ๋ฉด ๋ฌธ์ ๋ ์์ต๋๋ค. ํ์ง๋ง ๋ฌธ์ ๊ฐ ๋ฐ์ํด์ 3๋ฒ์์ Rollback์ ์งํํ๊ฒ ๋๋ฉด ์ฌ์ฉ์ B๋ ๋ ์ด์ ์ ํจํ์ง ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ํด๋น ๋ก์ง์ ์ด์ด๊ฐ๊ฒ ๋ฉ๋๋ค. ์ด๋ฐ ํ์์ ๋ํฐ ๋ฆฌ๋๋ผ๊ณ ํฉ๋๋ค.
์ฆ, READ UNCOMMITTED์์๋ ๋ํฐ ๋ฆฌ๋๊ฐ ๋ฐ์ํ๋ฉฐ RDBMS ํ์ค์์๋ ํธ๋์ญ์ ๊ฒฉ๋ฆฌ ์์ค์ผ๋ก ์ธ์ ํ์ง ์์ ์ ๋๋ก ์ ํฉ์ฑ์ ๋ฌธ์ ๊ฐ ๋ง์ ๊ฒฉ๋ฆฌ ์์ค์ด๊ธฐ ๋๋ฌธ์ ๊ฑฐ์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
READ COMMITTED ๋ ๋ฒจ์์๋ ๋ํฐ ๋ฆฌ๋ ํ์์ ๋ฐ์ํ์ง ์์ต๋๋ค. ์ด๋ค ํธ๋์ญ์ ์์ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋๋ผ๋ COMMIT์ด ์๋ฃ๋ ๋ฐ์ดํฐ๋ง ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์กฐํ๊ฐ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
- ์ฌ์ฉ์ A๋ emp_no=5000์ธ ์ฌ์์ first_name์ Han -> Yun์ผ๋ก ๋ณ๊ฒฝ ๊ฒฝ
- 1๋ฒ ๋ณ๊ฒฝ ์ ์๋ก์ด ๊ฐ์ธ Yun์ ์ฆ์ ๊ธฐ๋ก๋๊ณ ์ด์ ๊ฐ์ธ Han์ ์ธ๋ ์์ญ์ผ๋ก ๋ฐฑ์ ๋๋ค.
- ์ฌ์ฉ์ A๊ฐ ์ปค๋ฐ์ ์ํํ๊ธฐ ์ ์ ์ฌ์ฉ์ B๊ฐ emp_no=5000์ ์กฐํํ๋ฉด Yun์ด ์๋๋ผ Han์ผ๋ก ์กฐํ๋๋ค. ์ฆ ์ธ๋ ์์ญ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ๊ฒ์ด๋ค.
- ์ฌ์ฉ์ A๊ฐ ์ต์ข ์ ์ผ๋ก COMMIT ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์๊ตฌ์ ์ผ๋ก ๋ฐ์ํ๋ค.
READ COMMITTED ๊ฒฉ๋ฆฌ ์์ค์์๋ ์ด๋ค ํธ๋์ญ์ ์์ ๋ณ๊ฒฝํ ๋ด์ฉ์ด ์ปค๋ฐ ๋๊ธฐ ์ ๊น์ง ๋ค๋ฅธ ํธ๋์ญ์ ์์ ๊ทธ๋ฌํ ๋ณ๊ฒฝ ๋ด์ฉ์ ์กฐํํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ ์ฌ์ฉ์ A๊ฐ ๋ณ๊ฒฝ๋ ๋ด์ฉ์ ์ปค๋ฐ ํ๋ฉด ๊ทธ๋๋ถํฐ ๋ค๋ฅธ ํธ๋์ญ์ ์์๋ ๋ฐฑ์ ๋ ์ธ๋ ๋ ์ฝ๋๊ฐ ์๋๋ผ ์๋กญ๊ฒ ๋ณ๊ฒฝ๋ ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํ ์ ์๊ฒ ๋ฉ๋๋ค.
READ COMMITTED ๊ฒฉ๋ฆฌ ์์ค์์๋ NON-REPEATABLE READ๊ฐ ๊ฐ๋ฅํ์ฌ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
- ์ฌ์ฉ์ B๊ฐ BEGIN ๋ช ๋ น์ผ๋ก ํธ๋์ญ์ ์ ์์ํ๊ณ first_name='Yun' ๊ฒ์ํ์ฌ ๊ฒ์ ๊ฒฐ๊ณผ ์์ ์๋ต๋ฐ์
- ์ฌ์ฉ์ A๊ฐ ์ด๋ฆ์ 'Yun'์ผ๋ก ๋ณ๊ฒฝํ๊ณ ์ปค๋ฐ์ ์คํ
- ์ฌ์ฉ์ B๋ ๋๊ฐ์ด SELECT ํ๋ฉด ์ด๋ฒ์๋ ๊ฒฐ๊ณผ๊ฐ 1๊ฑด์ด ์กฐํ๋๋ค.
์ด๋ ๋ณ๋ค๋ฅธ ๋ฌธ์ ๊ฐ ์์ด ๋ณด์ด์ง๋ง, ์ฌ์ค ์ฌ์ฉ์ B๊ฐ ํ๋์ ํธ๋์ญ์ ๋ด์์ ๋๊ฐ์ SELECT ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ ๋๋ ํญ์ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค๋ REPEATABLE READ ์ ํฉ์ฑ์ ์ด๊ธ๋ฉ๋๋ค.
์ฌ์ฉ์ B๊ฐ ํ๋์ ํธ๋์ญ์ ๋ด์์ ๋๊ฐ์ SELECT ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ ๋๋ ํญ์ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์์ผ ํฉ๋๋ค. ์ด๋ฅผ REPEATABLE READ ๋ผ๊ณ ํ๋ฉฐ READ COMMITTED ๋ ๋ฒจ์์๋ NOE-REPEATABLE READ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
ํด๋น ๋ฌธ์ ๋ฅผ ์ ์ฐ ์์คํ ์ ์๋๋ฆฌ์ค๋ก ๋ค์ ํ์ด์ ์ค๋ช ๋๋ฆฌ๊ฒ ์ต๋๋ค. (๋ฌผ๋ก ์๋ ํ๋ฆ์ฒ๋ผ ์ ์ฐ ์์คํ ์ด ๋์ํ์ง๋ ์์ต๋๋ค. ์์๋ฅผ ๋ค์ด ์ค๋ช ํ๊ธฐ ์ํจ์ ๋๋ค.)
- ์ฌ์ฉ์ B๋ A ํธ์์ ์ง๊ธ ๊ธ์ก์ ์กฐํํ์ฌ ์ง๊ธ ๊ธ์ก 5,000์ ์กฐํํฉ๋๋ค.
- ์ฌ์ฉ์ A๋ A ํธ์์ ์ง๊ธ ๊ธ์ก์ 5,000 -> 10,000์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค.
- ์ฌ์ฉ์ 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๋ MySQL์ InnoDB ์คํ ๋ฆฌ์ง ์์ง์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋๋ค. ์ด ๊ฒฉ๋ฆฌ ์์ค์์๋ READ COMMITED ๊ฒฉ๋ฆฌ ์์ค์์ ๋ฐ์ํ๋ NON-REPEATABLE READ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์์ต๋๋ค. InnoDB ์คํ ๋ฆฌ์ง ์์ง์ ํธ๋์ญ์ ์ด ROLLBACK๋ ๊ฐ๋ฅ์ฑ์ ๋๋นํด ๋ณ๊ฒฝ๋๊ธฐ ์ ๋ ์ฝ๋๋ฅผ ์ธ๋ ๊ณต๊ฐ์ ๋ฐฑ์ ํด๋๊ณ ์ค์ ๋ ์ฝ๋ ๊ฐ์ ๋ณ๊ฒฝํ๋ฉฐ ์ด๋ฌํ ๋ณ๊ฒฝ ๋ฐฉ์์ MVCC๋ผ๊ณ ํฉ๋๋ค. REPEATABLE READ์ READ COMMITTED์ ์ฐจ์ด๋ ์ธ๋ ์์ญ์ ๋ฐฑ์ ๋ ๋ ์ฝ๋์ ์ฌ๋ฌ ๋ฒ์ ๊ฐ์ด๋ฐ ๋ช ๋ฒ์งธ ์ด์ ๋ฒ์ ๊น์ง ์ฐพ์ ๋ค์ด๊ฐ์ผ ํ๋์ง์ ์๋ ๊ฒ์ ๋๋ค.
๋ชจ๋ InnodB ํธ๋์ญ์ ์ ๊ณ ์ ํ ํธ๋์ญ์ ๋ฒํธ(์์ฐจ์ ์ผ๋ก ์ฆ๊ฐํ๋ ๊ฐ)๋ฅผ ๊ฐ์ง๋ฉฐ, ์ธ๋ ์์ญ์ ๋ฐฑ์ ๋ ๋ชจ๋ ๋ ์ฝ๋์๋ ๋ณ๊ฒฝ์ ๋ฐ์์ํจ ํธ๋์ญ์ ์ ๋ฒํธ๊ฐ ํฌํจ๋ผ ์์ต๋๋ค. ์ด ํธ๋์ญ์ ๋ฒํธ๋ฅผ ๋ณด๊ณ ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค์ง ๊ฒฐ์ ํ๊ฒ ๋ฉ๋๋ค.
- ์ด๋ฏธ TRX-ID: 6 INSERT ๋์ด ์๋ค๊ณ ๊ฐ์ ํ๋ค.
- ์ฌ์ฉ์ A์ TRX-ID: 10๋ฒ์ผ๋ก emp_no=5000 ์กฐํ, Han ์๋ต
- ์ฌ์ฉ์ B์ TRX-ID: 12๋ฒ์ผ๋ก emp_no=5000 first_name Han -> Yun์ผ๋ก ๋ณ๊ฒฝํ๊ณ ์ต์ข COMMIT, UNDO ์์ญ์ ์ด์ ๋ฐ์ดํฐ Yun ๋ฐฑ์
- ์ฌ์ฉ์ B์ TRX-ID: 10๋ฒ์ผ๋ก emp_no=5000 ์กฐํ ๋ค์ ์กฐํ, 10๋ฒ ํธ๋์ญ์ ์์์ ์คํ๋๋ ๋ชจ๋ SELECT ์ฟผ๋ฆฌ๋ ํธ๋์ญ์ ๋ฒํธ๊ฐ 10๋ณด๋ค ์์ ํธ๋์ญ์ ๋ฒํธ์์ ๋ณ๊ฒฝํ ๊ฒ๋ง ๋ณธ๋ค ์ฆ, ๋์ผํ๊ฒ Han ์๋ต.
4๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์กฐํํ์ง๋ง ํธ๋์ญ์
๋ฒํธ๊ฐ 12๋ฒ์ผ๋ก ์์ ์ ํธ๋์ญ์
๋ฒํธ 6๋ฒ ๋ณด๋ค ํฌ๊ธฐ ๋๋ฌธ์ UNDO ์์ญ์ ๋ฐ์ดํฐ๋ฅผ ์ ํํ๊ฒ ๋์ด ๋์ผํ ํธ๋์ญ์
์์ ๋ฐ๋ณต์ ์ธ ์ฝ๊ธฐ๋ฅผ ํ๋๋ผ๋ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅ๋ฐ์ต๋๋ค. SELECT ... FOR UPDATE
์กฐํ ์์๋ ๊ทธ ๊ฒฐ๊ณผ๊ฐ ๋ค๋ฅด๊ฒ ์ง๋ง ์ฌ๊ธฐ๊น์ง๋ ๋ ์ค๋ช
ํ์ง๋ ์๊ฒ ์ต๋๋ค.
๋ค์ 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๊ณผ ๊ฐ์ด ์์์ฑ ์ปจํ ์คํธ์์ ๊ด๋ฆฌํ์ง ์๋ ๊ฒฝ์ฐ์๋ ๋ฌธ์ ๊ฐ ์๊น๋๋ค.
- ์๋ฐ ORM ํ์ค JPA ํ๋ก๊ทธ๋๋ฐ
- Real MySQL ISOLATION ์ค๋ช ์ ํด๋น ๋์๋ฅผ ์ฐธ๊ณ ํ์ต๋๋ค.