Back-end/Spring Boot, JPA

스프링 활용 JPA 강의 내용 기록

philo0407 2021. 3. 29. 22:46

start.spring.io

 

파일을 받으면 압출을 풀어서 build.gradle 을 임포트한다.

 

Lombok 설치 및 테스트

1. (Plugins) 마켓플레이스 Lombok 설치

2. Annotaion Processors 에서 Enable Annotation Processing 클릭

 

 

 

테스트용 클라쓰

 

테스트하기

 

spring-boot-devtools

 

파일 수정후 저장시 restart

캐시를 지워서 thymeleaf 파일이 수정되었을 시에 반영되도록 함

 

Build - Recompile 누르면 (이때 서버 재기동안해도 됨) 브라우저를 새로고침할경우 자동반영된다!

 

@Repository : entity를 찾아주는 역할 (dao라 생각하면 된다)

 

Live-Template에 요걸 추가하면 편하당!

 

 

em을 이용한 데이터 변경은 Transactional을 타야한다.

 

 

 

로그 보기!

 

만일 위 파라미터를 보고 싶으면

 

요걸 추가하면 된다. trace

 

그럼 요리 나옴

 

좀 더 직관적으로 보고 싶으면

 

 

요거 추가
고럼 로그에 요게 추가된다

 

 

운영에는 성능 테스트를 꼭 해야 한다!

 

 

 

 

 

위 두 Entity의 관계는 Order : Member = N : 1 이다.

우리는 보통 회원이 여러개의 주문을 갖는다라고 생각을 하지만..

시스템 입장에서는 주문을 알기 위해서는 회원 정보가 필요하다라고 받아들인다.

Order는 member_id라는 외래키를 가지며 이 관계의 주인이다.

이 FK만 알면 테이블의 관계를 다 알 수 있기 때문이다.

반대로 Member는 orders라는 리스트를 가지고 mappedBy라는 애너테이션 속성이 있는데,

이 속성의 뜻은 자신의 매핑의 주인이 아니라 Order의 member에 의해 매핑되었다는 뜻이다.

 

 

만일 테이블이 1:1이라면

FK를 연관관계의 주인에 잡아두도록 하자.

 



** 요건 암기하도록 하자. N+1 성능 문제

@XToOne에 FetchType.LAZY를 안걸어두면 

1개의 쿼리에 대해 N개의 쿼리가 나갈 수 있어서 성능 악화 문제를 야기한다.

걸어두자 !!

(X = 1, N)

 

@Table(name = ...) 명은 필요할 때 걸어주면된다.

SpringPhysicalNamingStrategy 이 기본적인 테이블 네이밍을 해준다.

ex) MemberOrder -> member_order

 

CascadeType.ALL 을 안해주면

컬렉션(List등)의 요소 하나하나 마다 영속화를 해주어야한다.

 

연관 관계 메서드

public static void main(String[] args) {
    Member member = new Member();
    Order order = new Order();
    
    member.getOrders().add(order);
    order.setMember(member);
  }

 메서드 상에 연관 관계를 지정하지 않으면.. 위와 같이 작성해주어야 한다.

당초 필드를 set하는 부분에서도부터 상호간에 set하게끔 지정해주는 것이 좋다.

 

 

public class MemberService {

  @Autowired 
  private MemberRepository memberRepository;
  ...
}

관심의 대상이 아닌 곳은 코드상에서 제외하였다.

통상적으로 위와 같은 코드가 많다. 하지만 위와 같은 필드 주입보다는 생성자 주입방식이 좋다.

왜냐하면 Mock 등을 주입받을 수 없기 때문이다.

 

public class MemberService {

  private MemberRepository memberRepository;
  
    @Autowired
    public MemberService(MemberRepository memberRepository) {
    	this.memberRepository = memberRepository;
  }
  
  ...
}

그리고 생성자가 오로지 하나면 Autowired를 생략할 수 있다.

 

그러나 이것보다도 간편한 방법이 있다. 바로 Lombok을 이용하는 것.

 

@RequiredArgsConstructor
public class MemberService {

  private final MemberRepository memberRepository;
  
  ...
}

@AllArgs 보다는 위가 좋다. Required는 final이 붙은 필드만 주입한다!

 

 

