Intro
๋ค๋๋ค ํ ์ด๋ธ์ ๋ณดํต ์ผ๋๋ค ↔ ๋ค๋์ผ ํ ์ด๋ธ๋ก ํ์ด์ ๊ตฌ์ฑํ๊ณค ํฉ๋๋ค. ์ค๊ฐ์ ์ด์ด์ฃผ๋ ํ ์ด๋ธ์ ์ค๊ฐ ํ ์ด๋ธ์ด๋ผ๊ณ ๋ถ๋ฅด๋๋ฐ ์ด ํ ์ด๋ธ์ PK๊ฐ ์์ชฝ ํ ์ด๋ธ์ PK๋ฅผ ๊ฐ์ง๊ณ ๋ณตํฉํค๋ฅผ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
JPA์์ ๋ณตํฉํค๋ก PK๋ฅผ ๊ตฌ์ฑํ์ ๋, ์ ์ฅ ๋๋ PK๋ฅผ ์ ๋ฐ์ดํธ๋ฅผ ํด์ผํ ๋ ์ฃผ์ํด์ผ ํ ์ ์ ๋ํด์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๊ฐ๋ฐํ๊ฒฝ
- SpringBoot 2.5.11
- Java 11
- H2 memory db
ํ ์คํธ์ฉ ์ํฐํฐ ์๊ฐ
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
public Member(String name) {
this.name = name;
}
public Member(String name, Team team) {
this.name = name;
this.team = team;
}
public void updateTeam(Team team) {
this.team = team;
}
}
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Member> members = new ArrayList<>();
public Team(String name) {
this.name = name;
}
public void addMember(Member member) {
this.members.add(member);
member.updateTeam(this);
}
}
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class MemberTeam {
@EmbeddedId
private MemberTeamId id;
@MapsId("memberId")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member;
@MapsId("teamId")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
private String description;
public MemberTeam(Member member, Team team) {
this.id = MemberTeamId.of(team.getId(), member.getId());
this.member = member;
this.team = team;
this.description = "none";
}
public void changeMember(Member member) {
this.member = member;
}
public void changeDescription(String description) {
this.description = description;
}
}
์ํฐํฐ ์ ์ฅ ํ ์คํธ
๋ณตํฉํค๋ฅผ ๊ฐ์ง ์ํฐํฐ๋ฅผ ์์ ํ๊ฑฐ๋ ์ ์ฅํ ๋ ์ค์ํ ์ ์๋ ์ผ์ด์ค๋ค์ ํ ์คํธ๋ฅผ ํตํด์ ์์๋ณด๊ฒ ์ต๋๋ค.
ํ ์คํธ ๊ธฐ๋ณธ ๊ตฌ์กฐ
@DataJpaTest
class MemberTeamRepositoryTest {
@Autowired private MemberRepository memberRepository;
@Autowired private TeamRepository teamRepository;
@Autowired private MemberTeamRepository memberTeamRepository;
@Autowired private EntityManager em;
private Long member1Id;
private Long member2Id;
private Long team1Id;
private Long team2Id;
@Commit
@BeforeEach
void setup() {
Member member1 = new Member("member1");
Member member2 = new Member("member2");
Team team1 = new Team("team1");
Team team2 = new Team("team2");
team1.addMember(member1);
team1.addMember(member2);
teamRepository.saveAndFlush(team1);
teamRepository.saveAndFlush(team2);
MemberTeam memberTeam = new MemberTeam(member1, team1);
memberTeamRepository.saveAndFlush(memberTeam);
MemberTeam memberTeam2 = new MemberTeam(member1, team2);
memberTeamRepository.saveAndFlush(memberTeam2);
this.member1Id = member1.getId();
this.member2Id = member2.getId();
this.team1Id = team1.getId();
this.team2Id = team2.getId();
em.flush();
em.clear();
}
}
๋ณ๊ฒฝํ๋ ค๋ ์ํฐํฐ๋ง ์์ ํ๋ ๊ฒฝ์ฐ
@Test
void MemberTeam์_๋ณ๊ฒฝํ ์์๋ค() throws Exception {
// given
MemberTeam memberTeam = memberTeamRepository.findById(MemberTeamId.of(team1Id, member1Id)).orElseThrow();
// when
Member member2 = memberRepository.findById(member2Id).orElseThrow();
memberTeam.changeMember(member2);
MemberTeam modifiedMemberTeam = memberTeamRepository.save(memberTeam);
assertThat(modifiedMemberTeam.getId().getMemberId()).isEqualTo(member2Id);
}
์์ ๊ฐ๋จํ๊ฒ ํ๋์ ํ์ ์ํ 2๋ช ์ Member๋ฅผ ์ ์ฅํ๊ณ , member1๊ณผ team1์ MemberTeam์ ํ๋ ์ ์ฅํ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ memberTeam์ ์๋ member1์ member2๋ก ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํ๋ ค๊ณ ํฉ๋๋ค.
- member1๊ณผ team1์ ๋ํ memberTeam์ ์กฐํํ๋ค.
- memberTeam ๋ด๋ถ์ ์๋ changeMember๋ฅผ ํตํด member๋ฅผ ๋ณ๊ฒฝํ๋ค.
- ๋ณ๊ฒฝ๋ memberTeam์ ๋ค์ ์ ์ฅํ๋ค.
์ ํ ์คํธ๋ ํต๊ณผ์ผ๊น์? ์คํจ์ผ๊น์?
์คํ๊ฒฐ๊ณผ
org.opentest4j.AssertionFailedError:
expected: 3L
but was : 2L
Expected :3L
Actual :2L
์คํ๊ฒฐ๊ณผ๋ ๋น์ฐํ ์คํจ์ ๋๋ค. ์๊ทธ๋ด๊น์?
๋ณตํฉํค๋ฅผ ์ฌ์ฉํ๋ ์ํฐํฐ๋ฅผ ์์ ํ ๋ ๊ฐ์ฅ ํท๊ฐ๋ฆฌ๋ ๋ถ๋ถ์ธ๋ฐ, MemberTeam์ ๋ณตํฉํค๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๊ทผ๋ฐ member๋ง ๋ฐ๊พผ๋ค๊ณ ๋ฐ์์ด ๋ ๊น์?
์ฐ๋ฆฌ๋ MemberTeam์ ์์ด๋์ธ MemberTeamId์ ํจ๊ป ๋ฐ๊ฟ์ค์ผ ํฉ๋๋ค.
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Test
void MemberTeam์_๋ณ๊ฒฝํ ์์๋ค() throws Exception {
// given
MemberTeam memberTeam = memberTeamRepository.findById(MemberTeamId.of(team1Id, member1Id)).orElseThrow();
// when
Member member2 = memberRepository.findById(member2Id).orElseThrow();
memberTeam.changeMember(member2);
MemberTeam modifiedMemberTeam = memberTeamRepository.save(memberTeam);
assertThat(modifiedMemberTeam.getId().getMemberId()).isEqualTo(member2Id);
}
๋ณตํฉํค ์ ์ฒด๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ
์์์ ์ดํด๋ณธ ์์ ์์ ๋ณตํฉํค๋ฅผ ๋ณ๊ฒฝํด๋ณด๋๋ก ํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
์๋๋ DeployLogId๋ฅผ ์๋ก์ด ๊ฐ์ฒด๋ก ๋ณ๊ฒฝํ๋ ์ฝ๋์ ๋๋ค.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class MemberTeam {
...
public void changeMember(Member member) {
this.id = MemberTeamId.of(this.team.getId(), member.getId());
this.member = member;
}
}
์คํ๊ฒฐ๊ณผ๋?
org.springframework.orm.jpa.JpaSystemException: identifier of an instance of com.in2wise.in8opsbe.entity.deployLog.DeployLog was altered from com.in2wise.in8opsbe.entity.deployLog.DeployLogId@90404d71 to com.in2wise.in8opsbe.entity.deployLog.DeployLogId@90404d90; nested exception is org.hibernate.HibernateException: identifier of an instance of com.in2wise.in8opsbe.entity.deployLog.DeployLog was altered from com.in2wise.in8opsbe.entity.deployLog.DeployLogId@90404d71 to com.in2wise.in8opsbe.entity.deployLog.DeployLogId@90404d90
JPA์์๋ ๊ฐ์ฒด์ ๋์ผ์ฑ ๋น๊ต๋ฅผ ์ง์ํ๋๋ฐ ์ ์ฝ๋๋ก ์ธํด์ DeployLogId์ ๊ฐ์ฒด์ ์ฃผ์๊ฐ ๋ณ๊ฒฝ๋์๊ธฐ ๋๋ฌธ์, ์๋์ ๊ฐ์ ์๋ฌ๊ฐ ์๊น๋๋ค.
์ ๋ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํ์ง ๋ง๊ณ ๊ธฐ์กด Id ๊ฐ์ฒด์์ ๋ณ๊ฒฝํ๋ ค๋ ํค๊ฐ๋ง ์์ ํ๋๋ก ํด์ผํฉ๋๋ค.
๋ณตํฉํค ๋ด๋ถ ๋ณ์๋ฅผ ๋ฐ๊ฟ๋ณด์
์ ์์ ์ฒ๋ผ ์ ์ฒด ํต์ผ๋ก ๋ฐ๊พธ์ง๋ง๊ณ ID ํด๋์ค ๋ด๋ถ ๋ณ์๋ฅผ ๋ณ๊ฒฝํด์ฃผ๋๋ก ํฉ์๋ค.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class MemberTeam {
...
public void changeTrainedModel(TrainedModel trainedModel) {
this.member = member;
this.id.changeMemberId(member.getId()); // **
}
}
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(staticName = "of")
@Embeddable
public class MemberTeamId implements Serializable {
private Long teamId;
private Long memberId;
public void changeMemberId(Long memberId) {
this.memberId = memberId;
}
...
}
์ด์ ์๋ ํ ์คํธ๋ฅผ ์คํํด๋ณด๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
@Test
@Transactional
void MemberTeam์_๋ณ๊ฒฝํ ์์๋ค() throws Exception {
// given
MemberTeam memberTeam = memberTeamRepository.findById(MemberTeamId.of(team1Id, member1Id)).orElseThrow();
// when
Member member2 = memberRepository.findById(member2Id).orElseThrow();
memberTeam.changeMember(member2);
MemberTeam modifiedMemberTeam = memberTeamRepository.saveAndFlush(memberTeam);
// then
(1) assertThat(modifiedMemberTeam.getId().getMemberId()).isEqualTo(member2Id);
(2) assertThat(memberTeamRepository.count()).isEqualTo(2);
(3) assertThat(memberTeamRepository.findById(MemberTeamId.of(team1Id, member1Id))).isNotPresent();
}
์คํ๊ฒฐ๊ณผ
[1] o.h.p.entity.AbstractEntityPersister : HHH000502: The [member] property of the [me.iseunghan.testjpa.domain.MemberTeam] entity was modified, but it won't be updated because the property is immutable.
...
Expecting an empty Optional but was containing value: me.iseunghan.testjpa.domain.MemberTeam@5f01361e, mergedContextConfiguration = [WebMergedContextConfiguration@d3957fe testClass = MemberTeamRepositoryTest, locations = '{}', classes = '{class me.iseunghan.testjpa.TestJpaApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4d14b6c2, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@25df00a0, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@1be2019a, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@28cda624, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@14bdbc74, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@11dc3715], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
java.lang.AssertionError:
[2] Expecting an empty Optional but was containing value: me.iseunghan.testjpa.domain.MemberTeam@5f01361e
- [1]: MemberTeam์ member๋ Immutable์ด๋ผ ๋ณ๊ฒฝํ ์ ์๋ค๋ ๊ฒฝ๊ณ ์ ํจ๊ป Update ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ์ง ์์์ต๋๋ค.
- [2]: ๊ฐ์ฒด์์ผ๋ก๋ ๋ณ๊ฒฝ๋์ง๋ง, JPA์ชฝ์์ ๋ณ๊ฒฝ์ ๋ง์ ์ค์ DB์๋ ๋ฐ์์ด ๋์ง ์์์ (1)์ ํต๊ณผํ๊ณ (3)์ ์คํจํ์ต๋๋ค.
๊ทธ๋ผ ์ฌ๊ธฐ์ ์ฐ๋ฆฌ๋ ๋ค์์ ์ ์ ์์ต๋๋ค.
- ๋ณตํฉํค๋ Immutable์ด๋ผ ์์ ์ด ๋์ง ์๋๋ค.
- ๊ฐ์ฒด ์์ผ๋ก ์์ ์ด ๋์ง๋ง JPA์์๋ Update๋ฅผ ๋ง๋๋ค.
๋ง์ฝ ๋ค๋ฅธ ์ปฌ๋ผ์ ํจ๊ป ๋ณ๊ฒฝ ์๋ํ๋ค๋ฉด?
@Commit
@Transactional
@Test
void MemberTeam์_๋ณ๊ฒฝํ ์์๋ค() throws Exception {
System.out.println("-----------------------------------");
// given
MemberTeam memberTeam = memberTeamRepository.findById(MemberTeamId.of(team1Id, member1Id)).orElseThrow();
Member member2 = memberRepository.findById(member2Id).orElseThrow();
// when
memberTeam.changeText("edit");
memberTeam.changeMember(member2);
MemberTeam modifiedMemberTeam = memberTeamRepository.saveAndFlush(memberTeam);
// then
assertThat(modifiedMemberTeam.getId().getMemberId()).isEqualTo(member2Id);
assertThat(memberTeamRepository.count()).isEqualTo(2);
assertThat(memberTeamRepository.findById(MemberTeamId.of(team1Id, member1Id))).isNotPresent();
}
์คํ๊ฒฐ๊ณผ
2023-07-28 14:44:30.248 WARN 90834 --- [1] main] o.h.p.entity.AbstractEntityPersister : HHH000502: The [member] property of the [me.iseunghan.testjpa.domain.MemberTeam] entity was modified, but it won't be updated because the property is immutable.
2023-07-28 14:44:30.248 DEBUG 90834 --- [2] main] org.hibernate.SQL :
update
member_team
set
text=?
where
member_id=?
and team_id=?
2023-07-28 14:44:30.248 TRACE 90834 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [edit]
2023-07-28 14:44:30.249 TRACE 90834 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [3]
2023-07-28 14:44:30.249 TRACE 90834 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [1]
2023-07-28 14:44:30.250 DEBUG 90834 --- [3] main] org.hibernate.SQL :
select
memberteam0_.member_id as member_i1_1_0_,
memberteam0_.team_id as team_id2_1_0_,
memberteam0_.text as text3_1_0_
from
member_team memberteam0_
where
memberteam0_.member_id=?
and memberteam0_.team_id=?
2023-07-28 14:44:30.251 TRACE 90834 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [3]
2023-07-28 14:44:30.252 TRACE 90834 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2023-07-28 14:44:30.253 DEBUG 90834 --- [ main] .l.e.p.AbstractLoadPlanBasedEntityLoader : Done entity load : me.iseunghan.testjpa.domain.MemberTeam#me.iseunghan.testjpa.domain.MemberTeamId@3e3
2023-07-28 14:44:30.259 DEBUG 90834 --- [ main] cResourceLocalTransactionCoordinatorImpl : JDBC transaction marked for rollback-only (exception provided for stack trace)
...
[4] org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [me.iseunghan.testjpa.domain.MemberTeam] with identifier [me.iseunghan.testjpa.domain.MemberTeamId@3e3]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [me.iseunghan.testjpa.domain.MemberTeam#me.iseunghan.testjpa.domain.MemberTeamId@3e3]
- [1]: ์์์ ๋ดค๋ Id๊ฐ ๋ณ๊ฒฝ๋์๋ค๊ณ ๊ฒฝ๊ณ ํ๋ ๋ก๊ทธ. ๊ฐ์ฒด์ memberId๋ ๋ณ๊ฒฝ๋์์ง๋ง ์ค์ DB์๋ ๋ฐ์ ์๋จ.
- [2]: text๋ฅผ ์์ ํ update ์ฟผ๋ฆฌ ๋ฐ์.
- [3]: save ์ MemberTeamId๋ก ๋จผ์ ์กฐํ
- [4]: update๋ฌธ์๋ member๋ฅผ ๋ณ๊ฒฝํ ๋ถ๋ถ์ JPA์ ์ํด ์ ์ธ๋์ง๋ง, ์ด๋ฏธ MemberTeamId๋ ์์ ๋์์ต๋๋ค. ๊ทธ๋ ๋ค๋ณด๋ JPA๋ 1์ฐจ์บ์์ ์๋ ์ค๋ ์ท๊ณผ ๋น๊ตํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์์ธ๋ฅผ ํฐ๋จ๋ฆฌ๋ ๊ฒ์ ๋๋ค.
๊ทธ๋ ๋ค๋ฉด ํด๊ฒฐ๋ฐฉ๋ฒ์?
- ๊ฐ์ฅ ํ๋ช ํ ๋ฐฉ๋ฒ์ ๋ณตํฉํค๋ ์ ๋ ๋ณ๊ฒฝํ์ง ์๋ ๊ฒ์ ๋๋ค. ๋ณ๊ฒฝํ๋ ค๋ฉด ๊ธฐ์กด ์ํฐํฐ๋ฅผ ์ญ์ ํ๊ณ ์๋ก์ด ๊ฐ์ผ๋ก ์ ์ฅํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
- AutoIncrement๋ฅผ ๊ธฐ๋ณธํค๋ก ์ค์ ํ๊ณ ๋ณตํฉํค๋ฅผ ๋์ฒดํค๋ก ๋ณ๊ฒฝํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
1๋ฒ์งธ ๋ฐฉ๋ฒ์ ๊ทธ๋ฅ ์ญ์ ํ๊ณ ๋ค์ ์์ฑํ๋ฉด ๋๋ ๊ฒ์ด๋ฏ๋ก ์ค์ต์ ๊ฑด๋๋ฐ๊ณ , 2๋ฒ์งธ ๋ฐฉ๋ฒ์ ์ค์ต์ ํตํด ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
MemberTeamId๋ฅผ ๋์ฒดํค๋ก ๋ณ๊ฒฝํ๊ธฐ
AutoIncrement ๊ฐ์ PK๋ก ๋ณ๊ฒฝํ๊ณ , member์ team์ ๊ฐ์ด ๋ฌถ์ด์ unique ์ ์ฝ์ ์ค์ ํด์ค๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด ์ด์ PK๊ฐ ๋ณ๊ฒฝ๋ ์ผ์ ์์ ๊ฒ ์ ๋๋ค.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"MEMBER_ID", "TEAM_ID"})})
public class MemberTeam {
@Id @GeneratedValue
private Long candidateId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID", nullable = false)
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID", nullable = false)
private Team team;
private String text;
public MemberTeam(Member member, Team team) {
this.member = member;
this.team = team;
}
public void changeMember(Member member) {
this.member = member;
}
public void changeText(String text) {
this.text = text;
}
}
๋ณ๊ฒฝ๋ ํ ์คํธ ์ฝ๋
@Test
void MemberTeam์_๋ณ๊ฒฝํ ์์๋ค() throws Exception {
System.out.println("-----------------------------------");
// given
MemberTeam memberTeam = memberTeamRepository.findById(memberTeam1CandidateId).orElseThrow();
Member member2 = memberRepository.findById(member2Id).orElseThrow();
// when
memberTeam.changeText("edit");
memberTeam.changeMember(member2);
MemberTeam modifiedMemberTeam = memberTeamRepository.saveAndFlush(memberTeam);
// then
assertThat(modifiedMemberTeam.getMember().getId()).isEqualTo(member2Id);
assertThat(memberTeamRepository.count()).isEqualTo(2);
assertThat(memberTeamRepository.findById(MemberTeamId.of(team1Id, member1Id))).isNotPresent();
}
์คํ๊ฒฐ๊ณผ
2023-07-28 15:50:22.333 DEBUG 95256 --- [1] main] org.hibernate.SQL :
update
member_team
set
member_id=?,
team_id=?,
text=?
where
candidate_id=?
2023-07-28 15:50:22.335 TRACE 95256 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [3]
2023-07-28 15:50:22.335 TRACE 95256 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2023-07-28 15:50:22.335 TRACE 95256 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [edit]
2023-07-28 15:50:22.335 TRACE 95256 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [BIGINT] - [5]
- [1]: ์ ์์ ์ผ๋ก update Query๊ฐ ๋ฐ์ํ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
Outro
JPA๋ ํธ๋ฆฌํ๊ฒ ๊ฐ์ฒด์งํฅ์ ์ผ๋ก DB๋ฅผ ํธ๋ค๋งํ ์ ์๊ฒ ๋์์ฃผ๋ ํ๋ ์์ํฌ์ด์ง๋ง ๊ทธ๋งํผ ์ถ์ํ๋์ด์๊ณ ๋ด๋ถ๋์์๋ฆฌ๋ฅผ ์ ์์์ผ ์ค๋ฌด์์ ์ฅ์ ๋ก ์ด์ด์ง์ง ์๊ณ ์ข์ ์๋น์ค๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. JPA์ ๋ํด์ ๊น๊ฒ ํ์ตํ๋๋ฐ ์ง์ค์ ํด์ผํ ๊ฒ ๊ฐ์ต๋๋ค.
๊ธด ๊ธ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
(ํ๋ฆฐ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ๋ง์ํด์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค)