์ ๋ ์ง๊ธ๊น์ง ์ผ๋ฐ์ ์ผ๋ก JPA๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ @Id์ @GenerateValue, ๊ทธ๋ฆฌ๊ณ Long ํ์ ์ id๋ฅผ ์ฌ์ฉํ์์ต๋๋ค.
ํญ์ id๋ ์ง์ ํ ๋นํ์ง ์์๊ธฐ์ save()์ id์ ๊ฐ์ ํญ์ null์ด์์ต๋๋ค.
๊ทธ๋ฌ๋ค ์ฝํ๋ฆฐ์ ๊ณต๋ถํ๊ณ , ์ฝํ๋ฆฐ์ผ๋ก JPA๋ฅผ ์ฌ์ฉํด๋ณด๋ ค๋ ์ค, ์ฝํ๋ฆฐ์์ id์ null์ ํ์ฉํ๊ฒ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ค๋ฉด ์ฝ๋๊ฐ ๋๋ฌด ๋๋ฌ์์ง ๊ฒ ๊ฐ์์, null์ ์ฌ์ฉํ์ง ์๋ ๋ฐฉ๋ฒ์ ์ฐพ์๋ณด์์ต๋๋ค.
์๊ฐ๋ณด๋ค ๊ฐ๋จํ๋๋ฐ ๊ฒฐ๋ก ์ 0์ผ๋ก ์ด๊ธฐํ ์์ผ์ฃผ๋ฉด ๋๋ค์์ต๋๋ค.
ํน์ nullable ํ์ ์ ์ฌ์ฉํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ ๋๋ค.
์ด์ ๋ถํฐ ์ด๋ฌํ ์ด์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
save()๋ ์ด๋ป๊ฒ ์๋ํ ๊น?
์ ์ฅํ๋ ค๋ ์ํฐํฐ๊ฐ ์๋ก์ด ์ํฐํฐ์ธ ๊ฒฝ์ฐ persist๋ฅผ,
๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ merge๋ฅผ ์งํํฉ๋๋ค.
JpaRepository ์ฌ์ฉ ์ save()๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ๋์ด ์์ต๋๋ค.
์ ์ฅํ๋ ค๋ entity๊ฐ ์๋ก์ด ์ํฐํฐ์ธ ๊ฒฝ์ฐ persist๋ฅผ, ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ merge๋ฅผ ์งํํฉ๋๋ค.
merge๊ฐ ์๋ํ๋ ๋ฐฉ์์ ๋ค์ ๊ฒ์๋ฌผ(https://ttl-blog.tistory.com/167)์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
๊ฒฐ๋ก ์ ์ผ๋ก๋ select ์ฟผ๋ฆฌ์ insert ์ฟผ๋ฆฌ๊ฐ ๋๋ฒ ์คํ๋ฉ๋๋ค.
์ฆ save์ merge๊ฐ ํธ์ถ๋๋ค๋ฉด ๋ถํ์ํ ์ฟผ๋ฆฌ๊ฐ 1๋ฒ ๋ ๋ฐ์ํ๋ฏ๋ก ์ฑ๋ฅ์ ์ ์ํฅ์ ๋ฏธ์น๋ฉฐ, ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ๋ ์์ด๋ณด์ ๋๋ค.
์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด merge๊ฐ ํธ์ถ๋์ง ์๋๋ก ํด์ผ ํ๋๋ฐ, ์๋ก์ด ์ํฐํฐ๋ฅผ ํ๋จํ๋ ๊ทผ๊ฑฐ๊ฐ ๋ฌด์์ธ์ง ์์๋ณผ ํ์๊ฐ ์์ต๋๋ค.
์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์๋ก์ด ์ํฐํฐ๋ฅผ ํ๋จํ๋ ๊ทผ๊ฑฐ
@Id์ ํ์ ์ด primitive ํ์ ์ด ์๋ ๊ฒฝ์ฐ์๋ null์ธ ๊ฒฝ์ฐ๋ง ์๋ก์ด ์ํฐํฐ๋ก ํ๋จํฉ๋๋ค.
๊ทธ๋ฌ๋ primitive ํ์ ์ค, ํด๋น Wrapper ํด๋์ค๊ฐ Number์ ํ์ ํ์ ์ธ ๊ฒฝ์ฐ(int, long ๋ฑ)์๋ ํด๋น ๊ฐ์ด 0์ธ ๊ฒฝ์ฐ์๋ง ์ด๋ฅผ ์๋ก์ด ์ํฐํฐ๋ผ ํ๋จํฉ๋๋ค.
entityInformation์ ์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ JpaMetamodelEntityInformation์ด ์ฌ์ฉ๋ฉ๋๋ค.
์ฆ JpaMetamodelEntityInformation์ isNew()๊ฐ ์ฌ์ฉ๋๋ฉฐ, ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ๋์ด ์์ต๋๋ค.
versionAttribute๊ฐ ์ ํํ ๋ฌด์์ธ์ง๋ ๋ชจ๋ฅด๊ฒ ์ง๋ง(์ฃ์กํฉ๋๋ค..ใ ใ ์์๋ ๋ถ ๊ณ์๋ฉด ์๋ ค์ฃผ์ธ์..ใ ใ ),
๋๋ฒ๊น ํ ๊ฒฐ๊ณผ Optional.empty๊ฐ ๋์ ํญ์ if๋ฌธ์ ์กฐ๊ฑด์ ๊ฑธ๋ฆฌ๊ฒ ๋ฉ๋๋ค.
super.isNew()๋ AbstractEntityInformation์ ์ ์๋์ด ์์ผ๋ฉฐ, ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
id์ ํ์ ์ด primitive ํ์ ์ด ์๋ ๊ฒฝ์ฐ์๋ null์ธ ๊ฒฝ์ฐ๋ง ์๋ก์ด ์ํฐํฐ๋ก ํ๋จํฉ๋๋ค.
๊ทธ๋ฌ๋ primitive ํ์ ์ค ํด๋น ํ์ ์ Wrapper ํด๋์ค๊ฐ Number์ ํ์ ํ์ ์ธ ๊ฒฝ์ฐ(int, long ๋ฑ)์๋ ํด๋น ๊ฐ์ด 0์ธ ๊ฒฝ์ฐ์๋ง ์ด๋ฅผ ์๋ก์ด ์ํฐํฐ๋ผ ํ๋จํฉ๋๋ค.
๋ค์์ผ๋ก ๋์ด๊ฐ๊ธฐ ์ ์ ์ง๊ธ๊น์ง ํ์ธํ ๋ด์ฉ๋ค์ด ์ฌ๋ฐ๋ฅธ์ง ํ ์คํธ๋ฅผ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
Test Case 1 - Id ํ๋๊ฐ Primitive ํ์ ์ด ์๋ ๊ฒฝ์ฐ
์์๋ก ์ฌ์ฉํ Member ํด๋์ค์ ๋๋ค.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "member")
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
private String username;
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
}
@SpringBootTest
@Transactional
public class MemberRepositoryTest {
@Autowired private MemberRepository memberRepository;
@Test
public void saveTest() throws Exception {
Member member = new Member();
member.setUsername("username");
member.setId(0L);
memberRepository.save(member);
}
}
์ด ๊ฒฝ์ฐ์๋ Member์ Id ํ๋์ ๊ฐ์ด 0์ด์ง๋ง, Long ํ์ (primitive ํ์ ์ด ์๋)์ด๋ฏ๋ก merge๊ฐ ๋์ํฉ๋๋ค.
์คํ๋๋ ์ฟผ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Test Case 2 - Id ํ๋๊ฐ Primitive ํ์ ์ธ ๊ฒฝ์ฐ
์ด ๊ฒฝ์ฐ Member ํด๋์ค๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "member")
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private long id;
private String username;
public void setId(long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
}
ํ ์คํธ์ฝ๋๋ ๋ฌ๋ผ์ง ๋ถ๋ถ์ด ์์ต๋๋ค.
@SpringBootTest
@Transactional
public class MemberRepositoryTest {
@Autowired private MemberRepository memberRepository;
@Test
public void saveTest() throws Exception {
Member member = new Member();
member.setUsername("username");
member.setId(0L);
memberRepository.save(member);
}
}
์ด ๊ฒฝ์ฐ์๋ Member์ Id ํ๋์ ๊ฐ์ด 0์ด๋ฉฐ, primitive ํ์ ์ด๋ฉด์ long ํ์ ์ด๋ฏ๋ก persist๊ฐ ๋์ํฉ๋๋ค.
์คํ๋๋ ์ฟผ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ฝํ๋ฆฐ์์์ ๊ฒฝ์ฐ
์ฝํ๋ฆฐ์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ์ฌ์ผ ํฉ๋๋ค.
(์์ง ์ฝํ๋ฆฐ์ด ์ต์์น ์์ ๊ฐ๋จํ๊ฒ๋ง ๊ตฌํํ์์ต๋๋ค. ๋์ค์ ์ด์ํ ๋ถ๋ถ์ด ์๋ ๊ฒฝ์ฐ ์์ ํ๊ฒ ์ต๋๋ค.)
@Entity
class Member(
val username: String
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L
}
์์ ๊ฐ์ด ๊ตฌํํ ๋ค ์๋ ํ ์คํธ์ฝ๋๋ฅผ ์คํํด ๋ณด๊ฒ ์ต๋๋ค.
@SpringBootTest
@Transactional
class Tdd (){
@Autowired private lateinit var memberRepository: MemberRepository
@Test
fun tdd() {
val member = Member("username")
memberRepository.save(member)
}
}
id๋ ๋ค์๊ณผ ๊ฐ์ด(var id: Long) ์ ์ธ๋์ด ์์ผ๋ฏ๋ก merge๊ฐ ์๋ํด์ผ ํ ๊ฒ ๊ฐ์ง๋ง, ์ค์ ๋ก๋ persist๊ฐ ์คํ๋ฉ๋๋ค.
ํด๋น ์ด์ ๋ฅผ ์์๋ณด๊ธฐ ์ ์, id๋ฅผ null ๊ฐ๋ฅ์ผ๋ก ๋ง๋ค์ด ์ฃผ์ด ์คํํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ฆ ๋ค์๊ณผ ๊ฐ์ต๋๋ค
@Entity
class Member(
val username: String
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = 0L
}
merge๊ฐ ์๋ํ๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
์ด์ ๋ ์ฝํ๋ฆฐ์ ์กฐ๊ธ๋ง ๊ณต๋ถํ์ จ๋๋ผ๋ ์ฝ๊ฒ ์ถ์ธกํ ์ ์๊ฒ ์ง๋ง ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
null ๊ฐ๋ฅ ํ๋๋ฅผ ์ฌ์ฉํ๋ฉด Wrapper ํ์ ์ด ์ฌ์ฉ๋๊ณ , null ๋ถ๊ฐ๋ฅ ํ๋๋ฅผ ์ฌ์ฉํ๋ฉด primitive ํ์ ์ด ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ ๋๋ค.
nullable ๊ฒฝ์ฐ ( var id: Long? )
์ฝํ๋ฆฐ ํ์ผ -> ๋ฐ์ดํธ์ฝ๋ ๋์ปดํ์ผ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
null ๋ถ๊ฐ๋ฅ์ ๊ฒฝ์ฐ ( var id: Long )
์ฝํ๋ฆฐ ํ์ผ -> ๋ฐ์ดํธ์ฝ๋ ๋์ปดํ์ผ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ฌ์ค save์ ๋์ ๊ด๋ จํด์๋ ๊น์ํ ์ ์๋์ ์ฑ (์๋ฐ ORM ํ์ค JPA ํ๋ก๊ทธ๋๋ฐ)๊ณผ Spring Data JPA ๊ฐ์์์๋ ์ค๋ช ํด์ฃผ์ ๋ถ๋ถ์ ๋๋ค.
์ ๋ ๋ค ๋ฃ๊ณ ์ ๋ฆฌ๊น์ง ํ๋๋ฐ๋ ๊ธฐ์ต์ ๋ชปํด์...ใ ใ
์๋ฌดํผ ๊ฐ์ฌํฉ๋๋น!!!
๋์์ด ๋์ จ๋ค๋ฉด ๊ด๊ณ ํ๋ฒ์ฉ ํด๋ฆญํด์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค(์ฌ๋ฌ๋ฒ์ด๋ฉด ๋ ์ข์์ฉ) :)
Id๋ก Nullable ํ์ ์ฌ์ฉํ๊ธฐ
์ ๋ nullalble์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ฌด์กฐ๊ฑด ์ข์ง ์๋ค๊ณ ์๊ฐํ์๋๋ฐ, ๊ณต์ ๊ฐ์ด๋(https://spring.io/guides/tutorials/spring-boot-kotlin/)์ ์์์์๋ Nullalble ํ์ ์ ์ฌ์ฉํ์ต๋๋ค.
์ ๋ฆฌ
save() ๋ฉ์๋
- ์๋ก์ด ์ํฐํฐ์ธ ๊ฒฝ์ฐ - persist
- ์๋ก์ด ์ํฐํฐ๊ฐ ์๋ ๊ฒฝ์ฐ - merge
์๋ก์ด ์ํฐํฐ๋ฅผ ํ๋จํ๋ ๋ฐฉ๋ฒ
- ์๋ณ์๊ฐ ๊ฐ์ฒด์ธ ๊ฒฝ์ฐ - null์ธ์ง์ ์ฌ๋ถ
- ์๋ณ์๊ฐ ๊ธฐ๋ณธ ํ์ ์ธ ๊ฒฝ์ฐ - 0์ผ๋ก ํ๋จ
- Persistable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ฌ ํ๋จํ๋ ๋ฐฉ๋ฒ์ ์ง์ ์ ํ ์ ์์ต๋๋ค.
- ๋ค์ ๊ธ ์ฐธ๊ณ (https://ttl-blog.tistory.com/852)
Reference
'๐๏ธ Spring > JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring Data JPA] - ์ค์ ๊ฐ๋ ์๊ฐ (0) | 2022.07.28 |
---|---|
[JPA] ๋ณตํฉ ํค์ ์๋ณ ๊ด๊ณ ๋งคํ (0) | 2022.07.24 |
[JPA] SpringBoot Test์ ๋กค๋ฐฑ์ด ์๋ํ์ง ์๋ ๊ฒฝ์ฐ (MySQL ์ฌ์ฉ ์) (0) | 2022.05.15 |
[JPA] ์ด๋ ค์ฃผ์ธ์ (2) | 2022.03.14 |
[JPA] ์์ธ ์ข ๋ฅ์ ๋ฐ๋ฅธ ํธ๋์ญ์ ๋กค๋ฐฑ์ฒ๋ฆฌ (Checked Exception๊ณผ Unchecked Exception) (0) | 2022.01.22 |