묻고 답해요
169만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨실전! 스프링 데이터 JPA
스프링 부트3와 카운트 쿼리 분리
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요.카운트 쿼리 분리 부분을 듣는 도중에 카운트 쿼리에 LEFT JOIN이 사용되는 결과를 확인하기 위해 다음처럼 코드를 작성하였습니다.public interface MemberRepository extends JpaRepository<Member, Long> { @Query(value = "select m from Member m left join m.team t") Page<Member> findByAge(int age, Pageable pageable); ... } @Test public void paging() throws Exception { //given memberRepository.save(new Member("member1", 10)); memberRepository.save(new Member("member2", 10)); memberRepository.save(new Member("member3", 10)); memberRepository.save(new Member("member4", 10)); memberRepository.save(new Member("member5", 10)); //when PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.ASC, "username")); Page<Member> page = memberRepository.findByAge(10, pageRequest); //then List<Member> content = page.getContent(); assertThat(content.size()).isEqualTo(3); assertThat(page.getTotalElements()).isEqualTo(5); assertThat(page.getNumber()).isEqualTo(0); assertThat(page.getTotalPages()).isEqualTo(2); assertThat(page.isFirst()).isTrue(); assertThat(page.hasNext()).isTrue(); }그런데 결과는 강의와는 달리 카운트 쿼리에 LEFT JOIN 이 사용되지 않았고, 심지어 JPQL select m from Member m left join m.team t 을 @Query에 작성했음에도 조회 쿼리도 LEFT JOIN을 사용하지 않은 것을 확인했습니다.이후 스프링 부트 3 이하 버전에서 동일한 코드로 실행해보니 강의와 동일한 결과를 얻을 수 있었습니다.스프링 부트 3 부터 적용된 스프링 데이터 JPA 에서는 이러한 부분의 최적화를 자동으로 제공하는 것일까요?
-
해결됨Practical Testing: 실용적인 테스트 가이드
빌더 사용에 대해 질문드립니다!
안녕하세요 선생님. 빌더를 사용하시는 것을 보고 흥미가 동해 질문남깁니다!선생님께서는 예제의 경우 대부분 생성자를 private으로 막아두고 빌더를 통해서만 객체를 생성하시는 것 같습니다.저는 필드 개수가 많아도 public 생성자로 열어두는 편인데, 그 이유는어떤 인자를 전달해야할지 인텔리제이의 힌트로 알아채기도 편하고필드가 null 일 수도 있는 경우 객체를 생성할 때 명시적으로 null 이 보이는게 낫다 라고 생각해서입니다. null을 명시적으로 인자로 전달하는 것이 불편하다면 텔레스코핑을 통해 생성자를 조금 더 만들어두기도 합니다 :)물론 빌더 패턴을 사용하면 이런 코드들이 전부(?) 사라지기는 하지만, 이 외에 빌더로 객체 생성을 강제하는 것에 대한 장점이 있는지 궁금합니다! 또, 선생님만의 빌더랑 생성자 선택 기준이 따로 있을까요? 마지막으로 실무에서도 자주 사용하시는지 궁금합니다. 좋은 강의 감사드립니다 :)
-
해결됨스프링 DB 1편 - 데이터 접근 핵심 원리
비관적 락 질문드립니다.
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]JPA 책 16장 확인하고 질문이 생겨서 글 남깁니다.강의에서 'for update'를 사용하는걸 보면 비관적 락인 'PESSIMISTIC_WRITE'방식인것같아요.그런데 이게 'PESSIMISTIC_READ'랑 어떤 차이가 있는지 잘 모르겠어요..검색해보니까 write가 읽기도 막는다고 적혀있는곳이 종종 보이는데,강의에서는 for update하고 다른 트랜잭션에서 읽기가 정상적으로 된것같아서요..제가 스프링에서 두 방식 설정해서 각각 실행시킨 다음에 mysql workbench에서 select문 실행시켰을때도 모두 문제 없이 읽혔어요어떤 차이가 있는건가요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
@ScriptAssert 스프링 3.0.1 이상 / jdk 17 버전 이상 실행 불가 임시방편
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 내쉬혼 자바스크립트 런타임이 이제 jdk 17에서부터 deprecated 되면서 더이상 지원을 하지 않게 됬지만그래들로 임시방편이더라도build.gradle에 implementation ("org.openjdk.nashorn:nashorn-core:15.3") 로 강제로 연명은 아직 가능합니다..근데 내쉬혼이 현 자바스크립트 런타임이랑은 좀 머니최신 자바스크립트 런타임 가진 graalVM 을 임포트해 와서 CustomScriptAssertValidator 클래스를 만들어 @Bean 등록을 하고 싶은데 어렵네요..
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
spring 2.x - Querydsl 이슈있는분들 참고하세요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]plugins { id 'org.springframework.boot' version '2.6.5' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"}group = 'com.example'version = '0.0.1-SNAPSHOT'sourceCompatibility = '11'ext["hibernate.version"] = "5.6.5.Final"configurations { compileOnly { extendsFrom annotationProcessor}}repositories { mavenCentral()}dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'javax.validation:validation-api' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5' //JdbcTemplate 추가 //implementation 'org.springframework.boot:spring-boot-starter-jdbc' //MyBatis 추가 implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0' //JPA, 스프링 데이터 JPA 추가 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' //Querydsl 추가 implementation 'com.querydsl:querydsl-jpa' annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" //H2 데이터베이스 추가 runtimeOnly 'com.h2database:h2' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' //테스트에서 lombok 사용 testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok'}tasks.named('test') { useJUnitPlatform()}//querydsl 추가//def querydslDir = '$buildDir/generated'def querydslDir = "src/main/generated"//clean {// delete file(generated)//}querydsl { library = "com.querydsl:querydsl-apt" jpa = true querydslSourcesDir = querydslDir}sourceSets { main { java { srcDirs = ['src/main/java', querydslDir]} }}compileQuerydsl{ options.annotationProcessorPath = configurations.querydsl}configurations { querydsl.extendsFrom compileClasspath} 강사님이 올려주신 내용 + 구글링 + 직접 수정했습니다. 간혹, 빌드는 되는데 generated 파일에 QClass 안보이시는 분들은 queryDsl 저장되는 경로에 $buildDir 대신 'src/main/'으로 해보세요
-
해결됨3. 웹개발 코스 [스프링 프레임워크+전자정부 표준프레임워크]
무료쿠폰 발급 요청방법
모든 (기존,신규)수강생에 한해 https://www.inflearn.com/course/%EC%9B%B9%EA%B0%9C%EB%B0%9C-%EC%BD%94%EC%8A%A4-ea-%EC%A0%84%EC%9E%90%EC%A0%95%EB%B6%80-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC무료쿠폰드리고 있습니다.현재 강의보다 개선된 강의이며 자막을 제공하고 있습니다.자신의 인프런 닉네임을 적어서 쿠폰요청으로 메일 주시면 감사하겠습니다.vmproductor@gmail.com
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
JPA 특징 중 자동 업데이트 관련
주문 서비스 개발 부분 강의를 듣다가 궁금한 점이 있어 질문드립니다. 주문 취소(OrderService.cancelOrder) 부분에서order.cancel() 만 작성하면 JPA 특징으로 인해, 데이터 업데이트 관련 쿼리 로직 없이 자동으로 업데이트 된다고 하셨는데요. 주문 생성(OrderService.order)의 EntityManager 가 사용된 orderRepository.save(order); 와 혼동이 와서 질문 남깁니다. 주문 취소 order.cancel() 메서드를 타면서 엔티티의 값(status, count 등) 변경 내역이 자동으로 업데이트 되는 것이라고 하면, 주문 생성도 Order.createOrder(member, delivery, orderItem); 를 타면서 엔티티의 값들을 설정하기 때문에 orderRepository.save(order); 를 사용하지 않아도 되는게 아닌가 라는 생각이 듭니다. 제가 JPA 를 잘 몰라서 혼란이 오는 것 같은데.. 쿼리는 직접 작성하지 않아도 업데이트/저장/삭제 등 DB 조작이 일어나려면 EntityManager 를 통해야 된다고 생각하고 있었습니다. 주문 취소는 EntityManager 없이 업데이트 되고, 주문 생성은 EntityManager의 persist() 를 사용해서 저장하는 것 이 2가지 차이점에 대해서 설명 부탁드립니다! 감사합니다 :)
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
getOriginalFilename null check
public String convertedFile(@RequestParam MultipartFile attachFile) throws IOException { if (attachFile.isEmpty()) { return null; } String originalFilename = attachFile.getOriginalFilename(); String ext = extractExt(originalFilename); }위처럼 MultipartFile를 isEmpty로 체크하고 getOriginalFilename로 originalFilename을 가져온다음 originalFilename을 extractExt 함수의 argument로 넣어려고하는데 null을 check하라고 나옵니다. 정확히는 아래처럼 null이 나올 수 있다고 뜨네요.isEmpty로 위에서 한 번 체크하면 안뜨는 걸로 알고 있는데 뜨는 이유가 뭘까요
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
국제화 null 기준
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 질문이기보다는 발견한거 찾아낸거라..혹시 컴퓨터 기본 언어가 영어로 세팅되신 분들이라면해당 테스트는 무조건 실패하는거 같습니다locale 이 null 이 나게 되면그리고 이 getDefault() 를 파고들면initDefault가 나오고 initDefault를 보면 message.properties 로 가는것 없이 바로 props = privilegedGetProperties로 가는데 이 privilegedGetProperties 는 System.getProperties() 를 실행...애초에 locale = null 일때에는 시스템에서 가져와버리게 설계가 된 거 같습니다...java - Spring MessageSource seems to ignore property fallbackToSystemLocale - Stack Overflow해당 stackoverflow 를 보더라도 locale = null 일때spring.messages.fallback-to-system-locale=false쓰이는게 아니라고 되있기도 합니다.. 즉 저 fallback-to -system locale 은 locale = null 을 제외한 상황에서 Locale 에 해당하는 파일을 못 찾을 시.. 가 맞는거 같네요.. 아마 null일 때에는spring.messages.use-code-as-default-message를 변경해야 할 거 같습니다.... 시스템 언어 영어이신 분들스프링 정규 docs : Common Application Properties (spring.io) ctrl + f 로 검색저도 그냥 디버거 따라가다가 찾은거라 개인적인 의견을 말씀을 드리는 거라 누가 대신 확인을 좀 해주셨으면.....
-
해결됨스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
Model은 무엇을 가리키는 건가요?
Controller는 서블릿View는 템플릿 엔진Model은 Member 같은 객체를 의미한다고 생각했는데HttpServletRequest의 Attribute가 모델이라고 하셔서 질문 드립니다. Model은 무엇을 가리키는 것이고Member가 Model이 아니라면 무엇인가요?
-
해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
19:04 초 where in 쿼리 대신 where array_conatins 쿼리가 나왔습니다. (스프링 부트 3.1)
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]현재 스프링 부트 3.1 로 강의를 따라가고 있습니다.default_batch_fetch_size:10 을 설정해서 나가는 쿼리가 줄어드는 것을 확인했습니다만, 쿼리가 두 개가 아니고 총 세 개가 나왔습니다.023-06-13T23:24:02.666+09:00 DEBUG 68750 --- [nio-8080-exec-5] org.hibernate.SQL : select o1_0.order_id, d1_0.delivery_id, d1_0.city, d1_0.street, d1_0.zipcode, d1_0.status, m1_0.memeber_id, m1_0.city, m1_0.street, m1_0.zipcode, m1_0.name, o1_0.order_date, o1_0.status from orders o1_0 join member m1_0 on m1_0.memeber_id=o1_0.member_id join delivery d1_0 on d1_0.delivery_id=o1_0.delivery_id offset ? rows fetch first ? rows only 2023-06-13T23:24:02.676+09:00 INFO 68750 --- [nio-8080-exec-5] p6spy : #1686666242676 | took 2ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/springbootjpa select o1_0.order_id,d1_0.delivery_id,d1_0.city,d1_0.street,d1_0.zipcode,d1_0.status,m1_0.memeber_id,m1_0.city,m1_0.street,m1_0.zipcode,m1_0.name,o1_0.order_date,o1_0.status from orders o1_0 join member m1_0 on m1_0.memeber_id=o1_0.member_id join delivery d1_0 on d1_0.delivery_id=o1_0.delivery_id offset ? rows fetch first ? rows only select o1_0.order_id,d1_0.delivery_id,d1_0.city,d1_0.street,d1_0.zipcode,d1_0.status,m1_0.memeber_id,m1_0.city,m1_0.street,m1_0.zipcode,m1_0.name,o1_0.order_date,o1_0.status from orders o1_0 join member m1_0 on m1_0.memeber_id=o1_0.member_id join delivery d1_0 on d1_0.delivery_id=o1_0.delivery_id offset 0 rows fetch first 100 rows only; 2023-06-13T23:24:02.681+09:00 DEBUG 68750 --- [nio-8080-exec-5] org.hibernate.SQL : select o1_0.order_id, o1_0.order_item_id, o1_0.count, o1_0.item_id, o1_0.order_price from order_item o1_0 where array_contains(?,o1_0.order_id) 2023-06-13T23:24:02.689+09:00 INFO 68750 --- [nio-8080-exec-5] p6spy : #1686666242689 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/springbootjpa select o1_0.order_id,o1_0.order_item_id,o1_0.count,o1_0.item_id,o1_0.order_price from order_item o1_0 where array_contains(?,o1_0.order_id) select o1_0.order_id,o1_0.order_item_id,o1_0.count,o1_0.item_id,o1_0.order_price from order_item o1_0 where array_contains('ar2: ARRAY [CAST(1 AS BIGINT), CAST(2 AS BIGINT), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL]',o1_0.order_id); 2023-06-13T23:24:02.690+09:00 DEBUG 68750 --- [nio-8080-exec-5] org.hibernate.SQL : select i1_0.item_id, i1_0.dtype, i1_0.category_item_id, i1_0.name, i1_0.price, i1_0.stock_quantity, i1_0.author, i1_0.isbn, i1_0.artist, i1_0.etc from item i1_0 where array_contains(?,i1_0.item_id) 2023-06-13T23:24:02.691+09:00 INFO 68750 --- [nio-8080-exec-5] p6spy : #1686666242691 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/springbootjpa select i1_0.item_id,i1_0.dtype,i1_0.category_item_id,i1_0.name,i1_0.price,i1_0.stock_quantity,i1_0.author,i1_0.isbn,i1_0.artist,i1_0.etc from item i1_0 where array_contains(?,i1_0.item_id) select i1_0.item_id,i1_0.dtype,i1_0.category_item_id,i1_0.name,i1_0.price,i1_0.stock_quantity,i1_0.author,i1_0.isbn,i1_0.artist,i1_0.etc from item i1_0 where array_contains('ar3: ARRAY [CAST(1 AS BIGINT), CAST(2 AS BIGINT), CAST(3 AS BIGINT), CAST(4 AS BIGINT), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL]',i1_0.item_id); 수강 중에 제가 따라 작성한 코드를 스프링부트 2.4.1에 그대로 옮겨보니 강의 내용대로 where in 쿼리가 나오는 것을 확인했습니다.스프링부트 버전 차이에서 나오는 쿼리가 달라진 것 같은데, 제 생각이 맞을까요? 답변 확인 : 빠르게 답변해주셔서 감사합니다!!덕분에 array_contains 로 최적화된 이유를 쉽게 이해할 수 있었습니다. (Hibernate에서 최적화를 했었었군요!)아 그리고 강의 내용에서 쿼리 세 개 나온다는 것을 확인했습니다(잘못 본 것에 대해 질문글에서 수정을 해놨어야 했는데 빼놓지 않았었네요 ㅠㅠ)다시 한번 상세한 답변 정말 감사드립니다.
-
해결됨스프링 DB 2편 - 데이터 접근 활용 기술
jdbc 탬플릿으로 findById로 조회 시 정상적으로 반환은 되지만 AUTO_INCREMENT 한 컬럼이 null인 상황입니다.
안녕하세요. 질문 제목에 관해 말씀드리겠습니다.상황은 이러합니다.member table에 jdbc 탬플릿으로 잘 저장하고 잘 조회하나 테스트하는 중으로 테이블은 아래와 같습니다.CREATE TABLE member ( member_id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(45), login_id VARCHAR(45), password VARCHAR(45), email VARCHAR(45) UNIQUE ); 레포지토리는 아래와 같습니다.package com.boaz.sketch2fashion.repository.member; import com.boaz.sketch2fashion.domain.Member; import com.boaz.sketch2fashion.repository.member.MemberRepository; import com.boaz.sketch2fashion.repository.member.dto.MemberSaveDTO; import com.boaz.sketch2fashion.repository.member.dto.MemberUpdateDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; import javax.sql.DataSource; import java.security.Signature; import java.util.List; import java.util.Map; @Slf4j @Repository public class JdbcMemberRepository implements MemberRepository { private final NamedParameterJdbcTemplate template; public JdbcMemberRepository(DataSource dataSource) { this.template = new NamedParameterJdbcTemplate(dataSource); } @Override public Member save(Member member) { String sql = "insert into member(name, login_id, password, email) values(:name, :loginId, :password, :email)"; KeyHolder keyHolder = new GeneratedKeyHolder(); SqlParameterSource param = new BeanPropertySqlParameterSource(member); template.update(sql, param, keyHolder); long key = keyHolder.getKey().longValue(); member.setId(key); return member; } @Override public Member findById(Long id) { String sql = "select * from member where member_id = :id"; Map<String, Object> param = Map.of("id", id); Member member = template.queryForObject(sql, param, memberRowMapper()); return member; } @Override public Member findByLoginId(String loginId) { String sql = "select * from member where login_id = :loginId"; Map<String, Object> param = Map.of("loginId", loginId); Member member = template.queryForObject(sql, param, memberRowMapper()); return member; } @Override public void update(Long id, MemberUpdateDTO memberUpdateDTO) { String sql = "update member set name = :name, login_id = :loginId, password = :password, email = :email where id = :id"; SqlParameterSource param = new MapSqlParameterSource() .addValue("name", memberUpdateDTO.getName()) .addValue("loginId", memberUpdateDTO.getLoginId()) .addValue("password", memberUpdateDTO.getPassword()) .addValue("email", memberUpdateDTO.getEmail()); template.update(sql, param); } @Override public void delete(Long id) { String sql = "delete from member where id = :id"; Map<String, Object> param = Map.of("id", id); template.update(sql, param); } private RowMapper<Member> memberRowMapper() { return BeanPropertyRowMapper.newInstance(Member.class); } } 테스트 코드는 아래와 같습니다. @Test void findById() { // given Member memberA = new Member("a", "a", "a", "a"); // when Member saveMember = memberRepository.save(memberA); log.info("{}", saveMember.getId()); Member findMember = memberRepository.findById(saveMember.getId()); log.info("{}", saveMember.getId()); log.info("{}", findMember.getId()); // then assertThat(saveMember).isEqualTo(findMember); } memberA를 저장하고 findById로 찾아서 saveMember와 findMember의 내용물이 같은지 확인하는 코드입니다. memberA를 저장하면 pk는 1이 될 것이라고 생각하여 findById(saveMember.getId());로 H2 DB에서 pk가 1인 행을 찾아 findMember로 반환하여 isEqualTo를 하려고 했습니다. 결과 이미지saveMember의 getId 1로 findMember를 찾았는데 findMember의 getId는 null입니다.따라서 테스트가 실패합니다. 테스트 환경H2 스프링 내장 DB, jdk 17 항상 질문 답변 해주셔서 정말 감사합니다.
-
미해결스프링 핵심 원리 - 기본편
초기화, 소멸 인터페이스의 단점에서 질문
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]초기화, 소멸 인터페이스의 단점부분에서 아래의 내용이 언급 되었습니다. 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다클래스파일로 컴파일되어있는 수정이 불가한 라이브러리를 받았다고 가정해보자. 이 라이브러리의 초기화와 종료 메서드를 무조건 호출 해주어야 한다. 스프링 빈으로 이미 등록 했는데 우리는 코드를 고칠 방법이 없다. 위에 주어진 예시가 잘 이해가 안가서 좀 더풀어서 설명 가능하실까요?
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
데이타소스없이 JpaRepository 작동하는 이유
안녕하세요.문득 스프링 데이타 JPA로 테스트 진행 중 없는 테이블을 만들어주는 기능을 보니 db에 커넥션이 없이 이 모든 게 진행된다는 게 갑자기 낯선 느낌이 들었습니다.쿼리를 만들고 날리는 과정이 커넥션 여부와 관계없이 실행되는 것인가요? 어떻게 데이타소스 없이 작동하는건지 조금 이상하다는 생각이 듭니다.트랜잭션과 관련있는 내용은 아니지만 강의에서 설명을 들으니 문득 궁금해져 질문 드립니다.감사합니다.
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
build.gradle 빌드 실패 에러(JDK 17.0.6, Spring boot 3.0.7)
안녕하세요, build.gradle의 빌드 실패가 나서 문의드립니다. JDK 버전은 다음과 같이 17.0.6입니다.그리고 인텔리제이 설정은 다음과 같습니다.java 컴파일러 설정2. 빌드 도구 설정3. 프로젝트 설정마지막으로 처음 프로젝트를 다운로드 받았던 설정은 다음과 같습니다.(spring boot 3.0.7 버전 사용)처음 build.gradle을 실행할 때부터 에러 로그 하나도 없이 그냥 빌드 실패했다는 에러가 뜹니다.컴퓨터를 여러번 껐다가 켜봐도 여전히 같은 에러가 뜹니다. 인텔리제이와 스프링이 완전 처음이라서 이 에러를 어떻게 해결해야 하는지 모르겠습니다. 커뮤니티의 다른 글들을 참고해봐도 딱히 도움이 되지는 않는 것 같습니다. 도와주시면 너무 감사하겠습니다.
-
해결됨실전! 스프링 데이터 JPA
DDD 적용시 JPA 설계에 대해 궁금한 사항
안녕하십니까 강사님. 좋은 강의 정말 감사합니다.이번에 강사님께 강의를 듣고 현업에 적용하는 도중 궁금한 사항이 있어 문의하게 되었습니다.저희는 현재 DDD 를 적용하려고 합니다. DDD 에서는 애그리거트끼리의 관계를 맵핑하는 것을 지양하는 것으로 알고 있습니다. 따라서 저희는 ERD 기준으로 도메인을 설계해보니 실제 나온 도메인을 보면 테이블이 전부 도메인 클래스가 되었고 서로간의 관계를 코딩으로 맺는 부분 (@ManyToOne 등)이 존재 하지 않습니다.현업에서도 ERD 를 기준으로 도메인 클래스를 나누면 테이블 당 하나의 애그리거트가 나오는 것이 보편적인 사항일까요?DDD 로 개발하다보면 도메인 클래스에 관계를 맺는 부분이 없는게 보편적일까요?
-
해결됨Java/Spring 주니어 개발자를 위한 오답노트
IoC != DI에 대한 질문입니다.
안녕하세요,선생님 습하고 더운 여름 몸 건강히 계시길 바랍니다.강의를 듣는 도중 궁금한 부분이 생겼습니다.DIP와 IoC가 다르다고 말씀하신 부분이,DIP가 의존성 주입을 역전함으로써 제어의 흐름을 바꾸지만 IoC가 의존성을 주입해주는 것은 아니어서 그렇다고 이해되는데 제대로 이해한 게 맞을까요? ㅜㅜ 또, Open-Closed 법칙을 설명해주시는 1:32초 부분에 수정에는 열려있어야 한다는 말씀을 하셨는데수정 시 많은 메소드 혹은 클래스를 수정해야 하는 경우가 수정에 열려있는 것이 아닌지요 ㅜㅜ...제가 잘 모르다보니 헷갈려서 질문드립니다!
-
해결됨실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)
예외 처리 리팩토링 질문
안녕하세요.강의 들으며 재밌고 공부하고 있습니다. 감사합니다.예외 처리를 util패지키에 모아서 처리하는 것은 이해했습니다. 약간 궁금증 생겨서 문의 남겨봅니다!BookService의 코드 일부분 입니다. 여기서 보면 UserRepository와 BookRepository의 findByName에서 모두 fail()로 예외 처리를 하는데요, 이걸 그냥 리포지토리 계층의 메서드 자체에서 예외를 처리해주면 더 깔끔하지 않을까 하는 궁금증이 생겼습니다. 제가 지금까지 공부하면서는 리포지토리 계층에서 디폴트 메서드를 통해서 처리하는 코드도 봤고, 서비스 계층에서 처리하는 코드도 봐서 어떤 방식을 선호하시는지, 권장 하시는지 궁금합니다! 좋은 강의 감사합니다!
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
[h2 DB] test.mv.db 파일 생성 에러 해결법 공유
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용][H2 데이터베이스 설정] 강의의 2분30초~3분00초 강사님 매뉴얼대로 따라하면h2 DB 기본 생성에 에러가 발생하여 고생했습니다..콘솔에서 /~test로 먼저 연결해도 test.mv.db가 만들어지지 않았습니다.버전이 2.1.xxx대로 가면서 콘솔에선 db 자동 생성이 안 되는 이슈이므로, 수동 생성을 진행해야 했습니다. 제가 2시간 걸려서 해결한 과정 정리해봤습니다.링크 올립니다.https://blog.naver.com/jtcjtc/223117129981
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
인텔리제이 같이 설치하는 과정부터 좀 알려주시면 안될까요 너무 답답하고 짜증나죽겠습니다
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)몰라요3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)네[질문 내용]여기에 질문 내용을 남겨주세요.처음하는 초심자 입장에서 너무 어렵습니다 일단 설치부터 jdk까지는 꾸역꾸역 구글링해서 깔고했는데 인텔리제이를 설치하고 설치하는 과정에서 어떤 체크사항을 체크해야하고 어디에 저장하고 하는지 하나도 모르고 그냥 "인텔리제이 설치하시오", "자바 jdk 설치하시오" 미리 준비물로 딸랑 해놓고 지금 혼자 깔고 인텔리제이 실행해서 강사님이랑 똑같이 하려는데 처음 run하는것부터 run이 보이지도 않고 막히네요 무료강의도 아니고 돈내고 듣는데 너무 짜증나고 답답합니다 이 질문도 한참있다가 질문답변이 달릴테고... 극초심자를위한 설치부터 하나하나 같이 알려주고 하는방법좀 설명해주셨으면 좋겠습니다 시간아깝고 짜증납니다 정말