학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 예 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예 [질문 내용] 안녕하세요 영한님 이번에 트랜잭션 적용하면서 @Transactional(readOnly = true) 가 붙은 조회 메서드 내부에 @Transactional(propagation = Propagation.REQUIRES_NEW) 가 붙은 외부 클래스의 메서드가 사용 될 때 물리 트랜잭션이 분리되었고 각각 다른 트랜잭션임에도 변경감지가 적용안되는 부분이 궁금했습니다. 아래는 궁금한 부분에 대한 테스트를 진행한 코드입니다. @Service @RequiredArgsConstructor public class TeamService { private final TeamRepository teamRepository; private final TeamExternalService externalService; @Transactional(readOnly = true) public Team findTeamWithNameChangeExternal(Team team, String name) { Team findTeam = teamRepository.findByName(team.getName()); externalService.changeName(findTeam, name); return findTeam; } } @Service @RequiredArgsConstructor public class TeamExternalService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void changeName(Team findTeam, String name) { findTeam.setName(name); } } @Test void findTeamWithNameChangeExternal() { Team team = new Team("team"); Team savedTeam = teamRepository.save(team); teamService.findTeamWithNameChangeExternal(savedTeam, "changeName"); Team findTeam = teamRepository.findByName("changeName"); assertThat(findTeam.getName()).isEqualTo("changeName"); } 같은 클래스에서 내부 호출 할 경우에는 REQUIRES_NEW 를 적용하더라도 같은 프록시 객체로 해당 메서드가 호출된 상위 트랜잭션의 옵션을 그대로 따라 가기 때문에 변경 감지가 적용안된다고 생각했어요 하지만 이와 다른 외부 클래스의 분리된 트랜잭션에서는 변경 감지가 일어나는 것이 정상이라고 생각했는데 왜 변경 감지가 일어나지 않는 것일 까요 ..? 아래는 궁금해서 디버그를 찍어봤어요 보니가 두 트랜잭션의 번호가 Transaction@13592로 같고 REQUIRES_NEW 가 적용된 트랜잭션의 경우에는 oldTrasactionInfo 이라는 정보를 갖고 해당 메서드를 실행하는 @Transactional(readOnly = true) 옵션이 적혀있더라구요 ?? 그렇다면 내부 호출이 아닌 외부 호출이라도 readOnly = true 로 조회 성능을 챙기면서 변경 감지를 사용할 순 없는 것인가요 ??
안녕하세요 영한님 강의 정말 잘 듣고 있습니다. ㅎㅎ @Transactional에 대해서 궁금한 점이 생겨서 질문드립니다. @Test @Transactional public void 상품주문() throws Exception { //given Member member = createMember(); Book book = createBook("시골 JPA", 10000, 10); int orderCount = 2; //when Long orderId = orderService.order(member.getId(), book.getId(), orderCount); //then Order getOrder = orderRepository.findOne(orderId); assertEquals("상품 주문시 상태는 ORDER", OrderStatus.ORDER, getOrder.getStatus()); assertEquals("주문한 상품 종류 수가 정확해야 한다.", 1, getOrder.getOrderItems().size()); assertEquals("주문 가격은 가격 * 수량이다.", 10000 * orderCount, getOrder.getTotalPrice()); assertEquals("주문 수량만큼 재고가 줄어야 한다.", 8, book.getStockQuantity()); } 해당 코드는 강의에서 작성한 테스트 코드 입니다. 지만 저는 지금까지 @Transactional을 테스트 코드에 작성하지 않는 것이 좋다고 알고 있었습니다. 그 이유는 @Transactional을 깜빡하고 작성하지 않는 문제를 테스트 코드에서 잡아 낼 수 있고, 테스트 트랜잭션의 롤백 정책으로 인해 실제 로직과 다르게 작동하기 때문입니다. 아래는 제가 @Transactional을 제거하고 코드를 작성해 보았습니다. @Test public void 상품주문() throws Exception { //given Member member = createMember(); Book book = createBook("시골 JPA", 10000, 10); int orderCount = 2; //when Long orderId = orderService.order(member.getId(), book.getId(), orderCount); //then Order getOrder = orderRepository.findOne(orderId); assertEquals("상품 주문시 상태는 ORDER", OrderStatus.ORDER, getOrder.getStatus()); assertEquals("주문한 상품 종류 수가 정확해야 한다.", 1, getOrder.getOrderItems().size()); assertEquals("주문 가격은 가격 * 수량이다.", 10000 * orderCount, getOrder.getTotalPrice()); //Transactional이 없기 때문에 객체를 다시 불러와서 조회 Item findBook = itemRepository.findOne(book.getId()); assertEquals("주문 수량만큼 재고가 줄어야 한다.", 8, findBook.getStockQuantity()); } private Book createBook(String name, int price, int stockQuantity) { Book book = new Book(); book.setName(name); book.setPrice(price); book.setStockQuantity(stockQuantity); itemService.saveItem(book); // service에서 저장 return book; } private Member createMember() { Member member = new Member(); member.setName("회원1"); member.setAddress(new Address("서울", "강가", "123-123")); memberService.join(member); // service에서 저장 return member; } @Repository public class OrderRepository { private final EntityManager em; public OrderRepository(EntityManager em) { this.em = em; } // fetch join으로 LazyLoding 문제 해결 public Order findOne(Long id) { return em.createQuery( "select o from Order o join fetch o.orderItems where o.id = :id", Order.class ) .setParameter("id", id) .getSingleResult(); } 위와 같이 코드를 수정해서 @Transactional이 없어도 테스트를 통과하도록 변경하였습니다. 하지만 아래의 코드는 모든 객체를 테스트 작성자가 신경써야 하고 JPA가 관리하는 여러 가지 기능들을 사용하지 못하게 됩니다. 또한 rollback 기능을 사용하지 못해 테스트 작성자가 일일이 DB에 저장된 값을 지워줘야 합니다. 영한님은 이와 같은 상황에서는 어떤 선택이 좋다고 생각하는지 궁금합니다.
8월 11, 2024 1:23:56 오전 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation INFO: HHH000204: Processing PersistenceUnitInfo [ name: hello ...] 8월 11, 2024 1:23:56 오전 org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.3.10.Final} 8월 11, 2024 1:23:56 오전 org.hibernate.cfg.Environment <clinit> INFO: HHH000206: hibernate.properties not found Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException at org.hibernate.boot.spi.XmlMappingBinderAccess.<init>(XmlMappingBinderAccess.java:43) at org.hibernate.boot.MetadataSources.<init>(MetadataSources.java:86) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.<init>(EntityManagerFactoryBuilderImpl.java:212) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.<init>(EntityManagerFactoryBuilderImpl.java:174) at org.hibernate.jpa.boot.spi.Bootstrap.getEntityManagerFactoryBuilder(Bootstrap.java:76) at org.hibernate.jpa.HibernatePersistenceProvider.getEntityManagerFactoryBuilder(HibernatePersistenceProvider.java:171) at org.hibernate.jpa.HibernatePersistenceProvider.getEntityManagerFactoryBuilderOrNull(HibernatePersistenceProvider.java:119) at org.hibernate.jpa.HibernatePersistenceProvider.getEntityManagerFactoryBuilderOrNull(HibernatePersistenceProvider.java:61) at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:50) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54) at hellojpa.JpaMain.main(JpaMain.java:12) Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ... 12 more Process finished with exit code 1 JpaMain.java 를 실행시키면 저렇게 나옵니다. 잘 되는건줄 알았는데, 에러가 떠서 서버에 HelloA이게 저장이 안됩니다. 그리고 ======================== **javax jakarta 패키지 이름 변경 예)** **JPA 라이브러리** `import javax.persistence.*` **JPA 애노테이션** `javax.persistence.Entity` **persistence.xml 설정** `javax.persistence.jdbc.driver` `jakarta.persistence.jdbc.driver` `javax.persistence.jdbc.user` `jakarta.persistence.jdbc.user` `javax.persistence.jdbc.password` `jakarta.persistence.jdbc.password` `javax.persistence.jdbc.url` `jakarta.persistence.jdbc.url` **H2 데이터베이스 관련해서 이슈가 해결되지 않으면 다음 링크를 참고해주세요.** https://docs.google.com/document/d/1j0jcJ9EoXMGzwAA2H0b9TOvRtpwlxI5Dtn3sRtuXQas/ edit#heading=h.3ryn0qha48vi 이거는 수정을 해주라는건가요??? 어디에 어느부분을 수정 해줘야 할까요?? 구글링해서 스스로 해결해보고 싶었는데 도저히 안되서 질문 두가지 남깁니다.
안녕하세요. 영속성 컨텍스트에서 변경 감지 (dirty checking) 부분 수강 중 질문이 있습니다. 변경감지란, 영속성 컨텍스트에서 조회(em.find)한 값을 변경하면, 1차 캐시도 자동으로 업데이트가 일어나는 현상으로 이해하였습니다. 위의 코드와 같이 em.find로 조회하는 경우 1차 캐시에도 Id=memberA 인 엔티티가 저장되는 것으로 알고 있는데요. 1차 캐시에 저장되는 엔티티의 주소가 x001이라고 했을 때, em.find로 가져온 객체 memberA도 같은 객체 (x001)를 가져오기 때문에, memberA의 값을 업데이트하면 1차 캐시 값도 자동으로 업데이트 되는게 맞을까요?
엔티티 매니저 팩토리 사용자가 설정한 정보에 따라 엔티티 매니저 팩토리를 생성합니다. 이 엔티티 매니저 팩토리는 오직 애플리케이션이 실행될 때 하나만 존재해야 합니다. 엔티티 매니저 팩토리의 주요 기능은 엔티티 매니저를 생성하는 겁니다. 사용자의 요청이 디비로 쿼리를 보내는 경우라면 요청과 1:1로 매핑 되는 엔티티 매니저를 생성합니다. 엔티티 매니저 엔티티 매니저의 핵심 기능은 엔티티를 관리하는 것입니다.엔티티 매니저에는 영속성 컨텍스트라는 논리적 공간이 존재합니다. 여기서 엔티티를 관리합니다. 저장 persist()를 할 경우 엔티티 매니저에 엔티티를 저장합니다. 이때 1차 캐시에 키는 엔티티의 pk값, 값은 엔티티를 값으로 저장 합니다. 저장 시 전략이 identity인 경우 insert 쿼리를 DB로 바로 전송합니다. 그 외는 쓰기 저장소에 보관합니다. 조회 find()를 할 경우 처음에는 1차 캐시에서 조회하고 없으면 DB에서 조회합니다. 주의할점은 즉시로딩과 지연로딩입니다. 즉시로딩 은 A 조회시 A와 연관관계에 있는 데이터들도 한번에 조회하는 방식입니다. 지연로딩 은 A 조회 후 A와 연관관계에 있는 데이터 사용 시 조회하는 방식입니다. 수정 더치 체킹 덕분에 값만 변경해도 JPA가 알아서 update 쿼리를 보냅니다. 플러시 플러시가 발생 시 우선 더티 체킹이 발생합니다. 값이 변경된 부분이 있으면 update, delete 쿼리를 생성하여 쓰기 저장소에 저장합니다. 더티 체킹이 끝나면 쓰기 저장소에 보관했던 SQL문들을 DB로 한번에 전송합니다. 쓰기 저장소 em.persist()에 의해 발생한 insert 쿼리 더디 체킹에 의해 발생한 update 쿼리 em.remove()에 의해 발생한 delete 쿼리 위 상황에서 발생한 쿼리들을 보관하는 저장소입니다. 플러시 발생 시 위 쿼리들을 DB에 한번에 전송합니다. 감사합니다.
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 { Team team = new Team(); team.setName("TeamA"); em.persist(team); Member member = new Member(); member.setName("member1"); member.setTeam(team); em.persist(member); // team.getMembers().add(member); em.flush(); em.clear(); Team findTeam = em.find(Team.class, team.getTeamId()); List<Member> members = findTeam.getMembers(); System.out.println("==============="); for (Member m : members) { System.out.println("==========="); System.out.println("m = " + m.getName()); System.out.println("==========="); } tx.commit(); } catch (Exception e) { tx.rollback(); } finally { em.close(); } emf.close(); } } 위 코드를 실행했을때 출력 결과는 아래와 같습니다. Hibernate: select t1_0.TEAM_ID, t1_0.name from Team t1_0 where t1_0.TEAM_ID=? =============== Hibernate: select m1_0.TEAM_ID, m1_0.MEMBER_ID, m1_0.USERNAME from Member m1_0 where m1_0.TEAM_ID=? =========== m = member1 =========== 제가 예상한 실행 결과는 findTeam.getMembers(); 로 인해 두 번째 select 쿼리문이 나오고 그 다음에 =============== 가 출력 될거라고 예상을 했습니다. 근데 실행 결과는 =============== 가 먼저 출력 되고 두 번째 select 쿼리문이 출력 됐습니다. 그래서 이걸 보고 든 생각이 main 스레드가 코드를 한줄 씩 실행하다가 findTeam.getMembers(); 부분에서 SQL문을 디비로 보낼 스레드를 만들어서 해당 스레드에게 그러한 역할을 넘기고 바로 다음줄을 실행하여 =============== 가 두번째 select 쿼리문 보다 먼저 출력되는건지 궁금합니다. 감사합니다.
강의 처럼 벌크 연산 후 int resultCount = em.createQuery("update Member m set m.age=20") .executeUpdate(); clear없이 바로 createQuery를 날리면, Member result = em.createQuery("select m from Member m where m.id = :id", Member.class) .setParameter("id", member1.getId()) .getSingleResult(); System.out.println("member.getAge() = " + result.getAge()); 아래와 같은 select 쿼리가 날라가고 age가 원래값으로 나옵니다. Hibernate: /* select m from Member m where m.id = :id */ select m1_0.id, m1_0.age, m1_0.memberType, m1_0.TEAM_ID, m1_0.username from Member m1_0 where m1_0.id=? member.getAge() = 10 ////////////////////////////// find처럼 selcet문이 안날라갈줄 알았는데, 날라가서 createQuery 작동방식이 궁금합니다. 1.이 쿼리가 실제로 db에 날라갔는데, age=20값을 가지고오면서 영속성 컨텍스트에 들렸는데, 이미 캐시가 존재해서, 쿼리 값을 업데이트 안해주고 원래값 age=10을 가지고 온건가요? 2. 아니면 로그에 보이는 쿼리가 실제 db 에 날라가지 않고 생성만된채로, 영속성 컨텍스트를 찾는건가요?
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] querydsl 강의를 듣고 복습하면서 적용해보고 있는데, memberRepository 부분에서 @RequiredArgsConstructor을 사용했더니 compileJava부분에서 em의 초기값이 없다는 예외가 발생했습니다. 생성자를 활용해서 해결되긴 했는데 그 전의 코드에서 왜 작동이 안되었는지 궁금해서 질문드립니다. @RequiredArgsConstructor을 사용한다던가 하는 더 간단한 방법도 있는지 궁급합니다. 코드파일입니다 https://drive.google.com/file/d/1ezwW4PrReG_BX-lmAC1uiOL8Syk3GbFW/view?usp=sharing
queryFactory.select(Projections.bean(GetMyCommentResponse.MyCommentInfo.class, commentEntity.id.as("commentId"), commentEntity.content.as("content"), commentEntity.likeCount.as("likeCount"), commentEntity.createDateTime.as("createDateTime"), Projections.bean( GetMyCommentResponse.MyCommentedPopUpInfo.class, postEntity.id.as("postId"), postEntity.name.as("postName"), postEntity.mainImageUrl.as("mainImageUrl"), ).as("postInfo"), // commentEntity // 엔티티도 같이 조회하면 에러가 나지 않음 )) .from(commentEntity) .join(commentEntity.post, postEntity).fetchJoin() 안녕하세요 영한님! 강의 잘 듣고 있습니다. 공부 중에 코드를 작성하다 query specified join fetching, but the owner of the fetched association was not present in the select list 이런 에러가 발생했는데, fetchjoin시 실제 질의하는 대상 Entity를 select절에 쓰지않아서 에러가 나더라구요. select절에 commentEntity를 추가하면 에러가 발생하지 않지만 이미 commentEntity의 필드들은 모두 가져왔는데, 또 조회하는게 불필요한 작업이라고 생각되어서요. 그렇다고, fetchjoin이 아닌 일반 조인을 써버리면 코멘트마다 포스트에 대해서 n+1 문제가 발생할 것이라고 생각되구요 이러한 상황에서 좋은 해결방법이 있을까요? 감사합니다
강의하신 코드랑 같고 try { Team team = new Team(); team.setName("teamA"); em.persist(team); Member member = new Member(); member.setUsername("member"); member.setAge(10); member.setTeam(team); em.persist(member); em.flush(); em.clear(); String query = "select m from Member m left join m.team t"; List<Member> result = em.createQuery(query, Member.class) .getResultList(); tx.commit(); } catch (Exception e) { tx.rollback(); e.printStackTrace(); } finally { em.close(); } 결과 Hibernate: /* select m from Member m left join m.team t */ select m1_0.id, m1_0.age, m1_0.TEAM_ID, m1_0.username from Member m1_0 left join이 안나가네요, jpql로 join까지 썻는데 왜 안나가는걸까요
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 { Member member = new Member(); member.setUsername("A"); System.out.println("======================"); em.persist(member); Thread.sleep(9000); System.out.println("======================="); tx.commit(); } catch (Exception e) { tx.rollback(); } finally { em.close(); } emf.close(); } } @Entity public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "name", nullable = false) private String username; public Member() { } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } 우선 생각 했던 결과는 현재 identity 전략이므로 em.persist() 할 경우 insert 쿼리가 DB에 바로 보내지고 DB의 Member 테이블에 값이 저장되는거를 예상했습니다. 실제 실행해 보면 ====================== Hibernate: /* insert for jpa_basic.ex1_hello_jpa.hellojpa.Member */insert into Member (name, id) values (?, default) Thread.sleep(9000) 전까지는 insert 쿼리가 콘솔에 잘 찍힙니다. 그래서 H2 콘솔에 데이터가 잘 저장이 됐는지 확인 해봤습니다. 결과는 저장이 되지 않았습니다. Thread.sleep(9000)가 끝나고 나서야 데이터가 저장이 됩니다. 그래서 생각했던 결과인 identity 전략에서 em.persist() 할 경우 왜 DB에 바로 저장이 안되는건지 궁금합니다. 참고로 http://hibernate.hbm2ddl.auto 옵션은 create입니다. 감사합니다.
Member.java @OneToMany @JoinColumn(name="member") private List<Order> orders = new ArrayList<>(); Order.java @ManyToOne @JoinColumn(name="member_id") //맵핑할 컬럼 private Member member; Member 의 joinColumn 은 member 객체 자체이고 Order의 joinColumn은 db의 컬럼 명인데 member 의 joinColumn 을 member_id, 즉 db의 컬럼명으로 또는 그냥 id 라고 하면 안되나요? 안된다면 이유도 알려주시면 감사하겠습니다!!
수정, 삭제, 삽입 같은 경우에는 데이터 변경이라 영속성 컨텍스트의 쓰기 저장소에 보관되어 있다가 커밋 직전에 한번에 보내지는 반면 em.find()를 통해 발생하는 조회 쿼리는 find() 사용시 바로 디비에 전송이 되는건지 궁금합니다. em.find()를 통해 발생한 조회 쿼리는 트랜잭션 커밋 종료에 상관없이 디비에 바로바로 전송이 되는건지 궁금합니다.
DTO 쿼리 작성 시 join fetch 로 작성하는 경우는 에러가 발생하고 join 으로 작성하는 경우에는 에러가 발생하지 않았는데요. join fetch 의 개념은 결국 지연로딩을 작동시키지 않고 한 번에 연관된 것들 다 가져온다는 것인데 왜 여기선 안 되는 것인지 궁금합니다. 엔티티 @Query문하고 DTO @Query 테스트 코드를 돌렸는데 서로 영향을 미쳐 테스트가 실패라고 뜹니다. @Transactional 을 붙여 롤백이 되게끔 하여 해결은 했지만 궁금한 점이 있습니다. 스프링 데이터 JPA 테스트를 돌릴 때 @SpringBootTest 어노테이션 붙이면 자동으로 롤백이 되는 것이 아닌가요? package study.springdatajpa.repository; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; import study.springdatajpa.domain.Member; import study.springdatajpa.domain.Team; import study.springdatajpa.dto.MemberDto; import java.util.List; @SpringBootTest class MemberRepositoryTest { @Autowired private MemberRepository memberRepository; @Autowired private TeamRepository teamRepository; @Test @Transactional void testQuery() { Member m1 = new Member("AAA", 10); Member m2 = new Member("BBB", 20); memberRepository.save(m1); memberRepository.save(m2); List<Member> result = memberRepository.findUser("AAA", 10); Assertions.assertThat(result.get(0)).isEqualTo(m1); } @Test @Transactional void testDtoQuery() { Team team = new Team("teamA"); teamRepository.save(team); Member m1 = new Member("AAA", 10); Member m2 = new Member("BBB", 20); m1.setTeam(team); m2.setTeam(team); memberRepository.save(m1); memberRepository.save(m2); List<MemberDto> dtoList = memberRepository.findUserDto(); Assertions.assertThat(dtoList.size()).isEqualTo(2); } }
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? 아니요 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 모르겠습니다..ㅜㅜ 안보이더라고요 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/ [질문 내용] 강사님 fetch join과 entityGraph에 대해서 공부를 해서, 연관된 객체에 대해서 가져올때, fetch join을 통해서 한방에 가져오는 법을 배웠는데, 만약 양방향 매핑을 풀고, 단방향 매핑으로 코드를 작성하려고 하는데, @Data @NoArgsConstructor @Entity(name ="team") public class TeamJpaEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name ="team_id") private Long id; private String name; @OneToMany(mappedBy = "myTeamId") private List<MemberJpaEntity> memberJpaEntityList = new ArrayList<>(); } ---------- @Data @NoArgsConstructor @Entity(name = "user") public class MemberJpaEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name ="user_id") private Long id; private String name; @Column(name = "team_id") private Long myTeamId; } 일때, 양방향 매핑을 끊어서, 단반향 매핑만 되게끔 수정을 했는데, 이렇게 되면 패치조인을 못 하지않나여? 궁금해서 질문을 올립니다...ㅜ 순환참조를 삭제하려고 없애고, TeamJpaRepository.findById(teamId)로 팀이 필요할때 조회할 수 있게 만들었는데, 한방으로는 못 가져오나여?