JPA Web Application
์ํ๋์ ์ค์ ! ์คํ๋ง ๋ถํธ์ JPA ํ์ฉ1 - ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ๊ฐ์๋ฅผ ์์ฝํ ๋ด์ฉ์ ๋๋ค.
Spring Boot Project
web, thymeleaf, jpa, h2, lombok, validation..
set Lombok
Prefrences - plugin - lombok
Prefrences - Annotation Processors - Enable annotation processing
set Build Tools Gradle
Preferences - Build, Execution, Deployment - Build Tools - Gradle
Build and run using: Gradle IntelliJ IDEA
Run tests using: Gradle IntelliJ IDEA
Thymeleaf
H2 Database
๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์ผ ์์ฑ
jdbc:h2:~/databaseName (jsessionid ํฌํจ - ํ์ผ ๋ชจ๋)
~/databaseName.mv.db ํ์ผ ์์ฑ ํ์ธ
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์
jdbc:h2:tcp://localhost/~/databaseName (๋คํธ์ํฌ ๋ชจ๋)
JPA & DB ์ค์
spring:
datasource:
url: jdbc:h2:tcp://localhost/~/jpashop
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
# ์ ํ๋ฆฌ์ผ์ด์
์คํ ์์ ์ ํ
์ด๋ธ์ drop ํ๊ณ , ๋ค์ ์์ฑ
ddl-auto: create
properties:
hibernate:
# System.out ์ ํ์ด๋ฒ๋ค์ดํธ ์คํ SQL์ ๋จ๊ธด๋ค.
show_sql: true
format_sql: true
logging:
level:
# Logger๋ฅผ ํตํด ํ์ด๋ฒ๋ค์ดํธ ์คํ SQL์ ๋จ๊ธด๋ค.
org.hibernate.SQL: debug
# ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ๋ก๊ทธ
org.hibernate.type: trace@Transactional
@Transactional์ด ํ ์คํธ ์ผ์ด์ค์ ์ ์ฉ๋ ๊ฒฝ์ฐ, ํ ์คํธ ์ข ๋ฃ ํ ๋ฐ๋ก ๋กค๋ฐฑ ์คํ
๋กค๋ฐฑ์ ์ํ์ง ์์ ๊ฒฝ์ฐ @Rollback(false) ์ฌ์ฉ
@Test @Transactional @Rollback(false) public void testMember() { // given Member member = new Member(); member.setUsername("memberA"); // when Long savedId = memberRepository.save(member); Member findMember = memberRepository.find(savedId); // then Assertions.assertThat(findMember.getId()).isEqualTo(member.getId()); Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername()); Assertions.assertThat(findMember).isEqualTo(member); // JPA ์ํฐํฐ ๋์ผ์ฑ ๋ณด์ฅ }
Build
./gradlew clean build
cd build/libs/
java -jar XXX.jarQuery Parameter Log
spring-boot-data-source-decorator Public
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์์คํ ์์์ ์ฌ์ฉํ๋ฏ๋ก ์ด์ ์ ์ฉ ์ ์ฑ๋ฅํ ์คํธ ํ์
๋๋ฉ์ธ ๋ถ์ ์ค๊ณ
๋๋ฉ์ธ ๋ชจ๋ธ๊ณผ ํ
์ด๋ธ ์ค๊ณ
ํ์์ด ์ฃผ๋ฌธ์ ํ๊ธฐ ๋๋ฌธ์ ํ์์ด ์ฃผ๋ฌธ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ง๋ ๊ฒ์ด ์ ์ค๊ณํ ๊ฒ์ฒ๋ผ ๋ณด์ด์ง๋ง, ๊ฐ์ฒด ์ธ์์ ์ค์ ์ธ๊ณ์๋ ๋ค๋ฅด๋ค
ํ์์ด ์ฃผ๋ฌธ์ ์ฐธ์กฐํ์ง ์๊ณ , ์ฃผ๋ฌธ์ด ํ์์ ์ฐธ์กฐํ๋ ๊ฒ์ผ๋ก ์ถฉ๋ถํ๋ค.
์ธ๋ํค๊ฐ ์๋ ๊ณณ์ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ผ๋ก ์ ํ์.
์ํฐํฐ ํด๋์ค ๊ฐ๋ฐ
์ด๋ก ์ ์ผ๋ก ์ํฐํฐ ํด๋์ค ์ค๊ณ ์ Getter/Setter๋ฅผ ๋ชจ๋ ์ ๊ณตํ์ง ์๊ณ , ๊ผญ ํ์ํ ๊ฒฝ์ฐ ๋ณ๋์ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด ์ด์์ ์ด๋ค.
์ค๋ฌด์์๋ ์ํฐํฐ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ์ผ์ด ๋ง์ผ๋ฏ๋ก, Getter ์ ๋๋ ์ด์ด๋๋ ๊ฒ์ด ํธ๋ฆฌ
๋จ, ์ํฐํฐ๋ฅผ ๋ณ๊ฒฝํ ๋๋ Setter ๋์ ๋ณ๊ฒฝ ์ง์ ์ด ๋ช ํํ๋๋ก ๋ณ๋ ๋น์ฆ๋์ค ๋ฉ์๋๋ฅผ ์ ๊ณตํ์.
ํ ์ด๋ธ ID๋ ๊ด๋ก์ ํ ์ด๋ธ๋ช + id๋ฅผ ๋ง์ด ์ฌ์ฉ
์ค๋ฌด์์๋ @ManyToMany ๋ฅผ ์ฌ์ฉํ์ง ๋ง์
์ค๊ฐ ํ ์ด๋ธ์ ์ปฌ๋ผ ์ถ๊ฐ๊ฐ ๋ถ๊ฐ๋ฅํ๊ณ , ์ฟผ๋ฆฌ๋ฅผ ์ธ๋ฐํ๊ฒ ์คํํ๊ธฐ ์ด๋ ค์ฐ๋ฏ๋ก ์ค๋ฌด์์ ์ฌ์ฉํ๊ธฐ์๋ ํ๊ณ ์กด์ฌ
๋์ , ์ค๊ฐ ์ํฐํฐ๋ฅผ ๋ง๋ค๊ณ ๋๋ค๋ ๋งคํ์ ์ผ๋๋ค, ๋ค๋์ผ ๋งคํ์ผ๋ก ํ์ด๋ด์ ์ฌ์ฉํ์
๊ฐ ํ์ ์ ์์ฑ์์์ ๊ฐ์ ๋ชจ๋ ์ด๊ธฐํํด์ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ํด๋์ค๋ก ์ค๊ณํ์.
JPA ์คํ์ ์ํฐํฐ๋ ์๋ฒ ๋๋ ํ์ ์ ์๋ฐ ๊ธฐ๋ณธ ์์ฑ์๋ฅผ public ๋๋ (๊ฐ๊ธ์ ) protected ๋ก ์ค์ ํด์ฃผ์.
@NoArgsConstructor(access = AccessLevel.PROTECTED)JPA ๊ตฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ๋ฆฌํ๋์ , ํ๋ก์ ๊ฐ์ ๊ธฐ์ ์ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํด์ผ ํ๊ธฐ ๋๋ฌธ
์ํฐํฐ ์ค๊ณ ์ฃผ์์ฌํญ
์ํฐํฐ์๋ ๊ฐ๊ธ์ Setter๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ
Setter๊ฐ ๋ชจ๋ ์ด๋ ค์๋ค๋ฉด, ๋ณ๊ฒฝ ํฌ์ธํธ๊ฐ ๋๋ฌด ๋ง์์ ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ง๋ค.
๋ชจ๋ ์ฐ๊ด๊ด๊ณ๋ ์ง์ฐ๋ก๋ฉ(LAZY)์ผ๋ก ์ค์ ํ๊ธฐ
์ฆ์๋ก๋ฉ(EAGER)์ ์์ธก์ด ์ด๋ ต๊ณ , ์ด๋ค SQL์ด ์คํ๋ ์ง ์ถ์ ์ด ์ด๋ ค์
ํนํ๋ JPQL์ ์คํํ ๋ N+1 ๋ฌธ์ ๊ฐ ์์ฃผ ๋ฐ์
์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ํจ๊ป DB์์ ์กฐํํด์ผ ํ๋ค๋ฉด, fetch join ๋๋ ์ํฐํฐ ๊ทธ๋ํ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์.
@XToOne(OneToOne, ManyToOne) ๊ด๊ณ๋ ๊ธฐ๋ณธ์ด ์ฆ์๋ก๋ฉ(EAGER)์ด๋ฏ๋ก ์ง์ ์ง์ฐ๋ก๋ฉ(LAZY)์ผ๋ก ์ค์ ํด์ผ ํ๋ค.
์ปฌ๋ ์ ์ ํ๋์์ ์ด๊ธฐํ ํ๊ธฐ
์ปฌ๋ ์ ์ ํ๋์์ ๋ฐ๋ก ์ด๊ธฐํ ํ๋ ๊ฒ์ด null ๋ฌธ์ ์์ ์์
ํ์ด๋ฒ๋ค์ดํธ๋ ์ํฐํฐ๋ฅผ ์์ํ ํ ๋, ์ปฌ๋์ ์ ๊ฐ์ธ์ ํ์ด๋ฒ๋ค์ดํธ๊ฐ ์ ๊ณตํ๋ ๋ด์ฅ ์ปฌ๋ ์ ์ผ๋ก ๋ณ๊ฒฝ
org.hibernate.collection.internal.PersistentBag๋ง์ฝ getOrders() ์ฒ๋ผ ์์์ ๋ฉ์๋์์ ์ปฌ๋ ฅ์ ์ ์๋ชป ์์ฑํ๋ฉด ํ์ด๋ฒ๋ค์ดํธ ๋ด๋ถ ๋ฉ์ปค๋์ฆ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. ๋ฐ๋ผ์ ํ๋๋ ๋ฒจ์์ ์์ฑํ๋ ๊ฒ์ด ๊ฐ์ฅ ์์ ํ๊ณ , ์ฝ๋๋ ๊ฐ๊ฒฐํ๋ค.
private List<OrderItem> orderItems = new ArrayList<>();์์์ฑ ์ ์ด
ํน์ ์ํฐํฐ๋ฅผ ์์ ์ํ๋ก ๋ง๋ค ๋, ์ฐ๊ด๋ ์ํฐํฐ๋ ํจ๊ป ์์ ์ํ๋ก ๋ง๋ค๊ณ ์ถ์ ๊ฒฝ์ฐ ์ฌ์ฉ
๋จ, ์ํฐํฐ์ ์์ ์๊ฐ ํ๋์ผ ๋๋ง ์ฌ์ฉํด์ผ ํ๋ค.
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋
์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ ์ ๊ฐ์ฒด๊ฐ ๊ฐ ์ธํ ์ ํ์
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public void setDelivery(Delivery delivery) {
this.delivery = delivery;
delivery.setOrder(this);
}ํ ์ด๋ธ, ์ปฌ๋ผ๋ช ์์ฑ ์ ๋ต
SpringPhysicalNamingStrategyํ์ด๋ฒ๋ค์ดํธ์ ๊ธฐ์กด ๊ตฌํ์ ์ํฐํฐ์ ํ๋๋ช ์ ๊ทธ๋๋ก ํ ์ด๋ธ์ ์ปฌ๋ผ๋ช ์ผ๋ก ์ฌ์ฉ
์คํ๋ง ๋ถํธ ๊ธฐ๋ณธ ์ค์ ์ (์ํฐํฐ/ํ๋ > ํ ์ด๋ธ/์ปฌ๋ผ)
CamelCase->_(underscore).(dot) ->_(underscore)๋๋ฌธ์->์๋ฌธ์
๋ ผ๋ฆฌ๋ช ์ ์ฉ
๋ช ์์ ์ผ๋ก ์ปฌ๋ผ/ํ ์ด๋ธ๋ช ์ ์ง์ ์ ์ง ์์ผ๋ฉด
ImplicitNamingStrategy์ฌ์ฉ
spring.jpa.hibernate.naming.implicit-strategy : org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy๋ฌผ๋ฆฌ๋ช ์ ์ฉ
๋ชจ๋ ๋ ผ๋ฆฌ๋ช , ์ค์ ํ ์ด๋ธ์ ์ ์ฉ
SpringPhysicalNamingStrategy๋ฅผ ์ฐธ๊ณ ํด์ ์ปค์คํฐ๋ง์ด์ง ๋ฃฐ๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅ(username -> usernm)
spring.jpa.hibernate.naming.physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
๋๋ฉ์ธ ๊ฐ๋ฐ
Repository
@Repository์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก, JPA ์์ธ๋ฅผ ์คํ๋ง ๊ธฐ๋ฐ ์์ธ๋ก ์์ธ ๋ณํ
@PersistenceContext์ํฐํฐ ๋ฉ๋์ ( EntityManager) ์ฃผ์
@PersistenceUnit์ํฐํฐ ๋ฉ๋ํฐ ํฉํ ๋ฆฌ( EntityManagerFactory) ์ฃผ์
@Repository
@RequiredArgsConstructor
public class MemberRepository {
/**
* SpringBoot(SpringDataJPA) ๊ฐ
* @PersistenceContext ๋์ final, RequiredArgsConstructor (@Autowired)๋ก ๋์ฒด ๊ฐ๋ฅํ๋๋ก ์ง์
*/
private final EntityManager em;
public void save(Member member) {
em.persist(member);
}
public Member findOne(Long id) {
return em.find(Member.class, id);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public List<Member> findByName(String name) {
return em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
}
}Service
@Service@Transactionalํธ๋์ญ์ , ์์์ฑ ์ปจํ ์คํธ
readOnly=true๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ด ์๋ ์ฝ๊ธฐ ์ ์ฉ ๋ฉ์๋์ ์ฌ์ฉ
์์์ฑ ์ปจํ ์คํธ๋ฅผ flush ํ์ง ์์ผ๋ฏ๋ก ์ฝ๊ฐ์ ์ฑ๋ฅ ํฅ์(์ฝ๊ธฐ ์ ์ฉ์๋ ๋ค ์ ์ฉ)
๋ฐ์ดํฐ๋ฒ ์ด์ค ๋๋ผ์ด๋ฒ๊ฐ ์ง์ํ๋ฉด DB์์ ์ฑ๋ฅ ํฅ์
@Autowired์์ฑ์ Injection์ผ๋ก ๋ง์ด ์ฌ์ฉ, ์์ฑ์๊ฐ ํ๋๋ฉด ์๋ต ๊ฐ๋ฅ
DI ์ฃผ์
๊ณตํต์ ์ผ๋ก๋ Spring ๊ฐ๋ ์ ์์กด์ฑ ์ฃผ์ ๋ฐ์
Field Injection
ํ ์คํธ ์ฝ๋ ์์ฑ ์ ์์กด์ฑ ํ๋๋ฅผ ๋ณ๊ฒฝํ ์ ์์ด mock ๊ฐ์ฒด ์ฃผ์ ์ด ์ด๋ ค์ด ๋จ์
@Service
public class MemberService {
@Autowired
private MemberRepository memberRepository;
}Setter injection
ํ ์คํธ ์ฝ๋ ์์ฑ ์ mock ๊ฐ์ฒด ์ฃผ์ ๊ฐ๋ฅ
ํ์ง๋ง, setter ๋ฉ์๋๊ฐ ๋ ธ์ถ๋์ด ์ค๊ฐ์ ์์ฑ์ ๋ณ๊ฒฝ์ ์๋ํ ์ ์๊ณ , ์์กด์ฑ ํ๋ ์ถ๊ฐ ์ ๋ฒ๊ฑฐ๋ก์ด ์ฝ๋ ์ถ๊ฐ๊ฐ ํ์
@Service
public class MemberService {
private MemberRepository memberRepository;
@Autowired
public voidMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}Construct injection
์์ฑ์ ์ฃผ์ ๋ฐฉ์์ ๊ถ์ฅ
๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ์์ ํ ๊ฐ์ฒด ์์ฑ
์์ฑ์์์ injection ๋๋ฏ๋ก ์ค๊ฐ์ ์์ฑ์ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅ
ํ ์คํธ ์ฝ๋ ์์ฑ ์ ์์ฑ์ ์ฃผ์ ๊ด๋ จํ์ฌ ์ปดํ์ผ ์ค๋ฅ๋ก ๋ช ํํ๊ฒ ์ธ์ง ๊ฐ๋ฅ
์์ฑ์๊ฐ ํ๋๋ฉด, @Autowired ์๋ต ๊ฐ๋ฅ
@Service
public class MemberService {
private MemberRepository memberRepository;
@Autowired
public MemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}Construct injection using lombok
final ํ๋๋ง ๋์์ผ๋ก ์์ฑ์ ์์ฑ
final ํค์๋๋ฅผ ์ถ๊ฐํ๋ฉด ์ปดํ์ผ ์์ ์ memberRepository๋ฅผ ์ค์ ํ์ง ์๋ ์ค๋ฅ ์ฒดํฌ ๊ฐ๋ฅ
๋ณดํต ๊ธฐ๋ณธ ์์ฑ์๋ฅผ ์ถ๊ฐํ ๋ ๋ฐ๊ฒฌ
injection์ ํ์ํ ํ๋๋ง ๊ตฌ๋ถ ๊ฐ๋ฅ
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
}Test
@RunWith(SpringRunner.class)์คํ๋ง๊ณผ ํ ์คํธ ํตํฉ
@SpringBootTest์คํ๋ง ๋ถํธ ๋์ฐ๊ณ ํ ์คํธ(์ด๊ฒ ์์ผ๋ฉด @Autowired ๋ค ์คํจ)
@Transactional๋ฐ๋ณต ๊ฐ๋ฅํ ํ ์คํธ ์ง์
๊ฐ๊ฐ์ ํ ์คํธ๋ฅผ ์คํํ ๋๋ง๋ค ํธ๋์ญ์ ์ ์์ํ๊ณ ํ ์คํธ๊ฐ ๋๋๋ฉด ํธ๋์ญ์ ์ ๊ฐ์ ๋ก ๋กค๋ฐฑ
์ด ์ด๋ ธํ ์ด์ ์ด ํ ์คํธ ์ผ์ด์ค์์ ์ฌ์ฉ๋ ๋๋ง ๋กค๋ฐฑ
In-Memory DB
ํ ์คํธ๋ ์ผ์ด์ค ๊ฒฉ๋ฆฌ๋ ํ๊ฒฝ์์ ์คํํ๊ณ , ํ ์คํธ ์ข ๋ฃ ์ ๋ฐ์ดํฐ๋ฅผ ์ด๊ธฐํํ ํ์.
In-Memory DB ์ฌ์ฉ์ด ๊ฐ์ฅ ์ด์์ !
ํ ์คํธ ์ผ์ด์ค๋ฅผ ์ํ ์คํ๋ง ํ๊ฒฝ(
src/test/resources/application.yml)๊ณผ ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํ๋ ํ๊ฒฝ(src/main/resources/application.yml) ์ค์ ํ์ผ์ ๋ถ๋ฆฌํด์ ์ฌ์ฉํ์.์คํ๋ง ๋ถํธ๋ datasource ์ค์ ์ด ์์ผ๋ฉด, ๊ธฐ๋ณธ์ ์ In-Memory DB ์ฌ์ฉ
driver-class : ํ์ฌ ๋ฑ๋ก๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณด๊ณ ๊ฒฐ์
ddl-auto : create-drop ๋ชจ๋๋ก ๋์
datasource, JPA ๊ด๋ จ๋ ๋ณ๋์ ์ถ๊ฐ ์ค์ ์ ํ์ง ์์๋ ๊ฐ๋ฅ (์๋์ผ๋ก ์ธ๋ฉ๋ชจ๋ฆฌ ๋ชจ๋ ์ ํ)
๋๋ฉ์ธ ๋ชจ๋ธ ํจํด
๋๋ฉ์ธ ๋ชจ๋ธ ํจํด
์ํฐํฐ๊ฐ ๋น์ฆ๋์ค ๋ก์ง์ ๊ฐ์ง๊ณ ๊ฐ์ฒด ์งํฅ์ ํน์ฑ์ ์ ๊ทน ํ์ฉํ๋ ํจํด
์๋น์ค ๊ณ์ธต์ ๋จ์ํ ์ํฐํฐ์ ํ์ํ ์์ฒญ์ ์์ํ๋ ์ญํ
JPA, ORM...
ํธ๋์ญ์ ์คํฌ๋ฆฝํธ ํจํด
์ํฐํฐ์ ๋น์ฆ๋์ค ๋ก์ง์ด ๊ฑฐ์ ์๊ณ ์๋น์ค ๊ณ์ธต์์ ๋๋ถ๋ถ์ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๋ ํจํด
Mybatis
๋ณ๊ฒฝ ๊ฐ์ง์ ๋ณํฉ
์ค์์ ์ํฐํฐ
์์์ฑ ์ปจํ ์คํธ๊ฐ ๋์ด์ ๊ด๋ฆฌํ์ง ์๋ ์ํฐํฐ
์๋ณ์๋ฅผ ๊ฐ์ง๊ณ ์๋ new Object ์คํฐํฐ๋ฅผ ์ค์์ ์ํฐํฐ๋ก ๋ณผ ์ ์์
Book book = new Book();
book.setId(form.getId());
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());์ค์์ ์ํฐํฐ๋ฅผ ์์ ํ๋ ๋ฐฉ๋ฒ
๋ณ๊ฒฝ ๊ฐ์ง ๊ธฐ๋ฅ ์ฌ์ฉ
์์์ฑ ์ปจํ ์คํธ์์ ์ํฐํฐ๋ฅผ ๋ค์ ์กฐํํ ํ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ๋ฐฉ๋ฒ
ํธ๋์ญ์ ์์์ ์ํฐํฐ๋ฅผ ๋ค์ ์กฐํ/๋ณ๊ฒฝํ ๊ฒฝ์ฐ ํธ๋์ญ์ ์ปค๋ฐ ์์ ์ ๋ณ๊ฒฝ ๊ฐ์ง(Dirty Checking)๊ฐ ๋์ํด์ UPDATE ์ฟผ๋ฆฌ ์คํ
๊ท์ฐฎ์ ์ ์์ง๋ง ๋ณํฉ์ ์ํ์ฑ์ด ์กด์ฌํ๋ฏ๋ก Dirty Checking ์ ์ ํ์ฉํ์.
/**
* @param itemId
* @param param : ํ๋ฆฌ๋ฏธํฐ๋ก ๋์ด์จ ์ค์์ ์ํ์ ์ํฐํฐ
*/
@Transactional
public void updateItem(Long itemId, Item param) {
Item findItem = itemRepository.findOne(itemId); //๊ฐ์ ์ํฐํฐ ์กฐํ(์์ ์ํ)
findItem.setPrice(param.getPrice()); //๋ฐ์ดํฐ ์์
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());
// Transactional commit -> flush
}๋ณํฉ(merge) ์ฌ์ฉ
์ค์์ ์ํ์ ์ํฐํฐ๋ฅผ ์์ ์ํ๋ก ๋ณ๊ฒฝํ ๋ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ
๋ณ๊ฒฝ ๊ฐ์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ์ํ๋ ์์ฑ๋ง ์ ํํด์ ๋ณ๊ฒฝํ ์ ์์ง๋ง,๋ณํฉ์ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ํ๋๊ฐ ๋ณ๊ฒฝ๋๋ฏ๋ก ๋ณํฉ ์ ๊ฐ์ด ์์ผ๋ฉด null ๋ก ์ ๋ฐ์ดํธ๋๋ ์ํ์ฑ ์กด์ฌ
\1. merge() ์คํ
\2. ํ๋ผ๋ฏธํฐ๋ก ๋์ด์จ ์ค์์ ์ํฐํฐ์ ์๋ณ์ ๊ฐ์ผ๋ก 1์ฐจ ์บ์์์ ์ํฐํฐ๋ฅผ ์กฐํ
\2-1. ๋ง์ฝ 1์ฐจ ์บ์์ ์ํฐํฐ๊ฐ ์์ผ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ํฐํฐ๋ฅผ ์กฐํํ๊ณ , 1์ฐจ ์บ์์ ์ ์ฅ
\3. ์กฐํํ ์์ ์ํฐํฐ์ ์ค์์ ์ํฐํฐ์ ๋ชจ๋ ๊ฐ์ ์ฑ์ ๋ฃ๋๋ค.
\4. ์์ ์ํ์ธ ์ํฐํฐ ๋ฐํ
\5. ํธ๋์ญ์ ์ปค๋ฐ ์์ ์ ๋ณ๊ฒฝ ๊ฐ์ง ๊ธฐ๋ฅ์ด ๋์ํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค UPDATE ์ฟผ๋ฆฌ ์คํ
@Transactional
void update(Item itemParam) { //itemParam: ํ๋ฆฌ๋ฏธํฐ๋ก ๋์ด์จ ์ค์์ ์ํ์ ์ํฐํฐ
Item mergeItem = em.merge(item);
}๊ฒฐ๋ก
์ํฐํฐ๋ฅผ ๋ณ๊ฒฝํ ๋ ํญ์
๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ์ฌ์ฉํ์.์ปจํธ๋กค๋ฌ์์ ์ด์คํ๊ฒ ์ํฐํฐ๋ฅผ ์์ฑํ์ง ๋ง์.
ํธ๋์ญ์ ์ด ์๋ ์๋น์ค ๊ณ์ธต์ ์๋ณ์์ ๋ณ๊ฒฝํ ๋ฐ์ดํฐ๋ฅผ ๋ช ํํ๊ฒ ์ ๋ฌํ์.parameter, dto ํ์ฉ
ํธ๋์ญ์ ์ด ์๋ ์๋น์ค ๊ณ์ธต์์ ์์ ์ํ์ ์ํฐํฐ๋ฅผ ์กฐํํ๊ณ , ์ํฐํฐ์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๋ณ๊ฒฝํํ์.ํธ๋์ญ์ ์ปค๋ฐ ์์ ์ ๋ณ๊ฒฝ ๊ฐ์ง ์คํ
Setter ์์ด ์ํฐํฐ์์ ๋ฐ๋ก ์ถ์ ๊ฐ๋ฅํ ๋ฉ์๋๋ฅผ ๋ง๋ค์.
/**
* Controller
*/
@PostMapping(value = "/items/{itemId}/edit")
public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());
return "redirect:/items";
}
/**
* Service
*/
@Transactional
public void updateItem(Long itemId, String name, int price, int stockQuantity) {
Item findItem = itemRepository.findOne(itemId); //๊ฐ์ ์ํฐํฐ ์กฐํ(์์ ์ํ)
findItem.change(name, price, stockQuantity);
}Last updated