묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
isEqualTo를 isSameAs로 바꿔야하는것이 아닌가요??
@RunWith(SpringRunner.class) @SpringBootTest public class MemberRepositoryTest { @Autowired MemberRepository memberRepository; @Test @Transactional @Rollback(false) public void testMember() throws Exception { //given Member member = new Member(); member.setUsername("memberA"); //when Long saveId = memberRepository.save(member); Member findMember = memberRepository.find(saveId); //then assertThat(findMember.getId()).isEqualTo(member.getId()); assertThat(findMember.getUsername()).isEqualTo(member.getUsername()); assertThat(member).isEqualTo(findMember); System.out.println("findMember == member: " + (findMember == member)); } }위 코드에서 디비에 넣은 멤버와 찾은 멤버가 같은지를 비교를 isEqualTo를 사용을 하셨는데 이것은 제가 알기론 value부분만 같다면 true라는것으로 알고 있습니다. 같은 영속성 컨텍스트 안에서의 객체가 같은지 확인하려면 isSameAs가 맞지 않은지 의문이 생겼습니다!
-
해결됨JPA & Spring Data JPA 기초
도메인과 JPA 엔티티
도메인과 JPA 엔티티를 구분하려고 코드를 작성해봤습니다.@Repository@RequiredArgsConstructorpublic class OrderRepositoryImpl implements OrderRepository {private final OrderJpaRepository orderJpaRepository;@Override public void save(final OrderJpaEntity entity) {orderJpaRepository.save(entity);}@Overridepublic Optional<OrderJpaEntity> findById(final long orderId) {return orderJpaRepository.findById(orderId)}}이러한 방식으로 JpaRepository를 주입받고 OrderRepository를 구현하는 방식입니다.public interface OrderRepository {void save(final OrderJpaEntity entity);Optional<OrderJpaEntity> findById(final long orderId);} 그런데 이때 변경감지를 사용하려면 Order가 아닌 OrderJpaEntity를 반환해야하는데 이렇게 하면 결국 Repository에도 JPA 엔티티에 의존하기 때문에 분리를 잘 하지 못했다는 생각이 듭니다..혹시 도메인과 JPA 엔티티를 분리한다면 어떠한 방식으로 하시나요?
-
미해결Practical Testing: 실용적인 테스트 가이드
controller, service용 dto를 분리시키는 것에 대한 질문
항상 잘 듣고 있습니다. 감사합니다. controller layer와 service layer의 dto를 서로 분리시켜서 service layer가 상위 레이어를 모르도록 한다는 것은 이해가 되었습니다.질문 드리겠습니다! dto를 분리할 때, 중복된 코드가 복잡성을 증가시킬 수도 있고 운영 시, 두 dto 간의 변환 과정의 비용이 어느 정도 성능에 영향을 미칠 수 있다고도 생각합니다.2개로 분리하는 방법은 일반적인 설계 패턴은 아니라고 생각되는데, 혹시 현업에서도 자주 사용하는 방식이신지가 궁금합니다. service에서 생성된 response 데이터에 대해서도 controller만의 response dto를 따로 생성할 필요가 있는지 궁금합니다. 클라이언트로 내려주는 응답 객체로 controller 클래스에서 ResponseEntity는 사용하지 않으시는지,주로 현업에서도 강의에서처럼 응답 객체(ApiReponse)를 커스텀해서 내려주는 방식을 선호하시는지 궁금합니다.
-
미해결더 자바, 코드를 조작하는 다양한 방법
gradle 사용하시는 분 도움
일단 새로운 프로젝트 만들고 premain 추가해주는 것 까진 강의를 그대로 따라하시면 됩니다. manifest plugin 부터 조금 차이가 있어서 거기부터 설명하면, build gradle 에 다음과 같이 추가한다.tasks.named('jar') { manifest { attributes( 'Implementation-Title': project.name, 'Implementation-Version': project.version, 'Premain-Class' : "com.java.magicianAgent.MagicianAgent", 'Can-Redefine-Classes' : true, 'Can-Retransform-Classes' : true) } }터미널에서 다음 명령어를 통해 build 한다./gradlew clean buildbuild.libs file 안에 있는 jar file 을 확인한다. (옵션)강의에서와 마찬가지로 zip file 로 변경하면 확인가능합니다. 저같은 경우 SNAPSHOT.jar 과 SNAPSHOT-plain.jar 이렇게 2개가 생겼는데 SNAPSHOT.jar 은 제가 spring boot 로 실행서 그런지 관련 설정들이 보이고 SNAPSHOT-plain.jar 이 맞는거 같더라구요. jar file 의 절대 경로를 복사해 VM option 에 추가한다. 여기서부터는 다시 강의와 같습니다. VM option 이 안보이시면 오른쪽에 Modify options 클릭하면 add vm options 라고 보이실 겁니다. 이상한거나 궁금한거 있으시면 말씀해주세요. gradle 을 쓰시는 모든 분들도 마술을 성공시킵시다 하하
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
API인터셉터 경로
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]만약에 API로 개발할경우 인터셉터 경로는 어떻게 설정해야하죠?ex 유저API@RestController("api/user")@postmapping유저등록api @Deletemapping유저 삭제 api 이때만 인터셉터 실행 이렇게 있으면 .addPathPatterns("/**")할시 둘다 등록이 되기 때문에 @Deletemapping 이때만 등록하고 싶은데 방법이 있나요? 유저등록일때만@postmapping("/join")으로 설정해야 하나요?
-
미해결스프링부트 시큐리티 & JWT 강의
로그인 올바르게 해도 login?error로 갑니다
SecurityConfig.java@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig extends WebSecurityConfigurerAdapter { private final SecurityDetailsService securityDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http // .csrf().disable() .authorizeRequests() .antMatchers("/user/**").authenticated() .antMatchers("/admin/**").access("hasRole('ADMIN')") .anyRequest().permitAll() .and() .formLogin() .loginPage("/login") .loginProcessingUrl("/login") .defaultSuccessUrl("/home") .usernameParameter("userEmail") .and() .logout() .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/login") ; } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } SecurityDetails.java@RequiredArgsConstructor public class SecurityDetails implements UserDetails { private final UserEntity userEntity; @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new GrantedAuthority() { @Override public String getAuthority() { return userEntity.getAuthority().toString(); } }); return authorities; } @Override public String getPassword() { return userEntity.getUserPassword(); } @Override public String getUsername() { return userEntity.getUserEmail(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } SecurityDetailsService.java@Service @RequiredArgsConstructor public class SecurityDetailsService implements UserDetailsService { private final UserRepository userRepository; @Override public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException { UserEntity userEntity = userRepository.findByUserEmail(userEmail); if(userEntity != null) return new SecurityDetails(userEntity); return null; } } SecurityDetails에서 return값이 boolean인 override 받는 메소드들 다 true로 해줬고login페이지나 home페이지에는 이미지도 없어서 문제될 게 없다고 생각되는데도통 이유를 모르겠습니다ㅠㅠㅠ
-
미해결스프링 핵심 원리 - 기본편
MyLogger 를 Interceptor 로 구현 시 질문
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예[질문 내용]안녕하세요? 강의를 듣다가 requesURL 같은 경우 Interceptor 에서 멤버 변수로 저장하는 것이 좋다고 해서 구현을 해보려고 했는데 문제가 생겨서 질문 드립니다.## 앞으로 강좌를 계속 듣긴 할 거라서 혹시 뒤의 강좌를 듣고 나면 해결될 수 있는 문제라면 어느 강좌인지 알려주셔도 될 것 같습니다. MyLogger.javapackage hello.core.common; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.UUID; @Component @Scope(value = "request") public class MyLogger { private String uuid; private String requestURL; public void setRequestURL(String requestURL) { this.requestURL = requestURL; } public void log(String message) { System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message); } @PostConstruct public void init() { uuid = UUID.randomUUID().toString(); System.out.println("[" + uuid + "] request scope bean create: " + this); } @PreDestroy public void close() { System.out.println("[" + uuid + "] request scope bean close: " + this); } } CoreInterceptor.javapackage hello.core.common; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component @RequiredArgsConstructor public class CoreInterceptor implements HandlerInterceptor { private final ObjectProvider<MyLogger> myLoggerProvider; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("CoreInterceptor.preHandle"); String requestURL = request.getRequestURL().toString(); MyLogger myLogger = myLoggerProvider.getObject(); myLogger.setRequestURL(requestURL); return HandlerInterceptor.super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } } WebMvcConfig.javapackage hello.core.common; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @RequiredArgsConstructor public class WebMvcConfig implements WebMvcConfigurer { private final ObjectProvider<MyLogger> myLoggerProvider; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CoreInterceptor(myLoggerProvider)).excludePathPatterns("/css/**", "/images/**", "/js/**"); } } LogDemoController.javapackage hello.core.web; import hello.core.common.MyLogger; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; @Controller @RequiredArgsConstructor public class LogDemoController { private final LogDemoService logDemoService; private final ObjectProvider<MyLogger> myLoggerProvider; @RequestMapping("log-demo") @ResponseBody public String logDemo(HttpServletRequest request) { String requestURL = request.getRequestURL().toString(); MyLogger myLogger = myLoggerProvider.getObject(); myLogger.log("controller test"); logDemoService.logic("testId"); return "OK"; } } LogDemoService.javapackage hello.core.web; import hello.core.common.MyLogger; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class LogDemoService { private final ObjectProvider<MyLogger> myLoggerProvider; public void logic(String id) { MyLogger myLogger = myLoggerProvider.getObject(); myLogger.log("service id = " + id); } } 위와 같이 이렇게 구현을 해보니 문제가 생기는데...일단, 인터셉터를 스프링 빈으로 등록하면서 MyLogger 를 활용하려면 알려주신 ObjectProvider 를 사용해야 할 것 같은데... WebMvcConfig 에서 인터셉터를 등록하는 경우, 위의 경우처럼 처음 서버 구동 시에 처리되어야 하는데 이 때는 reqeust 를 사용할 수 없는 문제입니다. 앞서 이 문제를 ObjectProvider 로 해결했는데 Interceptor 의 경우, 나중에 등록이 안 될 것 같은데 이런 경우 어떻게 해결해야 될까요?저렇게 인터셉터를 null 로 호출하니 실행해 보면 WebMvcConfig.java -> registry.addInterceptor(new CoreInterceptor(null))...java.lang.NullPointerException: Cannot invoke "org.springframework.beans.factory.ObjectProvider.getObject()" because "this.myLoggerProvider" is null위와 같은 에러가 발생합니다.관련된 소스 첨부하였습니다.도움 부탁 드립니다. 위의 소스는 제대로 된 걸로 다시 첨부하였습니다.registry.addInterceptor(new CoreInterceptor(null))...->private final ObjectProvider<MyLogger> myLoggerProvider; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CoreInterceptor(myLoggerProvider)).excludePathPatterns("/css/**", "/images/**", "/js/**"); } 위와 같이 수정하였습니다. 이런 저런 버그들과 강의에서 가르쳐 주신 ObjectProvider 를 사용해서 설정파일에서 request 가 들어오는 시점에 로그를 주입해서 처리되도록 수정하였습니다.Interceptor 에서는 왜 ObjectProvider 를 사용하지 못할 거라고 생각했는지 모르겠네요. 답변 글 보고 질문을 수정하다가 생각이 나서 소스를 수정해 보니 잘 되는 것 같네요.감사합니다.
-
해결됨스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
6. 스프링 MVC - 기본 기능 12페이지에 질문이 있습니다.
"@RequestMapping 은 URL 경로를 템플릿화 할 수 있는데, @PathVariable 을 사용하면 매칭 되는 부분을 편리하게 조회할 수 있다."위 내용중에서 URL 경로를 템플릿화한다라는게 무슨 의미인지 모르겠습니다. 혹시 이 부분에 대해서 좀 더 자세히 설명을 해주시면 감사하겠습니다.
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
SpringDataJpa를 쓸때 수정 기능
test학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]test강의 내용으로 게시판 만들기를 병행하고 있습니다.이번 강의에서 Spring Data Jpa로 변경을 하였는데게시글 수정과 같은 수정 기능은인터페이스인 SpringDataJpaMemberRepository에서 작성하나요?아니면 Service단에서 Repository의 find를 통해서 수정하도록 작성하나요?SpringDataJpa에서 find, delete는 있는데 수정은 어떻게 이뤄지는지 궁금합니다..d
-
미해결스프링 부트 - 핵심 원리와 활용
@Configuration 주석 이유
두가지 질문이 있는데요MySpringBootMain띄울때 HelloConfig 클래스에 @Configuration 이 있어도 잘 동작 하던데 주석 처리하는 이유가 뭔지 알 수 있을까요? 다른 질문인데요 @Configuration안에 @Component가 있어서 @Component 대신 @Configuration을 써도 되는것 같던데 빈여러개 등록할때 말고 하나의 클래스를 빈으로 등록할때 @Component 대신 @Configuration을 사용하지 않는 이유가 뭘까요...?찾아봐도 잘 안나와서 질문 드립니다.
-
해결됨스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
실무에서는 로그를 어떻게 사용하는지, 간단한 토이 프로젝트에서는 로그를 어떻게 사용하면 좋은지 궁금합니다.
실무에서는 로그를 어떻게 사용하는지 궁금합니다.간단한 토이 프로젝트에서는 로그를 어떻게 사용하면 좋은지 궁금합니다.감사합니다.
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
rest일때만 무한 루프 도는 이유가 궁금합니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)예질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)예[질문 내용]그냥 컨트롤러에서 model에 담아서 조회할떄는 무한루프가 안도는데 json으로 반환할떄는 왜 무한로프 도는지가 궁금합니다.
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
p6spy 1.9.0 -> 1.8.1 버전 관련 내용 공유
p6spy releases를 확인해보면 1.9.0부터 Spring boot 3을 지원한다고 되어있고 테스트시 로그가 안뜹니다. 다른 질문에서보니 해당 버전이 메이븐에서만 지원되어서 그런 듯 합니다.그래서 바로 아래 버전인 1.8.1을 사용해서 정상적으로 로그가 뜨는 것을 확인했습니다.그래서 앞으로 최신 버전으로 했을때 안뜬다면 버전을 내려보시면 좋을 것 같습니다.
-
미해결스프링 핵심 원리 - 기본편
api vs 라이브러리 vs 프레임워크 제가 이해한게 맞을까요?
api: 사람과 컴퓨터 사이에 키보드라는 접점으로 소통하듯이 프로그램과 프로그램 사이는 api라는 접점,규약이 있어서 통신할 수 있고 api를 통해서 데이터나 라이브러리를 호출할 수 있다 라이브러리: 개발하면서 반복되는 코드들을 모아놓은 것 남이 만들어진 코드를 기반으로 개발을 할 수 있어서 생산성이 증가한다 라이브러리는 개발자가 능동적으로 호출할 수 있다프레임워크: 개발하면서 반복되는 코드들을 어떤 규칙에 맞게 쓸 수 있도록 환경을 구성해 놓은 것 라이브러리와 차이점은 개발자가 작성한 코드가 프레임워크에 의해 수동적으로 불려진다는 점이 차이점이다
-
미해결스프링 핵심 원리 - 기본편
RequestURL 이 로그에서 일부 생략됩니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예[질문 내용]강의 내용중에 http://localhost:8080/log-demo 이런식으로 로그가 남는데저같은 경우는 http://localhost:8080 부분이 생략된 채로 나옵니다.이런식으로요. 다른분이 작성한 글에도 저와 같은 내용인 것을 보긴했는데그분은 인텔리제이를 재실행하니 고쳐졌다고 하시는데저는 재실행 + 재부팅도 해봤는데 안되네요. 작동에는 문제가없는데 이부분만 그러네요.제 MyLogger 부분에 출력부분을 긁어왔습니다.@Component @Scope(value = "request") public class MyLogger { private String uuid; private String requestURL; public void setRequestURL(String requestURL) { this.requestURL = requestURL; } public void log(String message) { System.out.println("[" + uuid + "][" + requestURL + "] " + message); } @PostConstruct public void init() { uuid = UUID.randomUUID().toString(); System.out.println("[" + uuid + "] request scope bean create : " + this); } @PreDestroy public void close() { System.out.println("[" + uuid + "] request scope bean close : " + this); } }
-
해결됨실전! 스프링 데이터 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 활용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가지 차이점에 대해서 설명 부탁드립니다! 감사합니다 :)