3주 차 강의 내용 정리

  • JPA
  • 3주 차 미션(Lotto) 대한 피드백 

JPA

SQL을 직접 다룰 때 발생하는 문제점

  1. 반복 작업
    테이블 구조가 변경(추가, 삭제)되면 관련된 SQL을 전부 수정해줘야 한다.
  2. 신뢰성
    Human error가 발생할 가능성이 높아짐 (ex. 데이터 fetch 로직에서 테이블 간 매핑이나 쿼리 작성이 빠져있는 경우)

=> JPA를 사용하면 위 issue에서 자유로워질 수 있다.

 

더보기

JDBC, SQLMAPPER, ORM

공통점: persistence (데이터를 영속적으로 저장하기 위해 사용하는 기술) 

 

JDBC: java의 JDBC API

          DriverManager -> Connection -> Statement -> ResultSet 으로 이루어져 있는 구조

          커넥션, 쿼리를 직접 작성하고 관리해야 한다.

 

SQL MAPPER: MyBatis, Spring JDBC

                       SQL을 자바 코드로부터 분리한 것

 

ORM: JPA, Hibernate, Spring data JPA, Sping data JDBC

          사람이 쿼리를 관리하지 않는 것! (Lazy Loading, Dirty Checking, Caching 등의 특징이 있음)

 

 

JPA 특징

  1. 데이터베이스 스키마를 자동으로 생성하는 기능을 지원

spring.jpa.hibernate.ddl-auto=create
더보기

설정값 종류

create: 기존 테이블 삭제 후 다시 생성 (DROP + CREATE)

create-drop: create와 같으나 종료 시점에 테이블 DROP

update: 변경된 부분만 반영 (운영 DB에 사용하면 안됨)

validate: entity와 table이 정상 매핑되었는지만 확인

none: 사용하지 않음

 

※ 크리티컬 이슈를 발생시킬 수 있기 때문에 실무에서는 보통 validate or none으로만 사용한다. 

 

    2. @(Annotation)으로 많은 기능을 활용할 수 있다.

@Entity // (1)
@Table(name = "station") // (2)
public class Station {
    @Id // (3)
    @GeneratedValue(strategy = GenerationType.IDENTITY) // (4)
    private Long id;

    @Column(name = "name", nullable = false) // (5)
    private String name;
    
    protected Station() { // (6)
    }
}
  1. @Entity : pk 꼭 가져야함.  엔티티 클래스임을 지정하며 테이블과 매핑된다.
  2. @Table: 굳이 선언할 필요 없음. Name property 이용해서 컨벤션에 맞는 테이블이름을 정할 있다. 생략할 경우 엔티티클래스이름과 동일한 테이블로 매핑
  3. @Id : pk
  4. @GeneratedVAlue : pk 생성 규칙을
  5. @column
    • 컬럼의 이름을 이용하여 지정된 필드나 속성을 테이블의 컬럼에 매핑한다.
    • 굳이 선언하지 않아도 된다.
  6. 매개 변수가 없는 생성자
    • The entity class must have a no-arg constructor. The entity class may have other constructors as well. - JSR 338

   3. 영속성 컨텍스트

  • 엔티티를 영구 저장하는 환경
  • 엔티티 매니저로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다
  • 영속성 컨텍스트의 기능
    - 1차 캐시
    - 동일성 보장
    - 트랜잭션을 지원하는 쓰기 지연
    - 변경 감지
    - 지연 로딩

  4. 엔티티의 생명주기

비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
영속(managed): 영속성 컨텍스트에 저장된 상태
준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
삭제(removed): 삭제된 상태

 

JPA의 연관관계 

엔티티들은 대부분 다른 엔티티와 연관 관계를 맺는다.

객체는 참조를 사용, 테이블은 외래 키를 사용해서 관계를 맺는다.

  • 방향: 단방향, 양방향이 있다. 방향은 객체 관계에서만 존재하고 테이블 관계는 항상 양방향이다.
  • 다중성: 일대일, 다대다, 일대다, 다대일 
  • 연관관계의 주인
    • 객체 사이에 관계가 형성되면 관계의 주인을 정해야 한다.
    • 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 등록, 수정, 삭제할 수 있다.
    • 주인이 아닌 쪽은 읽기만 가능하다.
  • 연관관계의 주인이 아닌 곳에서 입력된 값은 외래 키에 영향을 주지 않는다.
  • 양방향 관계에서는 양쪽 다 데이터 일관성에 대해 신경을 써야 한다.
  • 양방향 관계에서 두 코드는 하나인 것처럼 사용하는 것이 안전하다.
  • 한 번에 양방향 관계를 설정하는 메서드를 연관 관계 편의 메서드라 한다.