중복회원을 검증할 때 실제로는 WAS상.. 사용자가 동시에 회원가입을 시도할 수 있어서

validation이 유효하지 않을 수 있다. 동시성을 고려해서 코드를 만들어야 한다.

 

test directory는 main과 달리

datsource, jpa 등의 설정을 안하여도

mem:testdb에서 동작한다.

그리고 테이블을 만들고 테스트가 끝나면 사라진다.

또한 메모리 db는 WAS가 내려가면 자동으로 소멸된다.

 

Item 의 재고 관련된 메서드는

ItemService에서가 아닌

Entity 자체에서 처리하는 것이 좋다. 

재고 변수들이 Item 안에 있는데 이 안에서 해결하는 것이 응집성에 좋기  때문이다.

 

 

객체 생성시 주의

OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
OrderItem orderItem = new OrderItem();

위와 같이 객체를 여러가지 방식으로 생성이 가능하면... 유지보수하기 어렵다. 

OrderItem.createOrderItem ~ 으로 생성하기로 하였으면 다른방식의 객체 생성은 막는게 좋다.

 

orderItem의 생성자의 접근제어자를 protect 로 설정하면 된다. jpa에서 protect까지는 허용한다.

protected OrderItem() { }

 

혹은 아래와 같이 Lombok을 이용해도 된다.

@NoArgsConstructor(access = AccessLevel.PROTECTED)

 

** em.persist() 만으로는 DB에 입력이 안된다.

em.flush()가 동작해야 한다.

기본적으로 flush()가 호출 될 때는 tx.commit() 이나, em.createNativeQuery() 가 동작할 때 이다.

 

** JPA Criteria...

JPA 표준 스펙임에도 불구하고 복잡하고.. 가독성도 안좋고 유지보수도 어려움..

코드를 읽으면 이게 무슨 쿼리인지 연상하기도 어렵다..

강의 저자도 아마 실무를 안한사람이나 고민을 과하게 해서 만든 스펙인것 같다고 하였다 ㄷ

심지어 책도 JPA 스펙에 있기 떄문에 적은 거지 ... 실무에서 못사용한다고 

** Spring Boot, JPA, Spring Data JPA, QueryDSL

 

** logging 설정

 

public class HomeController{ 
	 ... 
	Logger logger = LoggerFactory.getLogger(getClass());
}

요거는 아래와 같다. 요거트

@Slf4j
public class HomeController{ ... }

 

devtools 설정되어있으면

Recompile만 해주면 반영이 된다.

 

만일 파일이 적용이 잘 안되면

Sync(Reload)를 다시 해주거나 rebuild를 해주고 restart하자

 

** 위와같이 파라미터 뒤, @Valid 뒤에 BindResult가 있으면 error가 있었는지 를 점검해준다.

그리고 MemberForm의 유효성 검증을 해준다.. 스프링 2.1이 아닌 2.4 버전 등에서는 @NotEmpty 를 사용하려면

 

implementation 'org.springframework.boot:spring-boot-starter-validation'

 

위와 같은 gradle 코드를 하나 추가해주면 댄다.

 

화면에 있는 입력 Form들은 실무에서 정말 많다.. 이에 따라 입력받는 내용과 Entity과의 차이가 생기는데

이 차이를 계속 Entity에 반영하면 겉잡을 수 없이 커지며 화면 종속적으로 변한다. 이것을 중간에 createMemberForm과 같이 중간에 Form객체나 DTO를 통해 Entitty가 계속 커지는 것을.. 방지해야 한다.

 

**controller에서 타임리프를 호출할떄

타임리프에 셋팅한 모델명으로 안던져 주면.. 에러난다. ㅠ : SpringInputGeneralFieldTagProcessor

 

속성값이 많은 Entity라면 ModelMapper 라이브러리,

ModelMapper - Getting Started

요걸 고려하는 것도 좋다.

 

상품을 업데이트할 때

위와 같이 URL 방식으로 넘어 올때..

queryString을 조작하면 다른 사람의 상품을 변경할 수도 있다.

이 상품에 대한 변경 권한이 있는지 확인하는 로직을 service 계층에 넣는게 좋다. (세션 권한은.. 요새 안쓴다고 한다)