[5기 정의진] Springboot-jpa weekly 미션 1,2,3 제출합니다.#343
[5기 정의진] Springboot-jpa weekly 미션 1,2,3 제출합니다.#343uijin-j wants to merge 9 commits intoprgrms-be-devcourse:uijin-jfrom
Conversation
Customer 엔티티를 이용한 CRUD 구현
order, order_item, item 엔티티 생성
| @AttributeOverride(name = "street", column = @Column(name = "street")), | ||
| @AttributeOverride(name = "zip_code", column = @Column(name = "zip_code")) | ||
| }) | ||
| private Address address; |
There was a problem hiding this comment.
@AttributeOverrides는 클래스 안의 필드를 컬럼으로 인식하게 해주는 것이군요 👍
There was a problem hiding this comment.
신기하네요.. 혹시 Address 내부에서 Column을 추가하면 이상이 있을까요? 만약 이상이 없다면 AttributeOverrides를 사용하면서 얻을 수 있는 이점이 무엇인가요??
There was a problem hiding this comment.
@Sehee-Lee-01 정확하게는 @Embeddable, @Embedded 애너테이션으로 클래스를 어떤 엔티티의 속성으로 넣을 수 있다고 합니다! @AttributeOverrides는 컬럼명을 지정해주고 싶을 때 사용하는 것 같아요:)
There was a problem hiding this comment.
@IjjS Address 내부에 @Column을 추가해도 되고, @AttributeOverride와 @Column을 모두 쓰지 않아도 자동으로 Address의 필드명으로 DB에 저장되게 됩니다!
제 생각에 @AttributeOverrides를 쓰는 이유는 Address를 customer 테이블에 저장할 때, 컬럼명을 지정해주기 위해서 인 것 같습니다! 예를 들어 customer가 집주소와 회사 주소를 가지고 있다면 @AttributeOverride(name = "city", column = @Column(name = "home_city")) 와 같이 사용할 수 있을 것 같아요! 그리고 Address가 또 다른 엔티티에 저장될 때, 더 의미 있는 이름으로 컬럼명을 지정하고 싶은 경우에도 좋을 것 같아요!(ex. 배송지)
그리고 다시보니 지금 저의 경우에는 @AttributeOverride를 생략해도 될 것 같네용!
There was a problem hiding this comment.
[Baeldung] Jpa @Embedded and @Embeddable 이건 제가 참고 했던 링크입니다:) 참고해 보셔도 좋을 것 같아요!
There was a problem hiding this comment.
저는 처음에 Address라는 객체를 사용했음에도 AttributeOverrides로 명시하는 것은 캡슐화가 깨진다고 볼 수 있지 않을까? 하고 생각했는데, DB와 관련되어 있기도 하고 다른 엔티티에 저장할 때 더 의미 있는 이름으로 컬럼을 지정할 수 있다는 점은 완전 유용해보이네요!!!
|
|
||
| @Column(name = "age") | ||
| @Range(min = 0, max = 200) | ||
| private Integer age; |
There was a problem hiding this comment.
primitive 타입인 long, int가 아니라 Integer, Long 등 Wrapper 클래스로 하는 이유가 있나요!
제가 알기로는 null 값이 주어질 때, wrapper 클래스(null 설정)와 primitive 타입(0 설정)이 각각 값 설정이 달라서 이렇게 설정하는 걸로 알고 있어서요! 제가 잘 몰라서 확인차 여쭤봅니당!
There was a problem hiding this comment.
일단 첫번째로 primitive type은 객체가 아니기 때문에 객체지향 관점에서(?) 웬만하면 Wrapper 클래스를 쓰려고 했습니당!
두번째로는 age는 필수값이 아니기 때문에 충분히 null이 들어올 수 있고, 그 경우 0살인 것 보다 null로 명시하는 것이 더 좋을 것 같아서 사용했습니다:)
| protected Address() { | ||
| this(null, null, null); | ||
| } |
There was a problem hiding this comment.
여기는 롬복을 이용한 것이 아닌 이렇게 명시하신 이유가 있으신가요? 학습적인 이유일까요??
There was a problem hiding this comment.
처음에는 @NoArgsConstructor(force = true, access = AccessLevel.PROTECTED)를 달아줬는데, NoArgsConstructor is only supported on a class or an enum라는 에러가 발생하더라구요🥲
찾아보니 NoArgsConstructor가 record의 디자인 원칙(생성자를 통해 모든 필드를 초기화하는 데이터 전달 객체)과 모순되기 때문에 사용할 수 없다고 합니다.!
There was a problem hiding this comment.
오 그런게 있었군요..!😲
그렇다면 드는 생각이 Address는 record에 맞지 않은 객체가 아닌가 싶습니다!
record는 데이터 전달용으로 만들어진 기능이라고 생각하는데NoArgsConstructor가 달릴 수 없는 것이 타당하다고 생각이 들어요. Address가 VO로 사용된 만큼 로직이 생길 수도 있고요.
디자인 원칙과 모순된다고 경고도 줬는데 record를 이렇게 사용하는 것보다 class를 사용하는게 더 맞지 않을까 하는 의견입니다!
| @GeneratedValue(strategy = GenerationType.SEQUENCE) | ||
| private Long id; | ||
|
|
||
| @Column(name = "order_date_time", columnDefinition = "TIMESTAMP") |
There was a problem hiding this comment.
개인적인 궁금증인데 혹시 columnDefinition = "TIMESTAMP"가 없다면 이상이 생길까요? 만약 이상이 없다면 TIMESTAMP로 지정하는 이유는 무엇인가요?
There was a problem hiding this comment.
확인해보니 columnDefinition을 지정하지 않으면 order_date_time이 TIMESTAMP(6)로 생성 되고, 지정하면 TIMESTAMP로 생성이 되더라구요!
columnDefinition으로 해당 필드 값을 DB에 어떤 타입으로 저장할지 구체적으로 명시할 수 있는 것 같습니다!
그리고 columnDefinition 옵션을 주지 않는다면 JPA가 알아서 적절한 default 값으로 생성하는 것 같습니다!
이 방법을 쓰면 개발자가 원하는 타입으로 데이터베이스에 해당 값을 저장할 수 있다는 장점이 있는 것 같고, 단점으로는 만약 TIMESTAMP를 지원하지 않는 DB를 사용한다면 에러가 발생할 수 있을 것 같습니다!
| public Order(String memo, List<OrderItem> orderItems, Customer customer) { | ||
| this.orderStatus = OrderStatus.COMPLETED; | ||
| this.memo = memo; | ||
| this.orderItems = (orderItems == null)? new ArrayList<>() : orderItems; |
There was a problem hiding this comment.
실수로 공백이 빠진 것 같습니다!
| this.orderItems = (orderItems == null)? new ArrayList<>() : orderItems; | |
| this.orderItems = (orderItems == null) ? new ArrayList<>() : orderItems; |
| properties: | ||
| hibernate: | ||
| query.in_clause_parameter_padding: true |
There was a problem hiding this comment.
실수로 탭이 더 들어간 것 같습니다!
| properties: | |
| hibernate: | |
| query.in_clause_parameter_padding: true | |
| properties: | |
| hibernate: | |
| query.in_clause_parameter_padding: true |
|
|
||
| // then | ||
| Optional<Customer> found = customerRepository.findById(saved.getId()); | ||
| assertDoesNotThrow(() -> found.get()); |
There was a problem hiding this comment.
isPresent나 isNotEmpty가 아니라 이런 식으로 할 수도 있었군요!
There was a problem hiding this comment.
assertThat(found).isNotEmpty()로도 할 수 있군요! 몰랐는데 배워갑니당!
| .extracting("name", "nickName", "age", "address", "description") | ||
| .containsExactly(customer.getName(), customer.getNickName(), customer.getAge(), customer.getAddress(), customer.getDescription()); |
📌 과제 설명
스프링부트 JPA 위클리 미션
👩💻 요구 사항과 구현 내용
✅ PR 포인트 & 궁금한 점
커밋 메시지 중
fix: 사용하지 않는 애너테이션 제거커밋 타입이 fix가 아니라 refactor입니다..😅