작성
·
87
0
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.
1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)
질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)
[질문 내용]
여기에 질문 내용을 남겨주세요.
package jpabook.jpashop;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
import jpabook.jpashop.domain.*;
import org.hibernate.boot.model.source.spi.IdentifierSource;
import javax.swing.text.html.parser.Entity;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
Address address = new Address("city1", "street1", "10000");
Member member = new Member();
Order order = new Order();
Delivery delivery = new Delivery();
List<Order> orders = new ArrayList<>();
member.setName("raewoo");
member.setAddress(address);
order.setMember(member);
order.setDelivery(delivery);
orders.add(order);
member.setOrders(orders);
delivery.setOrder(order);
delivery.setAddress(address);
em.persist(member);
em.persist(order);
em.persist(delivery);
em.flush();
em.clear();
System.out.println("================================");
Address address2 = new Address("city2", "street2", "10000");
Member findMember = em.find(Member.class, member.getId());
Order findOrder = em.find(Order.class, order.getId());
findMember.setAddress(address2);
em.flush();
em.clear(); //여기!!
System.out.println(findMember.getAddress().getCity());
System.out.println("================================");
System.out.println(findMember.getOrders().getFirst().getDelivery().getAddress().getCity());
tx.commit();
}catch (Exception e){
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
이렇게 메인 메서드를 작성했습니다.
//여기!! 라고 주석처리 해놓은 em.clear()가 있으면 콘솔에 아래와 같이 결과가 나오고요,
Hibernate:
select
m1_0.MEMBER_ID,
m1_0.city,
m1_0.street,
m1_0.zipcode,
m1_0.CREATED_DATE_TIME,
m1_0.name,
m1_0.UPDATED_DATE_TIME
from
Member m1_0
where
m1_0.MEMBER_ID=?
Hibernate:
select
o1_0.ORDER_ID,
o1_0.CREATED_DATE_TIME,
o1_0.DELIVERY_ID,
o1_0.MEMBER_ID,
o1_0.orderDate,
o1_0.status,
o1_0.UPDATED_DATE_TIME
from
ORDERS o1_0
where
o1_0.ORDER_ID=?
Hibernate:
/* update
for jpabook.jpashop.domain.Member */update Member
set
city=?,
street=?,
zipcode=?,
CREATED_DATE_TIME=?,
name=?,
UPDATED_DATE_TIME=?
where
MEMBER_ID=?
city2
================================
보시는 바와 같이 마지막 =======을 기준으로 system.out.println() 문이 하나 더 출력되어야하는데, 출력되지 않습니다.
하지만 em.clear()를 없애면
================================
Hibernate:
select
m1_0.MEMBER_ID,
m1_0.city,
m1_0.street,
m1_0.zipcode,
m1_0.CREATED_DATE_TIME,
m1_0.name,
m1_0.UPDATED_DATE_TIME
from
Member m1_0
where
m1_0.MEMBER_ID=?
Hibernate:
select
o1_0.ORDER_ID,
o1_0.CREATED_DATE_TIME,
o1_0.DELIVERY_ID,
o1_0.MEMBER_ID,
o1_0.orderDate,
o1_0.status,
o1_0.UPDATED_DATE_TIME
from
ORDERS o1_0
where
o1_0.ORDER_ID=?
Hibernate:
/* update
for jpabook.jpashop.domain.Member */update Member
set
city=?,
street=?,
zipcode=?,
CREATED_DATE_TIME=?,
name=?,
UPDATED_DATE_TIME=?
where
MEMBER_ID=?
city2
================================
Hibernate:
select
o1_0.MEMBER_ID,
o1_0.ORDER_ID,
o1_0.CREATED_DATE_TIME,
o1_0.DELIVERY_ID,
o1_0.orderDate,
o1_0.status,
o1_0.UPDATED_DATE_TIME
from
ORDERS o1_0
where
o1_0.MEMBER_ID=?
Hibernate:
select
d1_0.DELIVERY_ID,
d1_0.city,
d1_0.street,
d1_0.zipcode,
d1_0.CREATED_DATE_TIME,
d1_0.status,
d1_0.UPDATED_DATE_TIME
from
Delivery d1_0
where
d1_0.DELIVERY_ID=?
Hibernate:
select
o1_0.ORDER_ID,
o1_0.CREATED_DATE_TIME,
o1_0.DELIVERY_ID,
o1_0.MEMBER_ID,
o1_0.orderDate,
o1_0.status,
o1_0.UPDATED_DATE_TIME
from
ORDERS o1_0
where
o1_0.DELIVERY_ID=?
city1
이렇게 ORDERS를 조회하는 쿼리문이 나오고, city1이 정상적으로 출력됩니다.
em.clear()가 있고 없고에 왜 이런 차이가 발생하는 건가요?
쿼리가 나가지 않는다던가, 어떤 null 값을 읽어온다던가 하면 예외가 나오거나, null로 나와야 할텐데, System.out.println()문 자체가 씹히는 건 어떤 경우인가요? 왜 이런건가요?
답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
raewoo0908
님의 질문과 관련하여, em.clear()
의 기능 및 특정 상황에서 쿼리가 출력되지 않는 현상에 대해 설명드립니다. em.clear()
메소드는 영속성 컨텍스트를 완전히 초기화하는 역할을 합니다. 이는 영속성 컨텍스트에 존재하는 모든 엔티티를 준영속 상태로 만들며, 이후의 작업에서 영속성 컨텍스트 내의 엔티티를 참조할 수 없게 됩니다. 이는 또한 1차 캐시 내용을 모두 제거한다는 것을 의미합니다. 따라서, em.clear()
호출 후에 데이터를 조회하려면 데이터베이스 쿼리를 다시 실행해야만 합니다.
질문하신 코드에서 em.clear()
가 호출된 후 System.out.println(findMember.getOrders().getFirst().getDelivery().getAddress().getCity());
이 부분이 “출력되지 않는다”고 하셨는데, 실제로는 이 부분에서 문제가 발생하는 것이 아니라 해당 코드가 예외를 발생시키거나, 예상치 못한 동작을 하고 있을 수 있습니다. em.clear()
에 의해 findMember
와 관련된 Order
및 Delivery
인스턴스도 모두 준영속 상태가 되어 관리되지 않게 됩니다. 따라서, 연관된 엔티티에 접근하려 할 때 문제가 발생할 수 있습니다.
이와 유사한 상황에 대해 설명한 내용이 다음 게시글에서 확인 가능합니다:
em.flush() & em.clear() 제가 이해하고 있는게 맞나요?에서는 em.flush()
와 em.clear()
전후의 동작 방식에 대해 설명하고 있습니다. 특히 em.clear()
호출 후 영속성 컨텍스트가 초기화되어 find()
메소드를 통해 데이터를 다시 조회할 때 데이터베이스에서 값을 가져오는 과정을 설명하고 있습니다.
em.close(); 게시글에서는 em.close()
와 비슷한 맥락의 질문에 대한 답변이 있지만, 정확히는 em.clear()
와 관련된 설명은 아닙니다. 하지만 영속성 컨텍스트 동작 원리에 대한 이해를 돕습니다.
이러한 동작 원리로 보아, System.out.println()
문 자체가 씹힌 것이 아니라, 아마도 관련 엔티티를 불러오는 과정에서 문제가 발생하였을 가능성이 큽니다. 이 때문에 해당 출력문에 도달하기 전에 예외가 발생하거나 다른 로직의 문제로 인해 출력이 이루어지지 않을 수 있습니다.
질문자님의 문제 해결을 위해 자세한 예외 메시지나, 특정 부분에서의 문제점 보다 명확한 진단이 필요할 경우, 이와 관련한 추가적인 정보를 제공해주시면 더 정확한 답변이 가능할 것입니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
이상한 점을 발견했습니다.
em.clear가 없는 상태에서 실행하면
다음과 같이 db에 값이 잘 있습니다.
하지만 em.clear()가 있는 상태에서 실행하면
다음과 같이 모든 정보가 다 사라져있습니다. em.clear()가 db에 저장된 값까지 없앨 수는 없지 않나요? 왜 이런건가요?
이거때문에 마지막 println문에서 출력되는 게 없는 것 같습니다..