월 19,800원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
어그리거트 설계
jpa를 활용해서 어그리거트를 만들고 있습니다. 게시글 (post 어그리거트)게시자 (member 어그리거트)로 어그리거트를 나눴습니다. 그리고 프로젝트 패키지 구조는 이런 식 입니다.controller- post- memberservice- post- memberdomain (entity와 repository 인터페이스)- post- memberinfra (repository 구현체)- post- member 만약 같은 데이터베이스임에도 post엔티티가 member엔티티를 간접참조(Long memberId를 통한 참조)를 하고 있다면, post 상세페이지에서 member의 이름을 띄워주는 것을 어떻게 구현해야 좋을까요? 현재 고려하고 있는 방법은 두 가지 입니다.service/post 패키지에 PostDetailService 객체를 만든다. 그리고 이 객체가 postRepository와 memberRepository를 di 받은 다음에 두 엔티티를 가져오고 responseDto로 합쳐서 PostDetailController에게 리턴한다.이유는 member 어그리거트가 다른 서버로 분리될 경우, memberRepository의 구현체만 변경하면 될 것 같아서 입니다. (member의 서버가 다른 서버가 되면 member 정보를 api로 받아와야 할 것 같아서 그렇게 하였습니다.) 또 다른 방법으로는 현재는 같은 rdb에 저장되어 있으니 Dao객체를 만들어서 조인으로 responseDto를 직접 만드는 방법이 있을 것 같습니다. jpa 연관관계는 없으니 조인으로 쿼리를 날려야 할 것 같습니다. 어느 방법이 더 좋은 방법인가요? 혹은 더 좋은 방법이 있을까요?
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
DTO를 Result<T> 클래스에 담아서 반환할 때의 예외처리
안녕하세요!영한님 강의를 들으면서 추가적으로 RestTemplate에 대해 알게되어 지금까지 배운 것을 복습할 겸 api와 DTO 통신에 대해 연습하고 있었습니다.DTO를 반환할 때, 그냥 주기보다는 Result<T>의 data같은 속성에 담아서 주는 것이 바람직하다고 하여 그 응용을 연습해 볼겸 Result<T>에 status 속성을 추가해보았습니다.status 속성은 외부 api에서 조건을 충족하는 api를 찾아 와서 반환할 DTO가 있을 경우 "success"를, 조건을 충족하는 api를 찾지 못하여 HttpClientErrorException 예외가 발생할 경우 "fail"을 담아서 반환하고자 하였습니다.아래의 코드를 통해 구현은 성공하였으나, Service 클래스에서 try-catch문을 사용하는게 좀 찜찜하여 혹시 try-catch문 없이 구현하는게 더 좋을지 아니면 그냥 사용해도 좋은지가 궁금하여 질문을 하게 되었습니다.(status가 "fail"이면 data는 null을 반환하게끔 설계해보았습니다) @RestController @RequiredArgsConstructor @RequestMapping("/summoners") public class SummonerController { private final SummonerService summonerService; @GetMapping public ResponseEntity<Result<SummonerDTO>> findSummonerApi(@RequestParam String name) { SummonerDTO summonerDTO = summonerService.findSummonerApi(name); if (summonerDTO == null) { return ResponseEntity.ok().body(new Result<>("fail", null)); } return ResponseEntity.ok().body(new Result<>("success", summonerDTO)); } }@Slf4j @Service @RequiredArgsConstructor public class SummonerService { @Value("${apiKey}") private String apiKey; private final SummonerRepository summonerRepository; private final RestTemplate restTemplate; // riot에서 소환사 정보 api를 받아오는 메서드 public SummonerDTO findSummonerApi(String name) { String url = "https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/{summonerName}"; HttpHeaders headers = new HttpHeaders(); headers.set("X-Riot-Token", apiKey); try { return restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), SummonerDTO.class, name).getBody(); } catch (HttpClientErrorException e) { return null; } } }Controller와 Service의 코드는 위와 같습니다. (restTemplate은 별도의 설정파일에 빈으로 구현하였습니다)
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
jpa_pdf 객체 조회 부분에서..
memberId를 파라미터로 받고 해당하는 단일 Member 객체를 반환하는 find 메소드와 연관지었을 때JOIN 뒤에 있어야 할 WHERE M.MEMBER_ID = ? 와 같은 where 절이 없는데.. 생략된 것인가요?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
오타 제보
영한님 안녕하세요.오타 제보 합니다.^^ 스프링 트랜잭션 전파1 -기본.pdf 파일 11page에 AS IS스프링 이 경우 외부 트랜잭션과 ~ TO BE스프링에서 이 경우 외부 트랜잭션과 ~ 감사합니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
criteriaApi 관련 사담
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. criteriabuilder cb-> 조건식 빌더 -> 스트링빌더같은 생성기criteriaQuery cq-> 조건 쿼리(조건식) -> criteriaBuilder 에서 생성, Member 관련 정보 넘김root -> 조건식이 걸리는 뿌리 -> 주체Path -> 조건식을 구하는 path -> file path 처럼 오브젝 필드도 . 노테이션으로 구해지니 이렇게 쓰임Predicate -> 조건자 (like , between 같은 조건이 붙음)이런 조건자은 where 뒤에 붙기에.and는 둘다 맞는 조건이 필요하기 때문cq.where(cb.and(between, like))그냥 cq = cq.where(cb.and(between,like)) 해도 됬을거 같음.조건식.순서(조건식생성기.내림차순(기준 = 나이))em.쿼리생성(조건식 query).최대결과개수(3).결과리스트()
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
connection. session. transaction 시작 순서가 궁금합니다.
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]안녕하세요!제가 수업을 듣고 이해한 바로는 1. sql 쿼리를 날리면 Connection이 생기고2. 그러면 Database 안에서 Session이 생기고 3. 그 다음에 Session 안에서 Transaction이 시작되는 것으로 파악했습니다. 그런데, 이번 강의 로그를 보니까 Creating new Transaction이 먼저 찍히고그 다음에 Acquired Connection 로그가 찍히는데 순서가 정확히 어떻게 되는 걸까요?
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
[정보] 최신 스프링부트 사용 시 build.gradle.kts 쓰시는 분들 위한..
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 크게 살려주신 분 : java.lang.NoClassDefFoundError: javax/persistence/Entity — 기록하는 곳 (tistory.com)//https://central.sonatype.com///search for mavencentral plugins here//메이븐센트럴 관련 플러그인 검색plugins {javaid("org.springframework.boot") version "3.1.0"id("io.spring.dependency-management") version "1.1.0"}group = "com.example"version = "0.0.1-SNAPSHOT"java.sourceCompatibility = JavaVersion.VERSION_17val queryDslVersion = "5.0.0"configurations {compileOnly {extendsFrom(configurations.annotationProcessor.get())}}repositories {mavenCentral()}dependencies {implementation("org.springframework.boot:spring-boot-starter-thymeleaf")implementation("org.springframework.boot:spring-boot-starter-web")compileOnly("org.projectlombok:lombok")annotationProcessor("org.projectlombok:lombok")testImplementation("org.springframework.boot:spring-boot-starter-test")// jpaimplementation("org.springframework.boot:spring-boot-starter-data-jpa:3.1.2")// this is the plain mybatis -> no spring boot integration (그냥 생짜 마바)// implementation("org.mybatis:mybatis:3.5.13")// myBatis Spring configured (스프링한테 맞게 바꾼 마바)implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2")//jdbctemplate (jdbc템플 -> 나중에 jpa 안에 들어가서 커멘트)// implementation("org.springframework.boot:spring-boot-starter-jdbc")//h2 database integration (h2 DB)runtimeOnly("com.h2database:h2")//테스트에서 lombok 사용testCompileOnly("org.projectlombok:lombok")testAnnotationProcessor("org.projectlombok:lombok")//Querydsl -> 한시간 넘게 걸림. 자주 하는 질문 스프링부트 3.x 이상에서 groovy 확인implementation("com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta") -> 둘 다 자카르타!annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:jakarta") -> 둘 다 자카르타!annotationProcessor("jakarta.annotation:jakarta.annotation-api")annotationProcessor("jakarta.persistence:jakarta.persistence-api")}tasks.withType<Test> {useJUnitPlatform()}//Querydsl 추가, 자동 생성된 Q클래스 gradle clean으로 제거tasks.named<Delete>("clean") {delete(file("src/main/generated"))} 열공!
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
오타 제보
안녕하세요. 영한님오타 제보 합니다. 스프링 트랜잭션 전파1 - 기본15페이지스프링은 어떻게 어떻게 외부 트랜잭션과 ~~ 감사합니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
회사에서는 Spring Data JPA를 사용하지 않고 , Query DSL 만 사용합니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요 영한님.얼마전에 스타트업에 신입 백엔드 개발자로 입사하였고, 작년부터 영한님 강의를 꾸준히 들어오던 수강생 입니다!저희 회사는 데이터 접근 기술로써 Query DSL로 통일되어 있었는데요, 왜 Spring Data JPA를 사용하지 않냐고 대표님께 문의드리자, Query DSL로 모든 기능을 다 수행할 수 있기에,코드의 일관성과 학습 기술을 줄이기 위해서 라고 답변 받았습니다. 그러나 저는 애초에 JPA를 사용하는 근본적인 이유중 하나가 바로 직접 SQL을 작성하지 않기 때문이라고 생각하고 있기 때문에 , Spring Data JPA를 사용하는게 어떻겠냐고 다시 한번 문의드리고 싶은 상황입니다. 영한님 께서 신입으로 입사한 저의 입장이셨다면, 문의를 드렸을지 생각을 여쭙고 싶습니다. 항상 좋은 강의 및 답변 감사합니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
테스트 트랜잭션
테스트 클래스에서 주로 @Transactional 을 클래스 계층에 선언하고 테스트를 전체 롤백시키는 코드를 자주 사용했었는데 권장되지 않는 방식인가여?? 비즈니스 로직의 트랜잭션에 영향을 끼친다고 하셔서 다른 방법이 어떤 게 있을까 궁금해요.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
internalCall() 실행 질문입니다.
@Test void internalCall(){ callService.internal(); } static class CallService{ @Transactional public void internal(){ log.info("call internal"); printTxInfo(); } private void printTxInfo(){ boolean txActive = TransactionSynchronizationManager.isActualTransactionActive(); log.info("tx active={}", txActive); } }강의 pdf에서 트랜잭션 적용 후 실제 callService 객체 인스턴스의 internal() 을 호출한다 라고 되있는데 언제 실제 internal()을 호출하는 지 잘 모르겠습니다.트랜잭션 시작 - printTxInfo()(실제 callService 객체 인스턴스의 internal() 을 호출) - 트랜잭션 종료 라고 생각이 드는데 맞게 생각한건지 알려주시면 감사하겠습니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
트랜잭션 동기화 매니저 작동 원리
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]내부 트랜잭션의 경우, 트랜잭션 동기화 매니저에서 con이 있으면, 생성하지 않고 그걸 가져다가 사용한다고 배웠습니다. 그림에는 단순하게 보여주기 위해 트랜잭션 동기화 매니저에 con이 하나만 있는 경우 인데, 실제로는 여러개가 있을텐데 그중에 아무거나 가져다 쓰나요?? 아니면 내부 트랜잭션을 호출한 외부 트랜잭션의 con을 찾아서 쓰려나요??
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
Invalid bound statement 에러
안녕하세요.MyBatis 적용 예제를 테스트하던 중 아래와 같은 에러가 발생해 질문드립니다. test와 application 모두 에러가 발생합니다. 다음과 같은 방법들을 시도해봤습니다.오타 수정 : 오타 문제인가 싶어 강사님의 자료를 그대로 복사해서 사용했습니다.패키지 구조 확인 : Mapper 파일이 있는 경로와 xml 파일의 경로도 일치하니다.버전 수정 : mybatis의 버전이 안맞나 싶어 2.3.1로 수정도 해봤습니다. 현재 스프링부트 버전은 2.6.5, mybatis 버전은 2.2.0입니다.gradle 재빌드캐시 삭제 제가 놓친 부분이 있는지 확인 부탁드립니다. 감사합니다!
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
RestAssured를 사용한 테스트에서는 Transactional이 동작하지 않는데 어떻게 데이터를 롤백할 수 있을까요?
RestAssured를 사용한 테스트에서는 Transactional이 동작하지 않는데 어떻게 데이터를 롤백할 수 있을까요? 현재 사용하고 있는 방법은 테스트 데이터베이스와 프로덕션 데이터베이스를 확실히 분리하고각 테스트 메서드 실행 이전, 혹은 이후에 'DELETE FROM item'과 같이 테이블의 모든 데이터를 삭제하는 쿼리를 날리는 방식을 사용하고 있습니다. 질문드리고 싶은 것은RestAssured를 사용하는 경우 @Transactional이 잘 작동하지 않는 이유에 대해 찾아봤지만 잘 이해가 되지 않습니다.위에서 제시한 방법 외에 더 좋은 방법은 없을까요..? (참고: 위에 설명드린 방법은 아래 다른 분의 포스팅에 자세히 나와 있더라고요..!)https://mangkyu.tistory.com/264
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
스프링부트 임베디드 모드 테스트 시 커밋
안녕하세요 강사님좋은 강의 감사드리고 해당 질문이 다소 답답하게 느껴지실 수도 있을 것 같아 죄송하지만 강의 실습하는 도중 궁금한 점이 생겨 질문 남깁니다.테스트할 때 임베디드 모드 DB를 사용하게 되면@Commit 어노테이션을 달더라도 실제 db 테이블에 데이터가 저장되지는 않더라고요실제 db가 아닌 임베이드 db를 사용해서 그런 것이라면 임베디드 모드 db를 사용할 때는 애플리케이션이 종료되면 임베디드 모드로 동작하는 H2 데이터베이스도 함께 종료되고, 데이터도 모두 사라진다고 강의 자료에도 써있는데 그러면 @Commit 어노테이션이 무의미하게 되는 것인가요?제가 아래의 동일한 테스트를 여러번 돌릴 때마다 DB데이터가 다 사라지는 것 같더라고요(테스트 할 때는 로그로 데이터 개수를 찍어봤습니다)그럼 각 테스트 단위마다 새로운 임베디드 모드의 DB가 종료되고 다시 새롭게 생성되는 건가요?확인 후 답변 주시면 정말 감사드리겠습니다..!!
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
Item find 관련한 쿼리 메소드 작성에 대한 질문 겸 참고사항입니다!
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]>> 로컬 환경윈도우 10hibernate-core:6.2.5.Finalspring boot:3.0.1아이템 조회하는 부분에서 강의와 똑같이 오류가 발생했었는데요. 지금 버전에서는 findByItemName 혹은 findByItemNameContaining 모두 정상 작동합니다.다른 부분에서 오류가 발생해서 찾아보니까 findItems에 작성된 JPQL에서 like 뒤에 변수 기입할 때 %를 넣지 않아서 그런 거더라고요. 넣으니 테스트 통과합니다 :-) @Query("select i from Item i where i.itemName like %:itemName% and i.price <= :price") List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);저만 겪은 오류인가 싶기도 한데 혹시 몰라서 공유해봅니다. 감사합니다.ps. 제가 겪은 에러는 문법 오류라고 알려주지 않는 것 같네요. 어렵습니다ㅠ12:17:43.066 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [hello.itemservice.domain.ItemRepositoryTest]: ItemRepositoryTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration. 12:17:43.135 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration hello.itemservice.ItemServiceApplication for test class hello.itemservice.domain.ItemRepositoryTest . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.1.1) 2023-07-04T12:17:43.419+09:00 INFO 26524 --- [ main] h.itemservice.domain.ItemRepositoryTest : Starting ItemRepositoryTest using Java 17.0.7 with PID 26524 (started by taetae in D:\source codes\intellij\itemservice-db) 2023-07-04T12:17:43.420+09:00 INFO 26524 --- [ main] h.itemservice.domain.ItemRepositoryTest : The following 1 profile is active: "test" 2023-07-04T12:17:43.813+09:00 INFO 26524 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2023-07-04T12:17:43.858+09:00 INFO 26524 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 37 ms. Found 1 JPA repository interfaces. 2023-07-04T12:17:44.197+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource 2023-07-04T12:17:44.197+09:00 INFO 26524 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2023-07-04T12:17:44.323+09:00 INFO 26524 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:be66698a-0e22-4c39-868d-4400050c76bd user=SA 2023-07-04T12:17:44.324+09:00 INFO 26524 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2023-07-04T12:17:44.336+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource 2023-07-04T12:17:44.337+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from file [D:\source codes\intellij\itemservice-db\out\test\resources\schema.sql] 2023-07-04T12:17:44.340+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : 0 returned as update count for SQL: drop table if exists item CASCADE 2023-07-04T12:17:44.346+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : 0 returned as update count for SQL: create table item ( id bigint generated by default as identity, item_name varchar(10), price integer, quantity integer, primary key (id) ) 2023-07-04T12:17:44.347+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executed SQL script from file [D:\source codes\intellij\itemservice-db\out\test\resources\schema.sql] in 9 ms. 2023-07-04T12:17:44.374+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource 2023-07-04T12:17:44.396+09:00 INFO 26524 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2023-07-04T12:17:44.437+09:00 INFO 26524 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.2.5.Final 2023-07-04T12:17:44.440+09:00 INFO 26524 --- [ main] org.hibernate.cfg.Environment : HHH000406: Using bytecode reflection optimizer 2023-07-04T12:17:44.534+09:00 INFO 26524 --- [ main] o.h.b.i.BytecodeProviderInitiator : HHH000021: Bytecode provider name : bytebuddy 2023-07-04T12:17:44.638+09:00 INFO 26524 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 2023-07-04T12:17:44.813+09:00 INFO 26524 --- [ main] o.h.b.i.BytecodeProviderInitiator : HHH000021: Bytecode provider name : bytebuddy 2023-07-04T12:17:45.187+09:00 INFO 26524 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 2023-07-04T12:17:45.197+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : drop table if exists item cascade 2023-07-04T12:17:45.200+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : create table item (price integer, quantity integer, id bigint generated by default as identity, item_name varchar(10), primary key (id)) 2023-07-04T12:17:45.203+09:00 INFO 26524 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2023-07-04T12:17:45.369+09:00 WARN 26524 --- [ main] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: hello.itemservice.repository.jpa.SpringDataJpaItemRepository 2023-07-04T12:17:45.426+09:00 INFO 26524 --- [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used. 2023-07-04T12:17:45.747+09:00 WARN 26524 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2023-07-04T12:17:45.875+09:00 INFO 26524 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html] 2023-07-04T12:17:46.137+09:00 INFO 26524 --- [ main] h.itemservice.domain.ItemRepositoryTest : Started ItemRepositoryTest in 2.883 seconds (process running for 3.558) OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended 2023-07-04T12:17:46.518+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : insert into item (item_name,price,quantity,id) values (?,?,?,default) 2023-07-04T12:17:46.543+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : insert into item (item_name,price,quantity,id) values (?,?,?,default) 2023-07-04T12:17:46.544+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : insert into item (item_name,price,quantity,id) values (?,?,?,default) 2023-07-04T12:17:46.594+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 2023-07-04T12:17:46.633+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 2023-07-04T12:17:46.645+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.item_name like ? escape '\' 2023-07-04T12:17:46.648+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.item_name like ? escape '\' 2023-07-04T12:17:46.649+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.item_name like ? escape '\' 2023-07-04T12:17:46.651+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.price<=? 2023-07-04T12:17:46.656+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.item_name like ? escape '' and i1_0.price<=? org.opentest4j.AssertionFailedError: Expecting actual: [] to contain exactly (and in same order): [Item(id=1, itemName=itemA-1, price=10000, quantity=10)] but could not find the following elements: [Item(id=1, itemName=itemA-1, price=10000, quantity=10)] Expected :[Item(id=1, itemName=itemA-1, price=10000, quantity=10)] Actual :[] <Click to see difference> at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) at hello.itemservice.domain.ItemRepositoryTest.test(ItemRepositoryTest.java:107) at hello.itemservice.domain.ItemRepositoryTest.findItems(ItemRepositoryTest.java:102) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55) 2023-07-04T12:17:46.678+09:00 INFO 26524 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2023-07-04T12:17:46.678+09:00 DEBUG 26524 --- [ionShutdownHook] org.hibernate.SQL : drop table if exists item cascade 2023-07-04T12:17:46.680+09:00 INFO 26524 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2023-07-04T12:17:46.681+09:00 INFO 26524 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. Process finished with exit code -1
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
트랜잭션 사용 전 후 / 수동 오토 커밋
1. 이전 강의에서는 트랜잭션을 사용하지 않아 오토커밋이 되었고, 이번 강의에서는 트랜잭션을 사용해서 수동커밋이 된것으로 이해했는데 맞을까요?2. 수동커밋이 맞다면, 수동 커밋이 되어서 롤백을 할 수 있는 건가요?3. 이전 강의 까지는 트랜잭션을 사용하지 않고 DB 커넥션만 사용했다고 이해하면 되는 걸까요?4. 제가 테스트 해보고 싶었던 것은 '스프링 DB 1편'에 있는 '트랜잭션 - DB 예제3 - 트랜잭션 실습'처럼 수동 커밋의 경우 한 세션에서는 데이터가 들어간것이 보이지만 다른 한 세션에서는 안 보이는 것을 테스트 해보고 싶었습니다.그래서 랜잭션 롤백 부분 코드( transactionManager.rollback(status); )를 주석처리 하였고, H2 DB를 2개 띄웠습니다. 이 상태에서 save(), updateItem(), findItems()를 각각 실행해서 확인해 보면 두 세션 모두 데이터가 보이는 것을 확인할 수 있었습니다.커밋을 안했고 롤백도 안했는데 둘다 데이터가 보이는 걸까요?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
구현 클래스 없어도 CRUD의 기능을 사용할수 있디는 말의 의미
강의 에서 JpaRepository 인터페이스만 상속받으면 스프링 데이터 JPA가 프록시 기술을 사용해서 구현 클래스를 만들어준다. 그리고 만든 구현 클래스의 인스턴스를 만들어서 스프링 빈으로 등록한다. 따라서 개발자는 구현 클래스 없이 인터페이스만 만들면 기본 CRUD 기능을 사용할 수 있다.이 글의 뜻은 public class JpaItemRepositoryV2 implements ItemRepository { private final SpringDataJpaItemRepository repository; @Override public Optional<Item> findById(Long id) { return repository.findById(id);}}위 코드에서 findById의 구현 클래스가 없어도 인터페이스(SpringDataJpaItemRepository )만 만들면 기본 CRUD 기능을 사용할 수 있다. 라고 이해하면 될까요??
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
트랜잭션
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]안녕하세요. 강의 들으면서 혼자 이것저것 만들어보면서 이해가 안가는부분이있어서 관련 강의에 질문남깁니다.스프링 aop를 사용해서 로깅 기능을 만들어보고있는중에 막히는 부분이있는데. joinpoint가 실행되고 정상 흐름일때,예외 상황일때 로그 저장 기능을 만들고있습니다. 코드를 알려드리면@Around(pointcut) public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable { try { result = joinPoint.proceed(); // @Transactional } catch { logService.saveLog(args); // @Transactional throw e; } logService.saveLog(args); return result; }간략히 이런식 구성돼있습니다.joinpoint가 실행되는 매서드에는 @Transactional이 붙어있어 트랜잭션이 실행되고 logService.saveLog에도 @Transactional이 붙어있어 트랜잭션이 실행됩니다.여기서 문제가 제가 이해하기론 트랜잭션안에서 트랜잭션이 실행될때 내부 트랜잭션, 외부 트랜잭션으로 나뉘고 이것들을 통합하는 하나의 물리트랜잭션으로 된다고 이해했는데, 위 코드의 상황에는 joinpoint.proceed에서 생성된 트랜잭션 안에서 또 다른 트랜잭션이 생성된게 아닌 joinpoint.proceed가 완전히 수행된후 logService.saveLog로 새로운 트랜잭션이 시작된거같은데 이때도 joinpoint.proceed에서 예외가 발생하면 logService.saveLog도 커밋이 되지 않더라구요. logService.saveLog의 @Transactional의 속성을 Requires_new로 하면 예외 상황에서도 잘 저장이되구요.내부 트랜잭션 외부 트랜잭션의 구분이 하나의 @Transactional과 같은 트랜잭션 안에서 또다른 트랜잭션이 생성될때만 구분되는게아니라 사용자 요청이 들어오고 응답이 나가기 전까지의 모든 트랜잭션이 연관되는건지 궁금합니다.아니면 애초에 제가 잘못 하고있는게 있는걸까요..?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
패키지를 분류하는 기준
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.안녕하세요!패키지를 분류하는 기준은 Entity별로 패키지를 분류할 수 있고 or 강의에서 설명해주신 것 처럼 기능별로 패키지를 분류할 수 있을것 같습니다.Entity별로 패키지를 분류한다면 -> member라는 패키지 안에 Member 엔티티 , MemberRepository , MemberService, MemberController 그리고 각종 MemberDto들을 저는 위치시켰습니다. 그리고 기능별로 패키지를 분류한다면 -> controller라는 패키지에 , 각 controller들을 모아놓았습니다. 이 두가지 방식이 있고 , 각 방식에 대한 장단점을 chatGPT에게 물어보았었는데요, 대규모 프로젝트에서는 entity 수가 많아지기 때문에 -> 기능별로 패키지를 분류하는 방법을 더 권장해주었습니다.혹시 이러한 이유로 영한님께서도 기능별로 패키지를 분류하는 방식으로 강의를 진행하시는 건지 여쭤보고 싶습니다.