๐ค ๊ถ๊ธ์ฆ์ ๊ฐ๊ฒ ๋ ๊ณ๊ธฐ
๋ฆฌ๋ทฐ์ด ๋ถ๊ณผ ๋ํ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ค๊ฐ, EntityManager ์ clear ๋ฅผ ์ฌ์ฉํ ์ด์ ์ ๋ํ ์ง๋ฌธ์ ๋ฐ์๋ค. ์ด์ ๋ํด ๋๋ ๋จ์ ์ง์ ํ์ธ ์ธก๋ฉด์ด๊ฒ ๊ฑฐ๋ - ํ๊ณ ๋ฑํ ๋์ ์ง์์ ๋๋ธ ์ฒดํฌํ์ง ์๊ณ ๋ต๋ณ์ ์์ฑํ์๋ค.
๋์ ์๋๋ ๋ ํฌ์งํ ๋ฆฌ์ ์ฝ๋๊ฐ ์ ๋๋ก ๋์ํ๋์ง์ ๋ํ ๊ฒ์ฆ์ ํ๊ณ ์ ํ๋ ๊ฒ์ด์๋ค.
๋ง์ฝ EntityManager ์ clear ๋ฅผ ํ์ง ์์ผ๋ฉด ์ค์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ๊ฒ์ด ์๋๋ผ 1์ฐจ ์บ์๋ฅผ ํ์ธํด ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ์ง ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค. ๊ทธ๋์ ์์ฑํ ๋๋ถ๋ถ์ ํ ์คํธ ์ฝ๋์์ ์ต๊ด์ ์ผ๋ก EntityManager.clear ๋ฅผ ํธ์ถํ๋ค.
๋๋ฆ ํ์ ์ด ์์๊ธฐ์ ํธ๊ธฐ๋กญ๊ฒ ๋๋ตํ๋ค. ๊ทธ๋ฐ๋ฐ ๋ฆฌ๋ทฐ์ด์ ๋ต๋ณ์ ๋ค์๊ณผ ๊ฐ์๋ค.
์ค์นํ ๊ธฐ๋ถ์ด ๋ค์๋ค. ํน์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ ํ๋..?
๐ ์ด ๊ธ์ ์ด๋ฐ ๋ถ๋ค๊ป ์ถ์ฒํฉ๋๋ค
- ์์ ๋ฆฌ๋ทฐ ๋ด์ฉ์์ ๋์ ๊ฐ์ด ์ค์นํ์ ๋ถ๋ค
- ์ฟผ๋ฆฌ ๋ฉ์๋์ ๋์ ๋ฐฉ์์ ์ ๋ชจ๋ฅด์๋ ๋ถ๋ค
- ์ต๊ด์ ์ผ๋ก EntityManager ์ clear ๋ฅผ ํ๊ธฐ๋ ํ๋๋ฐ ์์งํ ์ ํ๋์ง ์ ๋ชจ๋ฅด๊ฒ ๋ ๋ถ๋ค
์ด์ ๋ถํฐ ์ฌ๋ฌ ์คํ๋ค์ ํด๋ณด์. ์ง์ง๋ก ์์์ฑ ์ปจํ ์คํธ๋ฅผ ํ๋์ง ์ํ๋์ง!
์ฐ์ , ๊ธฐ๋ณธ์ ์ธ ์ธํ ์ ๋ค์๊ณผ ๊ฐ๋ค.
@Entity
@Getter
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
private Team team;
public Member(String name, Team team) {
this.name = name;
this.team = team;
}
}
@Entity
@NoArgsConstructor
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Team(String name) {
this.name = name;
}
}
๊ฐ๋จํ ์ค๋ช ํ์๋ฉด ํ์์ด ํ์ ์ํด์๋, Member ๊ฐ Team ์ ๊ฐ๊ณ ์๋ ๊ตฌ์กฐ๋ค.
public interface MemberJpaRepository extends JpaRepository<Member, Long> {
List<Member> findByTeam(Team team);
}
๊ทธ๋ฆฌ๊ณ MemberJpaRepository ์๋ Team ์ ํตํด Member ๋ค์ ์ฐพ์์ค๋ findByTeam ์ด๋ผ๋ ๋ฉ์๋๊ฐ ์กด์ฌํ๋ค.
๋ค์ ํ๋ฒ ์ง์ด๋ณด์. ๋์ ๊ฐ์ค์
ํ ์คํธ๋ฅผ ํ ๋ EntityManager#clear ๋ฅผ ํด์ผ ํ๋ ์ด์ ๋ 1์ฐจ ์บ์๋ฅผ ํ์ง ์๊ณ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋๋ก ํ๊ธฐ ์ํจ์ด๋ค.
โ 1. ์ฟผ๋ฆฌ ๋ฉ์๋์ ๋ํด EntityManager#clear ๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ
@Test
@DisplayName("ํ์ ์ํ๋ ๋ฉค๋ฒ๋ค์ ์กฐํํ๋ค")
void test1() {
// given
Team team = new Team("ํ๋ก์ ํ");
entityManager.persist(team);
Member ํ๋ก1 = new Member("ํ๋ก1", team);
entityManager.persist(ํ๋ก1);
Member ํ๋ก2 = new Member("ํ๋ก2", team);
entityManager.persist(ํ๋ก2);
Member ํ๋ก3 = new Member("ํ๋ก3", team);
entityManager.persist(ํ๋ก3);
Member ํ๋ก4 = new Member("ํ๋ก4", team);
entityManager.persist(ํ๋ก4);
entityManager.flush();
entityManager.clear();
// then
System.out.println("======");
memberJpaRepository.findByTeam(team).forEach(member -> System.out.println(member.getName()));
}
์์ ๊ฐ์ด Team ํ๋๋ฅผ ์ ์ฅํ๊ณ , ์ด์ ํด๋นํ๋ Member ๋ค 4๊ฐ๋ฅผ ์ ์ฅํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ ์ฅ๋ Team ์ ๊ธฐ์ค์ผ๋ก Member ๋ค์ ์กฐํํด์ค๋๋ก ํ๋ค.
======
Hibernate:
select
m1_0.id,
m1_0.name,
m1_0.team_id
from
member m1_0
where
m1_0.team_id=?
Hibernate:
select
t1_0.id,
t1_0.name
from
team t1_0
where
t1_0.id=?
ํ๋ก1
ํ๋ก2
ํ๋ก3
ํ๋ก4
๊ทธ๋ ๊ฒ ํ๋๋, ์์ ๊ฐ์ด ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ค. ์ฌ๊ธฐ๊น์ง๋ clear ๋ฅผ ํ๋ฉด, ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ค ๋ผ๋ ๋ด ๊ฐ์ค์ ๋ถํฉํ๋ค.
โ 2. ์ฟผ๋ฆฌ ๋ฉ์๋์ ๋ํด EntityManager#clear ๋ฅผ ํธ์ถํ์ง ์๋ ๊ฒฝ์ฐ
======
Hibernate:
select
m1_0.id,
m1_0.name,
m1_0.team_id
from
member m1_0
where
m1_0.team_id=?
ํ๋ก1
ํ๋ก2
ํ๋ก3
ํ๋ก4
clear ๋ฅผ ํธ์ถํ์ง ์์์์๋ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ค. ์ฆ, 1์ฐจ ์บ์์ ์ด๋ฏธ ์กฐํํด์ผ ํ๋ ์ํฐํฐ๊ฐ ๋ชจ๋ ์กด์ฌํจ์๋ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ ๊ฒ์ด๋ค.
clear ๋ฅผ ํ์ง ์์ผ๋ฉด ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ์ง ์๊ณ 1์ฐจ ์บ์๋ง์ ์ฐธ์กฐํ๋ค๋ ๋ด ๊ฐ์ค์ด ํ๋ฆฐ ์ํฉ์ด๋ค.
์ฌ๊ธฐ๊น์ง ๋ดค์ ๋ ์ฐ๋ฆฌ๋ ์ด๋ ดํ์ด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๋ก ์ ๋ด๋ฆด ๊ฒ์ด๋ค.
์! EntityManager#clear ์๋ฌด ์๋ฏธ ์๋ค. ๊ทธ๋ฅ ๋ค ์ฟผ๋ฆฌ ๋๊ฐ๋ค! ์ถ์ด์ง๋ค.
โ 3. findById ์ ๋ํด EntityManager#clear ๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ
๊ทธ๋ ๋ค๋ฉด ๋์ ๊ฐ์ค์ด ํ๋ ธ์์ ๊ฒ์ฆํ๊ธฐ ์ํด ๋ ๋ค๋ฅธ ํ ์คํธ๋ฅผ ํด๋ณด์.
์ด๋ฒ์๋ ์ง์ ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ์ ์ํ์ง ์๊ณ findById ๋ฅผ ํตํด ํ ์คํธ๋ฅผ ํด๋ณผ ๊ฒ์ด๋ค.
@Test
@DisplayName("id ๋ฅผ ํตํด ๋ฉค๋ฒ๋ฅผ ์กฐํํ๋ค")
void test2() {
// given
Team team = new Team("ํ๋ก์ ํ");
entityManager.persist(team);
Member ํ๋ก1 = new Member("ํ๋ก1", team);
entityManager.persist(ํ๋ก1);
Long id = ํ๋ก1.getId();
entityManager.flush();
entityManager.clear();
// then
System.out.println("======");
memberJpaRepository.findById(id);
}
======
Hibernate:
select
m1_0.id,
m1_0.name,
t1_0.id,
t1_0.name
from
member m1_0
left join
team t1_0
on t1_0.id=m1_0.team_id
where
m1_0.id=?
ํ๋ก1
์ฌ์ ํ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ฌ๊ธฐ๊น์ง๋ ์๊น์ ๋๊ฐ๋ค.
โ 4. findById ์ ๋ํด EntityManager#clear ๋ฅผ ํธ์ถํ์ง ์๋ ๊ฒฝ์ฐ
======
ํ๋ก1
์ค์. ์ด๋ฒ์๋ ๋๊ฐ์ง ์์๋ค.
EntityManager#clear ๊ฐ ํ ์คํธ์ ์๋ฌด๋ฐ ์ํฅ์ด ์๋๋ณด๋ค ์ถ์๋ ์์ ๋ ๊ฐ์ค์ด ๋ ๋ค์ ํ๋ฆฐ ๊ฒ์ด๋ค.
๊ทธ๋ฐ๋ฐ ์ด๋ค ์ฐจ์ด์ ์ผ๋ก ์ธํด ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ ๋ํ๋๊ฒ ๋ ๊ฒ์ผ๊น?
์ ๋ต์ findById ์ ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ์ ์๋ค.
MemberJpaRepository#findByTeam ์ ์ง์ ์๋กญ๊ฒ ์ ์ํ ์ฟผ๋ฆฌ ๋ฉ์๋์ด๋ค.
๊ทธ๋ฌ๋ findById ๋ ์ปดํ์ผ ํ์์ ํฌํจ๋๋, ์ฆ JPA ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ๋ฉ์๋์ด๋ค.
๐ธ ์ฟผ๋ฆฌ ๋ฉ์๋์ ์คํ ๊ณผ์
๋ค์ ๋ฌธ์์์ ์ด์ผ๊ธฐํ๊ณ ์๋ฏ์ด, JPA ์ ์ฟผ๋ฆฌ ๋ฉ์๋๋ ๋ด๋ถ์ ์ผ๋ก JPQL ๋ก ๋ณํ์ด ๋๋ค.
JPA Query Methods :: Spring Data JPA
JPA Query Methods :: Spring Data JPA
By default, Spring Data JPA uses position-based parameter binding, as described in all the preceding examples. This makes query methods a little error-prone when refactoring regarding the parameter position. To solve this issue, you can use @Param annotati
docs.spring.io
์๋ฅผ ๋ค์ด, findByLastnameOrFirstname ๋ผ๋ ๋ฉ์๋๋ฅผ ์ ์ํ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ JPQL ์ค๋ํซ์ด ๋๋ค.
… where x.lastname = ?1 or x.firstname = ?2
์ฆ, ์ฐ๋ฆฌ๊ฐ ์ ์ํ ์ฟผ๋ฆฌ ๋ฉ์๋๋ ๋ฉ์๋์ ํํ์์ JPQL ๋ก ๋ณํ์ด ๋๊ณ , ๊ทธ ์ดํ์ ๋ค์ดํฐ๋ธ ์ฟผ๋ฆฌ๊ฐ ๋์ด ์ต์ข ์ ์ผ๋ก ์ํ๋๋ค.
๐ธ findById ์ ๋์ ๋ฐฉ์
public Optional<T> findById(ID id) {
Assert.notNull(id, "The given id must not be null");
Class<T> domainType = this.getDomainClass();
if (this.metadata == null) {
return Optional.ofNullable(this.entityManager.find(domainType, id));
} else {
LockModeType type = this.metadata.getLockModeType();
Map<String, Object> hints = this.getHints();
return Optional.ofNullable(type == null ? this.entityManager.find(domainType, id, hints) : this.entityManager.find(domainType, id, type, hints));
}
}
findById ๋ Hibernate ๊ฐ ์ ๊ณตํ๋ ๊ตฌํ์ฒด์ ์์ค ์ฝ๋๋ฅผ ํตํด ์ง์ ์ ์ผ๋ก ํ์ธํด๋ณผ ์ ์๋ค.
์ฝ๋๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด, EntityManager ์ find ๋ฅผ ํตํด ์ํฐํฐ๋ฅผ ์กฐํํด์จ๋ค. ์ฆ, ์ฟผ๋ฆฌ๋ฅผ ๋ฐ๋ก ๋ณด๋ด๋ ๊ฒ์ด ์๋๋ผ ์ฐ์ ์ 1์ฐจ ์บ์๋ฅผ ํ์ธํ๊ณ ๊ทธ ์ดํ์ 1์ฐจ ์บ์์ ์กด์ฌํ์ง ์์ผ๋ฉด ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ธ๋ค. ๊ทธ๋์ ์์์ entityManage.clear() ๋ฅผ ํด์ 1์ฐจ ์บ์๋ฅผ ๋น์ด ๊ฒฝ์ฐ์๋ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋์ง๋ง, ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ์๋ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ด์ง ์์๋ ๋๋ ๊ฒ์ด๋ค.
๐ธ ๊ทธ๋์ ์ ์ฟผ๋ฆฌ ๋ฉ์๋๋ 1์ฐจ ์บ์๋ฅผ ์ ๋ณด๋๋ฐ?
findById ๋ 1์ฐจ ์บ์๋ฅผ ๋ณด๋ ๊ฒ์ ์ฝ๋๋ฅผ ํตํด ํ์ธํ ์ ์์๋ค.
๊ทธ๋ฌ๋ ์ฟผ๋ฆฌ ๋ฉ์๋๋ ์ 1์ฐจ ์บ์๋ฅผ ๋ณด์ง ์์๊น? ์ด๋ค ํน์ฑ์ ์ํด ๊ทธ๋ฐ ๊ฒ์ผ๊น?
์๋ฐํ ๋งํ๋ฉด ์ฟผ๋ฆฌ ๋ฉ์๋๋ผ์ ๋ผ๊ธฐ๋ณด๋ค๋ JPQL ์ ํตํด ์คํ๋๊ธฐ ๋๋ฌธ์ด๋ค.
JPQL ์ 1์ฐจ ์บ์๋ฅผ ๋ฐ๋ผ๋ณด์ง ์๋๋ค. ์ ๊ทธ๋ด๊น?
1. ์ด์ฐจํผ ํจ์จ์ ์ด์ง ์๋ค.
1์ฐจ ์บ์๋ Map ํํ๋ก ์ ์ฅ์ด ๋๋ค. ์ฌ๊ธฐ์ Key ๋ Id ์ด๊ณ , Value ๋ ํด๋น ์ํฐํฐ์ด๋ค. ์ฆ, pk ๋ฅผ ํตํด 1์ฐจ ์บ์๋ฅผ ์กฐํํ๊ณ ์ ํ ๋์๋ ๋งค์ฐ ๋น ๋ฅด๊ฒ ์ฐพ์ ์ ์๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ ์๋๋ผ๋ฉด, ์ด์ฐจํผ ์ ์ฒด ์ํฐํฐ๋ฅผ ์ ๋ถ ๋์์ผ ํ๊ธฐ์ 1์ฐจ ์บ์๋ฅผ ๋ณด๋ ๊ฒ์ ๋ํ ์ด์ ์ ์ ํ ์ฐพ์ ์๊ฐ ์๋ค.
์ฆ, PK ๊ธฐ๋ฐ ๋ฉ์๋๊ฐ ์๋๊ธฐ์ 1์ฐจ ์บ์๋ฅผ ๋ณด์ง ์๋ ๊ฒ์ด ๊ธฐ๋ณธ ์ค์ ๊ฐ์ธ ๊ฒ์ด๋ค.
2. ๋ฐ์ดํฐ ์ผ๊ด์ฑ ๋ฌธ์
์์์ฑ ์ปจํ ์คํธ์ ์กด์ฌํ๋ ๋ฐ์ดํฐ๊ฐ ์ต์ ์ผ ๊ฒ์ด๋ผ๊ณ ํ์ ์ง์ ์๊ฐ ์๋ค.
๋ง์ฝ, ํ๋์ ํธ๋์ญ์ ๋ด๋ถ์์ ํน์ ํ์ ๋ฉค๋ฒ๋ฅผ 3๋ช ์ ์ฅํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
๊ทธ๋ฌ๋ฉด ๊ทธ ํ๊ณผ ๋ฉค๋ฒ๋ค์ ๋ชจ๋ ์์์ฑ ์ปจํ ์คํธ์ ์กด์ฌํ ๊ฒ์ด๋ค. ๊ทธ๋ ๋ค๊ณ ํด์ ํด๋น ํ์ ๋ฉค๋ฒ๊ฐ ์ ๋ง ๊ทธ 3๋ช ๋ง ์กด์ฌํ ๊น?
๋ต์ ๊ทธ๋ด ์๋ ์๊ณ , ์๋ ์๋ ์๋ค. ๊ทธ๋ ๊ธฐ์ ํ์คํ๊ฒ ํ๊ธฐ ์ํด ๋งค๋ฒ ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์ ์ผ๋ก ๋ณด๋ด๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ๋ ๋ค๋ฅธ ์๋ฌธ์ด ์๊ธด๋ค.
findById ์ ๊ฐ์ด ์ปดํ์ผ ํ์์ ์์ฑ๋๋ ๋ฉ์๋๋ ๋ฌด์กฐ๊ฑด์ ์ผ๋ก JPA ์ 1์ฐจ ์บ์๋ฅผ ๊ฑฐ์ณ์ ์ฟผ๋ฆฌ๊ฐ ์์ ๋๊ฐ์ง ์๋?
๊ทธ๊ฑด ์ฌ์ค ๋ ์๋๋ค.
๐ธ findAll ์ ๋์ ๋ฐฉ์
findAll ์ findById ์ ๊ฐ์ด, ์ปดํ์ผ ํ์์ ์กด์ฌํ๋ ๋ฉ์๋์ด๋ค.
@Test
@DisplayName("๋ฉค๋ฒ๋ค์ ์ ์ฒด ์กฐํํ๋ค")
void test4() {
// given
Team team = new Team("ํ๋ก์ ํ");
entityManager.persist(team);
Member ํ๋ก1 = new Member("ํ๋ก1", team);
entityManager.persist(ํ๋ก1);
Member ํ๋ก2 = new Member("ํ๋ก2", team);
entityManager.persist(ํ๋ก2);
Member ํ๋ก3 = new Member("ํ๋ก3", team);
entityManager.persist(ํ๋ก3);
Member ํ๋ก4 = new Member("ํ๋ก4", team);
entityManager.persist(ํ๋ก4);
entityManager.flush();
entityManager.clear();
// then
System.out.println("======");
memberJpaRepository.findAll();
}
์์ ๊ฐ์ด ์ฌ๋ฌ ๋ฉค๋ฒ๋ค์ ๋ฑ๋กํ๊ณ , ํ ์คํธ๋ฅผ ์คํํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋์จ๋ค.
======
Hibernate:
select
m1_0.id,
m1_0.name,
m1_0.team_id
from
member m1_0
Hibernate:
select
t1_0.id,
t1_0.name
from
team t1_0
where
t1_0.id=?
clear ๋ฅผ ํ์ง ์์ ๊ฒฝ์ฐ์ ์ฟผ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ๋ค.
======
Hibernate:
select
m1_0.id,
m1_0.name,
m1_0.team_id
from
member m1_0
clear ๋ฅผ ํ์ง ์์ ๊ฒฝ์ฐ์๋ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ฉค๋ฒ๊ฐ ์ด๋ฏธ ์์์ฑ ์ปจํ ์คํธ์ ์กด์ฌํ๊ณ ์์์๋ Member ์ ๋ํ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ค.
public List<T> findAll() {
return this.getQuery((Specification)null, (Sort)Sort.unsorted()).getResultList();
}
๊ทธ ์ด์ ๋ findAll ๋ ์ฌ์ค์ JPQL ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
getQuery(...)๊ฐ ๋ฐํํ๋ ๊ฒ์ ๊ฒฐ๊ตญ TypedQuery, ์ฆ JPQL ๊ธฐ๋ฐ์ ์ฟผ๋ฆฌ ๊ฐ์ฒด์ด๋ค.
๐ต ๊ทธ๋ฌ๋ฉด EntityManager#clear ์ ํด?
EntityManager#clear ๋ ๋๋์ฒด ์ ํ๋๊ฑธ๊น?
๋๋ถ๋ถ์ ๋ฉ์๋๋ค์ด JPQL ์ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๊ณ , ๊ทธ๋ ๊ธฐ์ 1์ฐจ ์บ์๋ฅผ ๊ฑฐ์น์ง๋ ์์ํ ๋ฐ ๋ง์ด๋ค.
โ 1์ฐจ ์บ์์ ์๊ด์์ด ์กฐํ๋ฅผ ํ๋๋ผ๋ ์ฐธ์กฐ๋ ํ๋ค.
@Test
@DisplayName("clear ์์ด ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ฉด, ์กฐํ ๊ฒฐ๊ณผ๊ฐ ์ค์ DB์ ๋ค๋ฅผ ์ ์๋ค")
void test7() {
// given
Team team = new Team("ํ๋ก์ ํ");
entityManager.persist(team);
Member member = new Member("ํ๋ก", team);
entityManager.persist(member);
entityManager.flush();
// DB์ ๋ฐ์๋ ์ํ: name = "ํ๋ก"
// when - DB์๋ ๋ฐ์ํ์ง ์์์ง๋ง, ์ํฐํฐ ์ํ๋ง ๋ณ๊ฒฝ
member.rename("ํ๋ก_์์ ");
// then
List<Member> results = memberJpaRepository.findByTeam(team);
System.out.println(results.getFirst().getName());
}
์์ ์ฝ๋์์ member ์ ์ด๋ฆ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์์๋ ํ๋ก ์ด์ง๋ง, ์ํฐํฐ ์์ผ๋ก๋ 'ํ๋ก_์์ ' ์ด๋ค.
๊ทธ๋ ๊ธฐ์ ์์ ๋ฉ์๋๋ฅผ ์คํํ ๊ฒฐ๊ณผ๋ 'ํ๋ก'๊ฐ ๋์ด์ผ ๋ง๋ค.
'ํ๋ก_์์ '์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ๋ฐ์์ด ๋์ง ์์๊ธฐ์ JPQL ์ด ๋ชจ๋ฅด๋ ์ ๋ณด์ฌ์ผ ํ๋ค. ํ์ง๋ง ๊ฒฐ๊ณผ๋ 'ํ๋ก_์์ ' ์ด ๋์ค๊ฒ ๋๋ค.
์๋ํ๋ฉด, JPQL ์ 1์ฐจ ์บ์์ ์๊ด์์ด ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๊ธด ํ๋ค. ๊ทธ๋ฌ๋ ์ฟผ๋ฆฌ๋ฅผ ๋ง์น๊ณ ์ํฐํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋, 1์ฐจ ์บ์๋ฅผ ๋ค์ ๋ณด๊ฒ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
์ด ๊ณผ์ ์์ ์ด๋ฏธ ๊ฒฐ๊ณผ์ ํด๋นํ๋ PK ๊ฐ 1์ฐจ ์บ์์ ์กด์ฌํ๋ค๋ฉด ํด๋น ์ํฐํฐ๋ฅผ ์๋ก ๋ง๋ค์ง ์๊ณ ๊ทธ๋๋ก ๊ฐ์ ธ์จ๋ค.
1 | member ์ ์ฃผ์๊ฐ |
member ์ id ๊ฐ 1์ด๋ผ๊ณ ๊ฐ์ ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์์์ฑ ์ปจํ ์คํธ๊ฐ ๊ตฌ์ฑ๋์ด์์ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ member ์ ์ด๋ฆ์ 'ํ๋ก_์์ '์ด ๋์ด ์์ ๊ฒ์ด๋ค.
id | name | team_id |
1 | ํ๋ก | 1 |
JPQL ์ด ์กฐํํด ์จ ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ ์์ ๊ฐ์์ ๊ฒ์ด๋ค. pk ๋ 1์ด๊ณ , ์ด๋ฆ์ 'ํ๋ก_์์ '์ด ์๋๋ผ 'ํ๋ก'์์ ๊ฒ์ด๋ค.
์ฌ๊ธฐ์ JPQL ์ ์ฟผ๋ฆฌ ์กฐํ ๊ฒฐ๊ณผ์ ์๊ด์์ด 1์ฐจ ์บ์์ pk ๊ฐ 1์ธ ์ํฐํฐ๊ฐ ์กด์ฌํ๊ธฐ์, ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํด ์จ ๋ฐ์ดํฐ๋ฅผ ํ ๋๋ก ์ํฐํฐ๋ฅผ ์๋ก ๋ง๋ค์ง ์๊ณ ๊ธฐ์กด์ ์์์ฑ ์ปจํ ์คํธ์ ์กด์ฌํ๋ ๊ฐ๋ค์ ๊ทธ๋๋ก ์ฌ์ฉํ๊ฒ ๋๋ค.
๊ทธ๋์ ๊ฒฐ๊ตญ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํด ์จ ๋ฐ์ดํฐ๊ฐ ์๋๋ผ ์ํฐํฐ๋ฅผ ํ ๋๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
@Test
@DisplayName("clear ์์ด ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ฉด, ์กฐํ ๊ฒฐ๊ณผ๊ฐ ์ค์ DB์ ๋ค๋ฅผ ์ ์๋ค")
void test7() {
// given
Team team = new Team("ํ๋ก์ ํ");
entityManager.persist(team);
Member member = new Member("ํ๋ก", team);
entityManager.persist(member);
entityManager.flush();
// DB์ ๋ฐ์๋ ์ํ: name = "ํ๋ก"
// when
member.rename("ํ๋ก_์์ ");
entityManager.clear();
// DB์ ๋ฐ์๋ ์ํ: name = "ํ๋ก"
// then
List<Member> results = memberJpaRepository.findByTeam(team);
System.out.println(results.getFirst().getName());
}
๋ง์ฝ ์์ ๊ฐ์ด clear ๋ฅผ ํ๊ฒ ๋๋ฉด, ์์์ฑ ์ปจํ ์คํธ์ ์๋ฌด๋ฐ ์ํฐํฐ๊ฐ ์กด์ฌํ์ง ์๊ธฐ์ ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ฅผ ํ ๋๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ฒ ๋๋ค.
flush ๋ฅผ ํ ๊ฒ๋ ์๋๊ธฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์์๋ ์ฌ์ ํ 'ํ๋ก'๋ก ์กด์ฌํ๋ค. ๋ฐ๋ผ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ํํ ๊ฒฐ๊ณผ๊ฐ ๋์ค๊ฒ ๋๋ ๊ฒ์ด๋ค.
์ด๋ ๊ฒ EntityManager ์ clear ๋ฅผ ์ฌ์ฉํ๋ฉด, ์์์น ๋ชปํ ๋ถ์ ํฉ์ ๋ฐฉ์งํ ์ ์๊ฒ ๋๋ค.
โ ์ฒ์์ผ๋ก ๋์๊ฐ์ ๋ค์ ์ง์ด๋ณด์
๋์ ์ฒซ ๊ฐ์ค์ ๋ค์๊ณผ ๊ฐ์๋ค.
“EntityManager๋ฅผ clearํ์ง ์์ผ๋ฉด, ์ค์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ๊ฒ์ด ์๋๋ผ 1์ฐจ ์บ์์์ ๊ฐ์ ์กฐํํ ๊ฒ์ด๋ค.
๋ฐ๋ผ์ ํ ์คํธ์ ์ ํ๋๋ฅผ ์ํด clear๊ฐ ํ์ํ๋ค.”
ํ์ง๋ง ํ ์คํธ๋ฅผ ๋ฐ๋ณตํ๋ฉด์ ๋ค์๊ณผ ๊ฐ์ ์ฌ์ค์ ์๋กญ๊ฒ ์๊ฒ ๋์๋ค.
1. ํญ์ 1์ฐจ ์บ์๋ง์ ์ฐธ์กฐํ๋ ๊ฒ์ ์๋๋ค.
EntityManager.clear()๋ฅผ ํธ์ถํ์ง ์์๋, ์ํฉ์ ๋ฐ๋ผ ์ค์ ์ฟผ๋ฆฌ๊ฐ ๋ ์๊ฐ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์ฆ, clear ์ฌ๋ถ๊ฐ ๋ฌด์กฐ๊ฑด ์ฟผ๋ฆฌ ์คํ ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ์ง์ง๋ ์๋๋ค.
2. ํ ์คํธ์ ์ ํ๋๋ "์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋๋" ๋ง์ผ๋ก ๊ฒฐ์ ๋์ง๋ ์๋๋ค.
์ด๋ค ์ฟผ๋ฆฌ๊ฐ ์คํ๋์๋๋๋ ์ค์ํ์ง๋ง, ๋ ์ค์ํ ๊ฑด ๊ทธ ์ฟผ๋ฆฌ๊ฐ ์ด๋ค ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๊ฐ ์ด๋ค ์ปจํ ์คํธ์์ ์ฌ์ฉ๋๋๋ ์ด๋ค. ์ฆ, 1์ฐจ ์บ์์ ์ํฅ๋ฟ ์๋๋ผ ์กฐํ๋ ๊ฐ์ฒด์ ์ํ, flush ์ฌ๋ถ ๋ฑ๋ ํ ์คํธ์ ์ ๋ขฐ๋์ ํฐ ์ํฅ์ ๋ฏธ์น๋ค.
๊ฒฐ๊ตญ clear()๋ ๋จ์ํ ์ฟผ๋ฆฌ ์ ๋ฌด๋ฅผ ํต์ ํ๊ธฐ ์ํ ์๋จ์ด๋ผ๊ธฐ๋ณด๋ค, ์ํฐํฐ์ ์ํ๋ฅผ ์ด๊ธฐํํ์ฌ ๋ช ํํ ํ ์คํธ ์กฐ๊ฑด์ ๊ตฌ์ฑํ๊ธฐ ์ํ ๋๊ตฌ์ธ ๊ฒ์ด๋ค.
โ๏ธ ๊ฒฐ๋ก
- JPQL ์ 1์ฐจ ์บ์์ ์ํฐํฐ๊ฐ ์กด์ฌํ๋๋ผ๋ ๋ฌด์กฐ๊ฑด์ ์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฐ๋ค.
- ์ฟผ๋ฆฌ๋ 1์ฐจ ์บ์์ ์ฐ๊ด์ฑ์ด ์์ง๋ง, ์กฐํํด์จ ๋ค์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ ๋์๋ ์ํฅ์ ๋ฐ๋๋ค.
- EntityManager#clear ๋ 1์ฐจ ์บ์๋ก ์ธํ ์์์น ๋ชปํ ๋ถ์ ํฉ์ ๋ฐฉ์งํ ์ ์๋๋ก ํด์ค๋ค.
- ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋๋ ์๋๋ ๋ง์ด ํ ์คํธ์ ์ํฅ์ ์ฃผ๋ ์์ธ์ ์๋๋ค
โ๏ธ ๋๋ ์
๊ฐ์ธ์ ์ผ๋ก ๋ด๋ฆฐ ๊ฒฐ๋ก ์ ํ ์คํธ ๊ณผ์ ์์ EntityManager#clear ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ทธ๋ ๊ฒ๊น์ง ์๋ชป๋ ์ต๊ด์ ์๋ ๊ฒ ๊ฐ๋ค.
์๋์น ์์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฉ์งํ ์ ์๊ณ , ์ ๋ง ํ ์คํธํ๊ณ ์ ํ๋ ๋์์ ์ง์คํ ์ ์๋ ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ ๋ฐ์ ๋์์ด ๋๊ธฐ๋ ํ๋ค.
๊ทธ๋ฌ๋, ์ด์ ๋ํด ๋ฌด์์์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ๊ณผ ์๋ ๊ฒ์๋ ๋ถ๋ช ํ ์ฐจ์ด๊ฐ ์๋ค๊ณ ์๊ฐ์ด ๋ ๋ค. ๋ด๊ฐ ์ด๋ค ๋ชฉ์ ์ผ๋ก clear ๋ฅผ ํ๋ ค๊ณ ํ๋์ง ํ๋ฒ ๋ ๋ฉ์ถฐ ์์ JPA ์ ๋์ ๋ฐฉ์์ ๋ํด ๊ณ ๋ฏผํด๋ณธ๋ค๋ฉด ๋ ์ข์ ๊ฐ๋ฐ์๋ก ์ฑ์ฅํ ์ ์์ง ์์๊น ํ๋ ์๊ฐ์ด ๋ค์๋ค.
๋ฌด์์ด๋ ๊ด์ต์ ์ผ๋ก, ์๋์ ์ผ๋ก ๋ฐ์๋ค์ด์ง ๋ง์๋ ์๊ฐ์ ๋ค์๊ธ ํ๊ฒ ๋๋ค.