묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결실전! Querydsl
querydsl 2개의 파일에서 같은 조건을 사용해야 할 경우
강사님 안녕하세요.좋은 강의 잘 들었습니다.querydsl의 정점 중 하나로 where 조건문을 분리하고 재사용한다고 했는데,예를 들어 UserRepository, StudyRepository라는 2개의 querydsl을 사용하는 repository가 있다 했을 때, 각각의 respository 에서 조회를 할 때 동일한 조건을 사용해야 할 경우가 있습니다. UserRepository.javapublic User getUser(Long UserId) { return selectForm(user) .where(userId); } private BooleanExpression eqUser(Long userId) { return user.userId.eq(userId); }StudyRepository.javapublic User getUser(Long UserId) { select(study) .from(study.user, user) .join(study.user) .where(user.userId.eq(UserId)); } private BooleanExpression eqUser(Long userId) { return user.userId.eq(userId); } 이렇게 .where(user.userId.eq(1L)) 가 2개의 파일에서 반복되는 경우 각각의 파일에 메소드로 사용하는게 좋을까요? 아니면 다음처럼 공통 유틸 파일을 만들어서 공통으로 사용하는게 좋을까요?QueryUtils.javapublic static BooleanExpression eqUser(Long userId) { return user.userId.eq(userId); }실무에서는 어떻게 사용 할까요? 이런 경우가 빈번해서 질문 드립니다.
-
미해결코드로 배우는 React 19 with 스프링부트 API서버
ModelMapper 와 entityToDto 차이
강사님의 강의에서 엔티티를 DTO로 변환할때 2가지 방식을 다 보여주셨는데 , 모델 매퍼로 엔티티 -> DTO 변환방식과 entityToDto 메소드 처럼 직접 개발자가 명시해줘서 엔티티를 DTO로 변환하는 방식의 차이점과 선택기준이 궁금합니다!!
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
섹션3 질문 있습니다
안녕하세요!섹션3 마지막 부분쯤 나온 @Component 어노테이션에 대해 질문이 있습니다!이 어노테이션은 다음과 같은 경우에 사용된다고 하셨는데요!컨트롤러, 서지스, 리포지토리가 모두 아니고,개발자가 직접 작성한 클래스를 스프링 빈으로 등록할 때 사용된다. 제가 궁금한 것은 Service 나 리포지토리 모두 우리가 직접 작성한 클래스 아닌가요??그러면 컴포넌트 어노테이션 아닌가요??답변해주시면 감사하겠습니다
-
해결됨입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
섹션3 컨트롤러테스트 오류
안녕하세요. 강의노트와 강의를 보면서 수정해 보았는데 resume에서만 test가 실패합니다. 제가 보기엔 resume가 JSON이 null이라서 그런것 같은데 어느 부분을 봐야할지 모르겠습니다. Datainitiallizer.kt를 봐도 잘 안보이네요. 아래는 에러 메세지와 코드입니다.java.lang.NullPointerException: Cannot invoke "org.json.JSONArray.length()" because the return value of "org.json.JSONObject.optJSONArray(String)" is null at com.dogu.portfolio.presentation.controller.PresentationApiControllerTest.testGetResume(PresentationApiControllerTest.kt:66) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) @Test @DisplayName("resume") fun testGetResume() { //given val uri = "/api/v1/resume" //when val mvcResult = performGet(uri) val contentAsString = mvcResult.response.getContentAsString(StandardCharsets.UTF_8) val jsonObject = JSONObject(contentAsString) //then Assertions.assertThat(jsonObject.optJSONArray("experiences").length()).isPositive() Assertions.assertThat(jsonObject.optJSONArray("achievements").length()).isPositive() Assertions.assertThat(jsonObject.optJSONArray("skills").length()).isPositive() }
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
교재 내용중에 질문 있습니다.
교재 내용중에 아래와 같은 내용이 있습니다."데이터가 변해도 식별자로 지속해서 추적 가능회원 엔티티의 키나 나이값을 변경해도 식별자로 인식가능"여기서 궁금한 부분이 회원 엔티티의 키가 식별자라고 생각하는데 여기서 키가 바뀌게 되면 식별자도 바뀌게 되어 추적이 불가능하지 않나요?
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
join과 join feth 차이점
public List<Order> findSearchOrders(OrderSearch orderSearch){ return em.createQuery("select o from Order o join o.member m" + " where o.orderStatus = :status" + " and m.name like :name", Order.class) .setParameter("status", orderSearch.getOrderStatus() ) .setParameter("name", orderSearch.getMemberName()) .setMaxResults(1000) .getResultList(); } public List<OrderQueryDto> findQueryOrder() { List<OrderQueryDto> result = findAllOrder(); for (OrderQueryDto order : result) { List<OrderItemQueryDto> orderItems = findOrderItem(order.getId()); order.setOrderItems(orderItems); } return result; } public List<OrderItemQueryDto> findOrderItem(Long orderId) { String query = "select new jpabook.jpashop.repository.order.query." + " OrderItemQueryDto(oi.order.id, oi.orderPrice, oi.count )" + " from OrderItem oi " + " join oi.item i" + " where oi.order.id = :orderId"; return em.createQuery(query,OrderItemQueryDto.class) .setParameter("orderId", orderId) .getResultList(); } public List<OrderQueryDto> findAllOrder(){ String query = "select new jpabook.jpashop.repository.order.query." + " OrderQueryDto(o.id, m.name, d.address, o.orderStatus, o.orderDate) " + " from Order o " + " join o.member m" + " join o.delivery d"; return em.createQuery(query, OrderQueryDto.class).getResultList(); }첫번째 코드 블럭은 order를 select 할때 member를 join 하면 member를 프록시 객체로 가져오는걸로 알고 있습니다. join fetch 를 하게 되면 바로 객체로 가지고 오는걸로 알고 있습니다. 하지만, 두번째 코드 블럭은 그냥 join을 했는데, 왜 프록시 객체에서 객체로 전환? 프록시 객체가 객체를 가르키는 행위를 하지않고도 dto에 바로 삽입이 가능한가요?ex) order.getName() , member.getName()
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
CascadeType.ALL, CascadeType.REMOVE 옵션에 대해 질문 있습니다.
부모를 삭제 시에는 CascadeType.ALL과 CascadeType.REMOVE가 똑같이 동작한다고 이해해서 이 둘에 대해 동시에 질문 하겠습니다.만약에 하나의 게시글이 있고 이 게시글에 달려있는 첨부파일들이 100개 라고 하겠습니다.이때 CascadeType.ALL 옵션을 사용하면 게시글 삭제 시 100개의 첨부파일들도 삭제 된다고 이해를 했습니다.궁굼한 부분은 게시글 삭제 시 게시글에 달린 100개의 첨부파일에 대해 delete 쿼리가 따로 따로 보내지는건지 아니면 jpa가 최적화 해서 한번에 보내는건지 궁금합니다.만약에 1번 질문에서 Jpa가 알아서 최적화해서 delete 쿼리를 DB로 보낸다고 가정하겠습니다. 그러면 100개의 첨부파일에 대한 Id 값을 다 뽑아서 이 값들을 delete 쿼리의 where절의 in에 넣어서 삭제 쿼리를 보내는것 대신에 Jpa가 최적화 해서 delete 쿼리를 보내는 방식이 더 좋은지 궁금합니다.
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
26강 오류입니다 !
Execution failed for task ':LibraryAppApplication.main()'.> Process 'command 'C:/Program Files/Java/jdk-11/bin/java.exe'' finished with non-zero exit value 1 이것은 어떤 에러일까요 ㅠㅠ
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
@MappedSuperclass 공통속성 자동화
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]Mapped Superclass- 매핑 정보 상속 6:44 부분에 말씀하신 작성자, 작성일, 수정자, 수정일을 자동화하는 jpa 기능이 있다고 말씀 해주셨는데말씀 하신 건만 듣고는 이해가 안되서요자동화 시키는 걸 쉽게 할 수 있는 방법이 있는 건가요?바로 적용해보고 싶어서요
-
해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
비즈니스로직 처리 위치
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]지금까지 컨트롤러단에서 로직을 다 작성하셨는데요원래 비즈니스로직 같은 경우는 서비스 단에서 처리를 해야 되지 않나 생각이 들어 질문합니다.간단한 예시여서 컨트롤러 단에서 로직을 처리하고 dto를 이너클래스로 만드신 건가요?비즈니스로직 처리 위치를 컨트롤러에서 해야될지 서비스단에서 해야될지 기준이 잘안서서 질문드립니다.~~
-
해결됨Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)
쿠버네티스 버전에서의 토큰 검사
안녕하세요, 강의 잘 들었습니다.기존에 로그인 후 발급한 토큰을 apigateway에서 검사했었는데 k8s 버전에서는 별도로 토큰을 검사하지 않는 것 같습니다. 로그인하는 의미가 없어질 것 같은데 이부분을 추가하려면 각 서비스에 따로 추가를 하는게 좋을까요?아니면 중앙으로 처리하는 방법이 따로 있을까요?
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
'회원 이름 필수 입력' 기능
안녕하세요. 웹계층 회원등록 부분 강의 따라서 코딩하고, HTML 파일도 복붙했는데홈페이지에 이름을 입력 안 해도 '회원 이름을 필수입니다' 메세지가 뜨는 반응이 없이 redirect됩니다. h2에는 이름이 비어 있는 상태로 데이터가 들어와 있습니다. @NotEmpty가 왜 작동하지 않는지 질문드립니다. https://drive.google.com/file/d/1jvu4wiWz3GwoZ3YazNDJWsFTOxgSqt-M/view?usp=sharing
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
readonly 트랜잭션에 update를 수행하는 분리된 트랜잭션 메서드를 호출 할 때 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.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로 조회 성능을 챙기면서 변경 감지를 사용할 순 없는 것인가요 ??
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
@Transactional을 사용한 테스트에 대해서 질문드립니다
안녕하세요 영한님 강의 정말 잘 듣고 있습니다. ㅎㅎ@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에 저장된 값을 지워줘야 합니다.영한님은 이와 같은 상황에서는 어떤 선택이 좋다고 생각하는지 궁금합니다.
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
도저히 모르겠서용;;;;;
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 이거는 수정을 해주라는건가요??? 어디에 어느부분을 수정 해줘야 할까요?? 구글링해서 스스로 해결해보고 싶었는데 도저히 안되서 질문 두가지 남깁니다.
-
미해결자바 ORM 표준 JPA 프로그래밍 - 기본편
변경감지(dirty checking) 관련 기본적인 질문
안녕하세요. 영속성 컨텍스트에서 변경 감지 (dirty checking) 부분 수강 중 질문이 있습니다. 변경감지란, 영속성 컨텍스트에서 조회(em.find)한 값을 변경하면, 1차 캐시도 자동으로 업데이트가 일어나는 현상으로 이해하였습니다. 위의 코드와 같이 em.find로 조회하는 경우 1차 캐시에도 Id=memberA 인 엔티티가 저장되는 것으로 알고 있는데요. 1차 캐시에 저장되는 엔티티의 주소가 x001이라고 했을 때, em.find로 가져온 객체 memberA도 같은 객체 (x001)를 가져오기 때문에, memberA의 값을 업데이트하면 1차 캐시 값도 자동으로 업데이트 되는게 맞을까요?
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
강의 순서 질문
기본편 들은 상태인 지금 활용 1만듣고, data jpa 강의 들어도 되나요?jpa를 당장 써야돼서 data jpa 들은 후에 활용2를 들으려고 하는데요, 괜찮을련지 궁금하네요.
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
지금까지 배운 내용을 정리 했는데 맞게 정리한건지 궁금합니다.
엔티티 매니저 팩토리사용자가 설정한 정보에 따라 엔티티 매니저 팩토리를 생성합니다. 이 엔티티 매니저 팩토리는 오직 애플리케이션이 실행될 때 하나만 존재해야 합니다. 엔티티 매니저 팩토리의 주요 기능은 엔티티 매니저를 생성하는 겁니다. 사용자의 요청이 디비로 쿼리를 보내는 경우라면 요청과 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에 한번에 전송합니다. 감사합니다.
-
미해결Practical Testing: 실용적인 테스트 가이드
잘못된 Interceptor 사용으로 인한 h2-console 접근 문제
안녕하세요. 강의에서 h2 db를 사용하시는 것을 보고 제 개인프로젝트에서 profile이 test일 경우에 h2 db를 써보기로 결정했습니다.그런데 제가 AuthInterceptor를 잘못 만들어서인지 localhost:8080/h2-console url로 접속하면 JSON 응답이 나와버립니다. 어떤 코드가 잘못되었는지, 어떻게 개선해야 하는지 봐주실 수 있으실까요..? 제가 작성한 코드와 설명을 남깁니다. application.ymlspring: profiles: default: local jpa: hibernate: ddl-auto: none mail: host: smtp.gmail.com port: 587 username: kanggi1997@gmail.com password: 보안을위해지웁니다 properties: mail: smtp: starttls: enable: true auth: true --- spring: config: activate: on-profile: local datasource: url: jdbc:postgresql://localhost:5432/forecast?useSSL=false driver-class-name: org.postgresql.Driver username: gunwoo jpa: properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true show-sql: true hibernate: ddl-auto: update --- spring: config: activate: on-profile: test h2: console: enabled: true # /h2-console 설정 datasource: url: jdbc:h2:mem:~/databasesByH2/forecastBE driver-class-name: org.h2.Driver username: sa password: jpa: properties: hibernate: format_sql: true show-sql: true hibernate: ddl-auto: create spring security를 사용하는데 어려움을 느껴 직접 인가를 구현하기 위해 AuthInterceptor와 WebConfig를 구성했고 사이트 회원만 api에 접근할 수 있게 만들었습니다. 로그인하지 않았을 경우 로그인이 필요하다는 메시지를 JSON형태로 전달합니다.AuthInterceptorpackage site.gunwoo.forecastBE.config; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import site.gunwoo.forecastBE.dto.ResponseDTO; @Component @Slf4j public class AuthInterceptor implements HandlerInterceptor { private ObjectMapper mapper = new ObjectMapper(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if ("OPTIONS".equals(request.getMethod())) { log.debug("preflight은 통과시킴"); return true; } String loggedInUserEmail = (String) request.getSession().getAttribute("loggedInUser"); if (loggedInUserEmail == null) { response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); response.setStatus(HttpStatus.UNAUTHORIZED.value()); ResponseDTO responseDTO = new ResponseDTO("로그인이 필요합니다.", null); String jsonResponse = mapper.writeValueAsString(responseDTO); response.getWriter().write(jsonResponse); return false; } return true; } } 아래의 addInterceptors 메서드에서 AuthInterceptor의 처리를 거치지 않아도 되는 uri를 정의했습니다. h2 콘솔 접근을 위한 uri인 "/h2-console"도 포함시켰습니다. 하지만 여전히 로그인이 필요하다는 응답이 JSON 형식으로 나타납니다.WebConfigpackage site.gunwoo.forecastBE.config; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { private final AuthInterceptor authInterceptor; @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:5173") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("Authorization", "Content-Type") .exposedHeaders("Custom-Header") .allowCredentials(true) .maxAge(3600); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/**") .excludePathPatterns("/user/join") .excludePathPatterns("/user/login") .excludePathPatterns("/test") .excludePathPatterns("/regions") .excludePathPatterns("/h2-console"); //적용이 안되는 듯 } }
-
해결됨자바 ORM 표준 JPA 프로그래밍 - 기본편
쿼리를 보내는 부분은 따로 스레드를 만들어서 처리하는건지 궁금합니다.
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 쿼리문 보다 먼저 출력되는건지 궁금합니다. 감사합니다.