public void setLine(Line line) {
    this.line = line;
    line.getStations().add(this);
}

public void addStation(Station station) {
    stations.add(station);
    station.setLine(this);
}
매핑 시 무한루프에 빠지지 않게 조심해야 한다.

 

TIPS

  • AUDITING
    애플리케이션의엔티티의 생성 시간과 마지막 수정시간을 관리할 필요가 있다면 수동으로 매번 추가하는 대신 Auditing 기능을 이용해서 자동으로 추가해 있다.

  1. @Configuration 클래스에 @EnableJpaAuditing 추가한다.
@EnableJpaAuditing
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

    2. 엔티티에 콜백 리스너를 추가한다.

@EntityListeners(AuditingEntityListener.class)
@Entity
public class Line {

    3. 생성 날짜와 마지막 수정 날짜 프로퍼티에 @CreatedDate @LastModifiedDate 추가한다.

@EntityListeners(AuditingEntityListener.class)
@Entity
public class Line {
    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;

    4. @MappedSuperclass 를 사용해서 abstract class 생성해서 사용한다면 중복코드를 분리할 있다.

  • 비교
    엔티티 비교시에는 식별자있어도 충분하다.

좀 더 디테일한 JPA 관련 내용은 추후 해당 블로그에 기재하도록 하겠다.

 

3주 차 미션에 대한 피드백

  • . 매직 넘버는 명명된 상수로 치환해서 사용하기
// AS-IS
public class Calculator {

    public int splitAndSum(String text) {
        String[] numArr = Splitter.split(!isEmptyOrNull(text) ? text : "0");

//TO-BE
public class Calculator {
	private final static String ZERO = "0";

    public int splitAndSum(String text) {
        String[] numArr = Splitter.split(!isEmptyOrNull(text) ? text : ZERO);
        

 

  • 유틸리티 클래스의 접근성 제한하기
    • 유틸 클래스의 경우 불필요한 객체 생성을 방지하기 위해서 Private 생성자를 사용하기
  • 직관적인 코드 작성
//AS-IS
if(inputValue == null || inputValue.isEmpty()) {
	return true;
}
	return false;
        
//TO-BE       
 return inputValue == null || inputValue.isEmpty();

 

  • 스트림을 사용해서 간략하게 코드 작성
//AS-IS
int resultNum = Integer.parseInt(numArr[0]);
for(int i = 1; i < numArr.length; i++) {
	resultNum = add(resultNum, Integer.parseInt(numArr[i]));
}

	return resultNum;

//TO-BE
return Arrays.stream(numArr)
.mapToInt(Integer::parseInt)
.sum();

 

  • RetainAll 함수를 사용하는 방법
//AS-IS
return lotto.nums.stream().
	filter(n -> this.nums.contains(n)).count();
    

 

  • 로또 2,3등 구하는 로직 개선
//AS-IS

    public static Rank of(int matchNums, boolean matchBonus) {
        if (matchNums == SECOND.matchNums && matchBonus) {
            return SECOND;
        }
        if (matchNums == THIRD.matchNums) {
            return THIRD;
        }
        return Arrays.stream(Rank.values())
                .filter(rank -> rank.matchNums == matchNums)
                .findAny()
                .orElse(MISS);
    }
    
//TO-BE

    return Arrays.stream(values())
        .filter(rank -> rank. matchNums == matchNums)
        .filter(rank -> !rank.equals(SECOND) || matchBonus)
        .findFirst()
        .orElse(MISS);

3주 차 미션(Lotto) 리뷰

소스코드: github.com/louisJu/java-lotto-ex

느낀 점

  • 상수처리에 대해서 조금 더 신경 써야 할 것 같음.
  • ENUM 클래스 활용에 아직 많이 미숙하구나. 
  • 스트림 활용법에 대한 학습 필요.

회고 

 : 아직까진 예전에 실습해본 주제로 과제를 진행하다 보니 접근하기 나름 편했다. 역시 반복학습의 효과는 짱...

+ Recent posts