0124 - 0206
# 0124 - 0206
# 0125 - JPA ์ฌ์ฉ์ ์ํฐํฐ ์ํ ํ์ธํ๊ธฐ
JPA๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ๋์ ์์์ผ ํ๋ ๊ฒ์ค ํ๋๊ฐ ์ํฐํฐ ๊ฐ์ฒด์ ์ํ ๋ณํ์ธ๋ฐ JPA๋ฅผ ์ฐ๋ฉด์ Transient(New), Persistent, Detached, Deleted ์ด๋ฐ ์ํ ๋ณํ์ ๊ทธ ํน์ง์ ๋ํ ๊ณต๋ถ๋ฅผ ํ์ ์ด ์๋ค๋ฉด JPA๋ฅผ ์๋ชป์ฐ๊ณ ์์ ๊ฐ๋ฅ์ฑ์ด ํฌ๋ค.
์ด ์ค์์๋ Persistent ์ํ๊ฐ ๊ฐ์ฅ ์ค์ํ๋ฐ, ํ์ด๋ฒ๋ค์ดํธ๊ฐ ํ ํธ๋์ญ์ ๋ด์์ ๋ถํ์ํ ์ฟผ๋ฆฌ๋ฅผ ์ค์์ฃผ๋ ์ญํ ์ ํ๊ธฐ ๋๋ฌธ์ด๋ค. ํํ 1์ฐจ ์บ์๋ผ๊ณ ๋ถ๋ฅด๋ Persistent Context๊ฐ ํด๋น ์ธ์คํด์ค๋ฅผ ์ด๋ฏธ ๋ด๊ณ ์๊ธฐ ๋๋ฌธ์ DB์์ ์ฟผ๋ฆฌ๋ฅผ ํ๋๋ผ๋ ๋ถํ์ํ select ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ค.
private void savePost() {
Post post = new Post();
post.setTitle("keesun");
Post newPost = postRepository.save(post);
System.out.println(postRepository.findById(newPost.getId()));
}
์ ๊ธ์ ์ ์ฅํ๊ณ findById๋ฅผ ํตํด id๋ก๋ก ์ ์ฅํ๋ Post๋ฅผ ๋ค์ ๊ฐ์ ธ์ค๋ ์ฝ๋์ด๋ค. ์ด ๋ postRepository๊ฐ ์คํ๋ง ๋ฐ์ดํฐ JPA๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์คํ๋ง JDBC๋ฅผ ์ฌ์ฉํด ๊ตฌํํ๊ฑฐ๋ ์คํ๋ง ๋ฐ์ดํฐ JDBC๋ฅผ ์ฌ์ฉํด ๊ตฌํํ๋ค๋ฉด ์ด ์ฝ๋๋ ๋ถ๋ช ํ insert ์ฟผ๋ฆฌ ์ดํ์ select ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๋ค.
ํ์ง๋ง, postRepository๋ฅผ ์คํ๋ง ๋ฐ์ดํฐ JPA๊ฐ ์ ๊ณตํ๋ JpaRepository ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ ๊ตฌํํ๋ค๋ฉด, ์ด ์ฝ๋๋ insert ์ฟผ๋ฆฌ๋ง ๋ฐ์ํ๊ณ select ์ฟผ๋ฆฌ๋ ๋ฐ์ํ์ง ์๋๊ฒ ์ด๋ก ์ ์ผ๋ก ๋ง๋ค. save()๋ฅผ ํธ์ถ ํ์ ๋ newPost(post์ ๊ฐ์ ์ธ์คํด์ค)๊ฐ Persistent ์ํ๋ก ์์ํ ์ปจํ ์คํธ์ธ EntityManager(๋๋ ํ์ด๋ฒ๋ค์ดํธ์ Session)์ ์บ์์ ์บ์๊ฐ ๋๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ํด๋น id๋ฅผ ๊ฐ์ง๊ณ ์๋ Post ์ธ์คํด์ค๋ฅผ DB์์ ๊บผ๋ด์ค๋๊ฒ ์๋๋ผ ์บ์ํ๊ณ ์๋ ๋ฉ๋ชจ๋ฆฌ์์ ๊บผ๋ด์ฃผ๊ณ , ๋ฐ๋ผ์ select ์ฟผ๋ฆฌ๋ ๋ฐ์ํ์ง ์๋๊ฒ ๋ง๋ค.
๊ทธ๋ฆฌ๊ณ ์ค์ ํด๋น ๊ฐ์ฒด๊ฐ Persistent ์ํ์ธ์ง ํ์ธํ๋ ค๋ฉด EntityManager๋ฅผ ์ฃผ์ ๋ฐ์์ contains ๋ฉ์๋๋ฅผ ํตํด ํ์ธํ ์ ์๋ค.
entityManager.contains(post);
- ํ์ธ
@Component
public class JpaRunner implements ApplicationRunner {
@PersistenceContext
EntityManager entityManager;
@Autowired
PostRepository postRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
savePost();
}
@Transactional
private void savePost() {
Post post = new Post();
post.setTitle("keesun");
Post newPost = postRepository.save(post);
System.out.println(postRepository.findById(newPost.getId()));
System.out.println(entityManager.contains(newPost));
}
}
- ๊ฒฐ๊ณผ
2020-08-26 22:04:22.546 INFO 54359 --- [ main] m.w.demodomainclassconverter.App : Started App in 2.062 seconds (JVM running for 2.458)
Hibernate: call next value for hibernate_sequence
Hibernate: insert into post (title, id) values (?, ?)
Hibernate: select post0_.id as id1_0_0_, post0_.title as title2_0_0_ from post post0_ where post0_.id=?
Optional[me.whiteship.demodomainclassconverter.Post@7ac48e10]
false
insert ์ฟผ๋ฆฌ ์ดํ์ select ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๊ณ , EntityManager์ contains๋ฅผ ํตํด Persistent ์ํ๊ฐ ๋ง๋์ง(EntityManager) ํ์ธ์ false๊ฐ ์ถ๋ ฅ๋๋ค. ๋ฌธ์ ๋ savePost ๋ฉ์๋์ ํธ๋์ญ์ ์ด ์ ์ฉ๋์ง ์์์ ์ด๋ค.
๊ฒฐ๋ก ์ ๊ฐ๋จํ์ง๋ง ๊ตฌ์ฒด์ ์ผ๋ก '์ ํธ๋์ญ์ ์ด ์ ์ฉ๋์ง ์์์ง?'์ ๋ตํ๋ ค๋ฉด ์คํ๋ง์ด ์ ๋ ธํ ์ด์ ๊ธฐ๋ฐ์ ํธ๋์ญ์ ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์ง ์ดํดํด์ผ ํ๋๋ฐ @Transaction์ด๋ผ๋ ์ ๋ ธํ ์ด์ ์ ์ฐ๋๊ฑด ๋ง์ง๋ง private ๋ฉ์๋์ ์ฐ๋๊ฑด ๋ฌด์๋ฏธํ๋ค. ์ค๋ฒ๋ผ์ด๋ฉ์ด ๊ฐ๋ฅํ ๋ฉ์๋์ ์จ์ผํ๋ค. ๊ทธ๋ ๋ค๋ฉด savePost() ๋ฉ์๋๋ฅผ public์ผ๋ก ๋ณ๊ฒฝํด๋ณด์.
@Transactional
public void savePost() {
Post post = new Post();
post.setTitle("keesun");
Post newPost = postRepository.save(post);
System.out.println(postRepository.findById(newPost.getId()));
System.out.println(entityManager.contains(newPost));
}
public ๋ฉ์๋๋ก ๋ฐ๊ฟจ์ง๋ง ์ด๋ ๊ฒ ํด๋ savePost()์๋ ํธ๋์ญ์ ์ด ์ ์ฉ๋์ง ์๊ณ ์์ํ ์ปจํ ์คํธ๋ newPost๋ฅผ ์บ์ฑํ์ง ์๋๋ค. ๊ทธ๋์ ๊ฒฐ๊ณผ๋ private ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ ๋์ ๋์ผํ๋ค. ๊ทธ ์ด์ ๋ ์คํ๋ง์ ํธ๋์ญ์ ์ฒ๋ฆฌ๊ฐ ์คํ๋ง AOP๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ณ ์์ผ๋ฉฐ ์คํ๋ง AOP๊ฐ ๋ค์ด๋๋ฏน ํ๋ก์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ค๋ ๊ฒ์ ์๊ณ ์๋ค๋ฉด ์ด๋ ๊ฒ ํด๋ ํธ๋์ญ์ ์ด ์ ์ฉ๋์ง ์๋ ์ด์ ๋ฅผ ์ดํดํ ์ ์์ ๊ฒ์ด๋ค.
ํ๋ก์ ๊ธฐ๋ฐ AOP์ ๋จ์ ์ค์ ํ๋์ธ ํ๋ก์ ๋ด๋ถ์์ ๋ด๋ถ๋ฅผ ํธ์ถํ ๋๋ ๋ถ๊ฐ์ ์ธ ์๋น์ค(์ฌ๊ธฐ์๋ ๊ทธ๊ฒ ๋ฐ๋ก ํธ๋์ญ์ )๊ฐ ์ ์ฉ๋์ง ์๋๋ค. ํธ์ถํ๋ ค๋ ํ๊ฒ์ ๊ฐ์ธ๊ณ ์๋ ํ๋ก์๋ฅผ ํตํด์๋ง ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ด ์ ์ฉ๋๋๋ฐ ํ๋ก์ ๋ด๋ถ์์ ๋ด๋ถ๋ฅผ ํธ์ถ ํ ๋๋ ๊ฐ์ธ๊ณ ์๋ ์์ญ์ ๊ฑฐ์น์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
!(image)[https://www.whiteship.me/content/images/size/w1000/2020/08/Screen-Shot-2020-08-27-at-20.28.13.png]
ํ๋ก์๋ก ๊ฐ์ผ ํ๊ฒ(JpaRunner)๋ฅผ ์ธ๋ถ์์ ํธ์ถํ ๋ run()์ด๋ผ๋ public ๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ฐ (ApplicationRunner๋ฅผ ๊ตฌํํ๊ธฐ ๋๋ฌธ์) ์ด ๋ run() ๋ฉ์๋์๋ ํธ๋์ญ์ ์ด ์ ์ฉ๋์ง ์๋๋ค. ์๋๋ฉด @Transactional์ savePost()๋ง ๋ถ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ๋ ๊ฒ ํธ์ถํ run()์ด ๋ด๋ถ์์ @Transactional์ ์ฌ์ฉํ savePost()๋ฅผ ํธ์ถํ๋๋ผ๋, JpaRunner ๋ฐ์์ ํธ์ถ์ด ๋๋๊ฒ ์๋๋ผ ํ๋ก์ ๋ด๋ถ์์ savePost()๋ฅผ ๋ฐ๋ก ํธ์ถํ๊ธฐ ๋๋ฌธ์ ํ๊ฒ์ ๊ฐ์ผ ํธ๋์ญ์ ์ด ์ ์ฉ๋์ง ์๋ ๊ฒ์ด๋ค. ์ฐจ๋ผ๋ฆฌ JpaRunner๋ฐ์์ savePost() ๋ฉ์๋๋ฅผ ๋ฐ๋ก ํธ์ถํ๋ค๋ฉด ํธ๋์ญ์ ์ด ์ ์ฉ๋์ ๊ฒ์ด๋ค.
ํด๊ฒฐํ๋ ๋ฐฉ์์ ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ง๋ง ์ ์ผ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ @Transactional์ run() ๋ฉ์๋๋ก ์ฎ๊ธฐ๋ฉด ๋๋ค. ๊ทธ๋ผ run()์ ํธ์ถ ํ ๋๋ถํฐ ํธ๋์ญ์ ์ด ์ ์ฉ๋๋ฉด์ ๊ทธ ๋ฉ์๋์์ ํธ์ถํ๋ ๋ค๋ฅธ ๋ฉ์๋๋ ์ ๋ถ ํด๋น ํธ๋์ญ์ ์์์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ insert ์ฟผ๋ฆฌ ์ดํ์ select๋ ๋ฐ์ํ์ง ์์ผ๋ฉฐ EntityManager์ contains ๋ฉ์๋๊ฐ true๋ฅผ ๋ฆดํดํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
@Transactional
@Override
public void run(ApplicationArguments args) throws Exception {
savePost();
}
public void savePost() {
Post post = new Post();
post.setTitle("keesun");
Post newPost = postRepository.save(post);
System.out.println(postRepository.findById(newPost.getId()));
System.out.println(entityManager.contains(newPost));
}
# 0202 - AllArgsConstructor, RequiredArgsConstructor์ staticName ์ต์
# staticName ์ต์
staticName ์ต์ ์ ์ฌ์ฉํ์ฌ ์์ฑ์๋ฅผ private๋ก ์์ฑํ๊ณ , private ์์ฑ์๋ฅผ ๊ฐ์ธ๊ณ ์๋ static factory ๋ฉ์๋๋ฅผ ์ถ๊ฐ ํ ์ ์๋ค.
@AllArgsConstructor(staticName = "of")
public class TestDTO {
private int price;
private int discount;
private final int amount;
}
-----------------------------------------
@Getter
public class TestDTO {
private int price;
private int discount;
private final int amount;
private TestDTO(int amount){
this.amount = amount;
}
public static TestDTO of(int i) {
return new TestDTO(i);
}
}
private ์์ฑ์๊ฐ ์์ฑ๋๊ณ , private ์์ฑ์๋ฅผ ๊ฐ์ธ๋ static factory ๋ฉ์๋ ์์ฑ
class TestDTOTest {
@Test
public void test1(){
TestDTO testDTO = TestDTO.of(4);
assertNotNull(testDTO);
assertEquals(0, testDTO.getPrice());
assertEquals(0, testDTO.getDiscount());
assertEquals(4, testDTO.getAmount());
System.out.println("testDTO.getPrice() = " + testDTO.getPrice());
System.out.println("testDTO.getDiscount() = " + testDTO.getDiscount());
System.out.println("testDTO.getAmount() = " + testDTO.getAmount());
}
}
# ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋์ ์ฅ๋จ์
์ฅ์
- ์ด๋ฆ์ด ์์ผ๋ฏ๋ก ์์ฑ์์ ๋นํด ๊ฐ๋ ์ฑ์ด ์ข๋ค
- ํธ์ถํ ๋๋ง๋ค ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํ ํ์๊ฐ ์๋ค
- ํ์ ์๋ฃํ ๊ฐ์ฒด๋ฅผ ๋ฐํํ ์ ์๋ค
- ํ์ธ์ ์๋ฃํ(parameterized type) ๊ฐ์ฒด๋ฅผ ๋ง๋ค ๋ ํธํ๋ค
๋จ์
- ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ง ์๋ ํด๋์ค๋ผ๋ฉด, ์์ฑ์๊ฐ ์์ผ๋ฏ๋ก ํ์ ํด๋์ค๋ฅผ ๋ชป ๋ง๋ ๋ค
- ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ ๋ค๋ฅธ ์ ์ ๋ฉ์๋์ ์ ๊ตฌ๋ถ๋์ง ์๋๋ค
// ํ์ ์๋ฃํ ๊ฐ์ฒด๋ฅผ ๋ฐํ
class OrderUtil {
public static Discount createDiscountItem(String discountCode) throws Exception {
if(!isValidCode(discountCode)) {
throw new Exception("์๋ชป๋ ํ ์ธ ์ฝ๋");
}
// ์ฟ ํฐ ์ฝ๋์ธ๊ฐ? ํฌ์ธํธ ์ฝ๋์ธ๊ฐ?
if(isUsableCoupon(discountCode)) {
return new Coupon(1000);
} else if(isUsablePoint(discountCode)) {
return new Point(500);
}
throw new Exception("์ด๋ฏธ ์ฌ์ฉํ ์ฝ๋");
}
}
class Coupon extends Discount { }
class Point extends Discount { }
ํ ์ธ ์ฝ๋์ ๊ท์น์ ๋ฐ๋ผ Coupon๊ณผ Point ๊ฐ์ฒด๋ฅผ ์ ํ์ ์ผ๋ก ๋ฆฌํดํ๊ณ ์๋ค.
์ด๋ฅผ ์ํด์๋ ๋ ํ์ ํด๋์ค๊ฐ ๊ฐ์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ฑฐ๋, ๊ฐ์ ๋ถ๋ชจ ํด๋์ค๋ฅผ ๊ฐ๋๋ก ํ๋ฉด ๋๋ค.
๋ง์ฝ ํ์ผ์ด ๋ถ๋ฆฌํ๊ธฐ ์ ๋งคํ ์์ ํด๋์ค๊ฐ ์๋ค๋ฉด private class๋ฅผ ํ์ฉํ ์๋ ์๋ค.
// java.util.Collections์์ EMPTY_MAP ๋ถ๋ถ
@SuppressWarnings("rawtypes")
public static final Map EMPTY_MAP = new EmptyMap<>();
/**
* Returns an empty map (immutable). This map is serializable.
*/
@SuppressWarnings("unchecked")
public static final <K,V> Map<K,V> emptyMap() {
return (Map<K,V>) EMPTY_MAP;
}
private static class EmptyMap<K,V> extends AbstractMap<K,V> implements Serializable {
/* ์๋ต */
}
EmptyMap ํด๋์ค๋ java.util.Collections ๋ด์ private static์ผ๋ก ์ ์ธ๋์ด ์์ผ๋ฉฐ, emptyMap์ด๋ผ๋ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ํตํด ์บ์คํ ๋ ์ธ์คํด์ค๋ฅผ ์ป์ ์ ์๋ค.