[질문 내용] 부모 클래스 Parent와 해당 클래스를 상속 받는 Child 클래스가 있다면 Parent 클래스의 인스턴스를 생성하면 해당 클래스를 상속받는 모든 자식 클래스도 같이 생성되는 것인가요? 일전에 배우기로는 인스턴스 생성시 해당 인스턴스의 부모와 자식이 모두 같은 참조값 내부에 생성된다고 배웠습니다. 혹시 자식 인스턴스를 생성했을 때만 부모가 모두 생성된다와 같은 사항이 있는건지 아니면 말 그대로 상속 관계에 있는 모든 인스턴스가 생성 되는건지 궁금합니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] application.yml spring: datasource: url: jdbc:h2:tcp://localhost/~/jpashop username: sa password: driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create properties: hibernate: # show-sql: true format_sql: true logging.level: org.hibernate.SQL: debug # org.hibernate.type:trace org.hibernate.orm.jdbc.bind: trace build.gradle plugins { id 'java' id 'org.springframework.boot' version '3.2.0' id 'io.spring.dependency-management' version '1.1.4' } group = 'jpabook' version = '0.0.1-SNAPSHOT' java { sourceCompatibility = '17' } configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' //JUnit4 추가 testImplementation("org.junit.vintage:junit-vintage-engine") { } test { useJUnitPlatform() } MemberRepositoryTest.java package jpabook.jpashop; import jpabook.jpashop.Entity.Member; import jpabook.jpashop.Repository.MemberRepository; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(SpringExtension.class) @SpringBootTest class MemberRepositoryTest { @Autowired MemberRepository memberRepository; @Test @Transactional @Rollback(false) public void testMember() throws Exception { //given Member member = new Member(); member.setUsername("memberA"); //when Long savedId = memberRepository.save(member); Member findMember = memberRepository.find(savedId); //then assertThat(findMember.getId()).isEqualTo(member.getId()); assertThat(findMember.getUsername()).isEqualTo(member.getUsername()); System.out.println("findMember == member: " + (findMember == member)); } } 테스트 실행 시 No matching tests found in any candidate test task. Requested tests: Test pattern jpabook.jpashop.Repository.MemberRepositoryTest in task :test * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org. Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0. You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins. For more on this, please refer to https://docs.gradle.org/8.8/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation. BUILD FAILED in 1s 4 actionable tasks: 1 executed, 3 up-to-date 오류가 발생합니다.
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] 교안 "8. 생산자 소비자 문제 1" - 12 page - 3번째 줄 "임계 영역은 synchronized를 영역을 뜻한다." -> "임계 영역은 synchronized를 적용한 영역을 뜻한다." "적용한"이 빠져서 다음 업데이트 때 반영되면 좋을 것 같아 남깁니다. 좋은 강의 항상 감사드립니다 영한님! :)
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의 회원테스트 기술 부분을 학습하고있는데 테스트케이스 오류가 났습니다 어떻게 해결해야할지 몰라 질문글을 적었습니다 아래는 테스트 코드입니다 import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; class MemberServiceTest { MemberRepository memberRepository = new MemoryMemberRepository(); MemberService memberService = new MemberService(); //테스트는 과감하게 한글로 작성 가능 //테스트할땐 given,when,then 틀 사용 @Test void 회원가입() { //given Member member = new Member(); member.setName("hello"); //when Long saveId = memberService.join(member); //then Member findMember = memberService.findOne(saveId).get(); org.assertj.core.api.Assertions.assertThat(member.getName()).isEqualTo(findMember.getName()); } @Test void findMembers() { } @Test void findOne() { } } 그리고 MemberService코드입니다 package hello.hello_spring.service; import hello.hello_spring.domain.Member; import hello.hello_spring.repository.MemberRepository; import hello.hello_spring.repository.MemoryMemberRepository; import java.util.List; import java.util.Optional; public class MemberService { private final MemberRepository memberRepository = new MemoryMemberRepository(); /*회원가입*/ public Long join(Member member){ validateDuplicateMember(member); //중복회원검증 memberRepository.save(member); return member.getId(); } private void validateDuplicateMember(Member member){ memberRepository.findByName(member.getName()) .ifPresent(m -> { throw new IllegalArgumentException("이미 존재하는 회원입니다"); }); } /*전체회원 조회*/ public List<Member> findMembers() { return memberRepository.findAll(); } public Optional<Member> findOne(Long memberId){ return memberRepository.findById(memberId); } } 아래는 오류 메시지 입니다 java.lang.NullPointerException: Cannot invoke "java.util.Optional.ifPresent(java.util.function.Consumer)" because the return value of "hello.hello_spring.repository.MemberRepository.findByName(String)" is null at hello.hello_spring.service.MemberService.validateDuplicateMember(MemberService.java:22) at hello.hello_spring.service.MemberService.join(MemberService.java:15) at hello.hello_spring.service.MemberServiceTest.회원가입(MemberServiceTest.java:24) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) Process finished with exit code -1 어떻게 해결하면 좋을까요?
@Entity @Getter @Setter @DiscriminatorColumn(name = "dtype") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public abstract class Item { @Id @Column(name = "item_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private int price; private int stockQuantity; @ManyToMany(mappedBy = "items") private List<Category> categories = new ArrayList<>(); //== 비즈니스 로직 ==// public void addStock(int quantity) { this.stockQuantity += quantity; } public void removeStock(int quantity) { int restStock = this.stockQuantity - quantity; if (restStock < 0) { throw new NotEnoughStockException("need more stock"); } this.stockQuantity = restStock; } } @Entity @Getter @Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class OrderItem { @Id @Column(name = "order_item_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "order_id") private Order order; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_id") private Item item; private int orderPrice; private int count; //==생성 메서드==// public static OrderItem createOrderItem(Item item, int orderPrice, int count) { OrderItem orderItem = new OrderItem(); orderItem.setItem(item); orderItem.setOrderPrice(orderPrice); orderItem.setCount(count); item.removeStock(count); return orderItem; } //==비즈니스 로직==// /* * 재고 수량을 복구한다. * */ public void cancel() { getItem().addStock(count); } //==조회 로직==// public int getTotalPrice() { return getOrderPrice() * getCount(); } } @Entity @Getter @Setter @Table(name = "orders") @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Order { @Id @Column(name = "order_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List<OrderItem> orderItems = new ArrayList<>(); @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "delivery_id") private Delivery delivery; private LocalDateTime orderDate; @Enumerated(EnumType.STRING) private OrderStatus status; //== 연관관계 메서드 ==// public void addMember(Member member) { this.member = member; member.getOrders().add(this); } public void addOrderItem(OrderItem orderItem) { orderItems.add(orderItem); orderItem.setOrder(this); } public void addDelivery(Delivery delivery) { this.delivery = delivery; delivery.setOrder(this); } // 생성하는 지점을 변경해야 되면 아래 생성 메서드만 바꾸면 된다. 이게 중요한 포인트다 이것저것 찾아다닐 필요없고 //== 생성 메서드==// public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems) { Order order = new Order(); order.setMember(member); order.setDelivery(delivery); for (OrderItem orderItem : orderItems) { order.addOrderItem(orderItem); } order.setStatus(OrderStatus.ORDER); order.setOrderDate(LocalDateTime.now()); return order; } //==비즈니스 로직==// /* * 주문 취소 * */ public void cancel() { if (delivery.getStatus() == DeliveryStatus.COMP) { throw new IllegalStateException("이미 배송 완료된 상품은 취소가 불가능합니다."); } this.setStatus(OrderStatus.CANCEL); for (OrderItem orderItem : orderItems) { orderItem.cancel(); } } //==조회 로직==// /* * 전체 주문 가격 조회 * */ public int getTotalPrice() { int totalPrice = 0; for (OrderItem orderItem : orderItems) { totalPrice += orderItem.getTotalPrice(); } return totalPrice; } } @Service @Transactional(readOnly = true) @RequiredArgsConstructor public class OrderService { private final OrderRepository orderRepository; private final MemberRepository memberRepository; private final ItemRepository itemRepository; // 주문 @Transactional public Long order(Long memberId, Long itemId, int count) { // 엔티티 조회 Member member = memberRepository.findOne(memberId); Item item = itemRepository.findOne(itemId); // 배송정보 생성 Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); // 주문상품 생성 OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count); // 주문 생성 Order order = Order.createOrder(member, delivery, orderItem); // 주문 저장 orderRepository.save(order); return order.getId(); } // 취소 @Transactional public void cancelOrder(Long orderId) { Order order = orderRepository.findOne(orderId); order.cancel(); } // 검색 // public List<Order> findOrders() { // return orderRepository.f // } } OrderService에서 cancelOrder()을 할 경우 @Transactional에 의해 자동으로 트랜잭션을 시작하고 커밋을 보내고 커밋을 보낼때 플러시가 발생하고 더티체킹이 발생해 직업 update를 안해도 알아서 update 쿼리가 날라가는 부분에 대해서는 이해를 했습니다. 궁금한 부분은 현재 Order order = orderRepository.findOne(orderId); 를 통해 order 엔티티만 조회 했으니 영속성 컨텍스트에는 order 엔티티만 존재하므로 Item 엔티티는 영속성 컨텍스트에 없는데 어떻게 더티체킹에 들어갈까가 저의 의문입니다. 이 부분에 대해서 아래와 같이 생각해봤는데 맞게 생각한건지 궁금합니다. 잘못된 부분에 대해서 알려주시면 감사하겠습니다. order.cancel();의 cancel() 메서드를 보면 orderItem.cancel(); 부분이 존재한다. orderItem.cancel() 의 cancel() 메서드를 보면 getItem().addStock(count); 부분이 존재한다. 여기서 getItem()을 사용해서 (현재 Order와 OrderItem은 지연로딩 관계) 실제 orderItem 값을 사용하므로 DB에서 orderItem 엔티티를 조회해서 영속성 컨텍스트에 저장한다. 다시 orderItem.cancel() 의 cancel() 메서드에서 addStock(count) 메서드를 들어가 보면 Item의 addStock() 메서드로 이동한다. 여기서 this.stockQuantity += quantity; 를 통해 Item의 실제 값을 사용하므로 Item 엔티티도 DB에서 조회해서 영속성 컨텍스트에 저장한다. 결과적으로 OrderService의 cancelOrder() 메서드에서 order.cancel();가 호출되면 Item과 OrderItem 엔티티도 영속성 컨텍스트에 저장되서 더티체킹 범위에 들어가게 된다.
빌더 패턴을 사용하여 객체를 생성하는 방법과 정적 팩토리 메서드를 사용하여 객체를 생성하는 방법을 각각 언제 사용하면 좋을지 궁금합니다. 구글링해서 찾아보니 빌더 패턴은 파라미터가 많을때 사용하고 정적 팩토리 메서드는 파라미터가 적을때 사용한다고 하는데 파라미터가 많고 적음의 기준도 잘 모르겠고 명확하게 언제 사용하면 좋을지에 대한 글을 찾지 못해 질문글을 작성합니다.
안녕하세요 토비님 변하지 않는 코드 분리 commit 에서 WebApiExRateProvider가 private "static" 인 이유가 있나요? 강의에서는 extract method 단축키로 만드셨는데 왜 intellij가 static을 붙이는것을 추천해줬는지 도 좀 궁금합니다...
========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] 강의 내용에서 hashCode 결과로 음수 값이 나올 수 있으므로 절대값으로 변환하는 과정을 가졌는데요, 자바에서는 왜 음수 값이 나올 수 있게 해놓은 건가요? 인덱스로 사용하는 용도 외에 다른 용도가 있나요?
안녕하세요 선생님, 여쭤볼께 있어 글남깁니다 ~! 만약 2회차 회귀 문제라 가정하면 만약 데이터프레임이 x1 x2 x3 ~~~~ y 값이 있을 때, 만약 target으로 하는 y가 있다 ! 라고 했을 때 거기에 대한 x1, x2, x3가 ~~범위에 들어와야 target하는 y값에 가깝다 ! 라는 코딩도 있나요 ? 어떻게 보면 회귀분석인거같은데. .ㅠ 배운건 x1 x2 x3가 있을떄 y가 나오는거까지 밖에 안배워서 궁금하네용
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. 자바 버전 17이상을 사용하는데 빌드가 안됩니다.
안녕하세요 영한님! 다중서버로 이루어진 분산환경에서 분산락이 주로 사용되는데 이는 전체 또는 여러 서버에서 동기화가 필요한 데이터여서 그런것이죠? 현재 학습내용인 자바에서 제공하는 락 (서버별로 락을 갖는 방식은) 분산환경에는 적합하지 않고, 서버별로 락을 걸어도 커버 되는 자원에 한정되게 사용되는걸로 이해한게 맞을까요?
auth LoginView/LogoutView에 success_url_allowed_hosts 설정 추가하여, 로그인/로그아웃 후에 지정 호스트로의 이동 허용 현재 로그인/로그아웃후 지정 호스트로 이동을 허용하는 부분을 실습하고 있는데 막히는부분이 있어서 질문 드립니다. 이부분 코드를 붙여넣고 실행하였더니 로그아웃 부분에서 아래와 같은 에러가 발생하였습니다. (프로필 페이지로 이동은 잘 됩니다.) 찾아보니 GET 메서드가 거절된 것같은데, 이전 강의에서 장고에서 과거에는 GET으로 받았으나, 이제는 POST 요청만 허용한다는 내용을 들은적이 있었던것같은데, 그것과 관련된 것인지 궁금합니다. 다른 분들은 잘되셔서 넘어가셨을텐데, 저가 잘못된것 같긴한데 혹시나해서 여쭈어봅니다. 아래는 로그아웃 구현된 부분입니다.
질문을 온전히 이해할 수 있도록, 모든 맥락을 전달해주세요. 질문은 질문자가 번거로워야 보다 좋은 답변을 얻으실 수 있습니다. 시행착오를 알려주시면 곧바로 원하는 문제에 집중할 수 있습니다. 오류 메시지는 일부만 알려주시기보다 전체 오류 메시지를 캡처해서 주시면, 오류 파악에 도움이 됩니다. 당신의 파이썬/장고 페이스메이커가 되겠습니다. ;-) 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 11-15 강의에서 구현한 프로필 수정 폼에서 이미지를 올린 경우, 다시 프로필 수정 페이지에 들어가면 정상적으로 이전에 등록된 이미지가 현재 란에 보여집니다. 이렇게 구현된 모델은 문제가 몇 가지 있는데요. 수정하지 않고 저장 을 누를 경우 이전 강의에서 교육하신 내용처럼 접미사가 계속 바뀌어가면서 같은 내용의 파일이 spamming 되는 문제가 발생합니다. 우리가 만약 프로필에 다른 항목이 추가된다면, 다른 항목만 수정하지 않고 나가는 유저가 있을텐데 해당 문제를 해결하려면 어떻게 해야할까요? 수정 혹은 취소 체크 박스를 선택하여 삭제를 할 경우 여전히 경로 안에 파일이 남아 있습니다. signal을 이용하면 될 것 같은데, 하기와 같은 방법을 생각해 볼 수 있을까요? @receiver(pre_save, sender=Profile) def edit_delete_on_profile(instance=Profile, **kwargs): instance.avatar.delete(save=False) 수업 내용과는 상관없지만, 기본 제공되는 이미지 업로드 양식이 어색합니다. 특히 취소 체크 박스를 체크하여 제거하는 방식은 너무 어색한데요. 더 좋은게 없을까요? 예를들어 아래는 네이버 '치지직' 에서 프로필 수정 양식입니다.