2021 07 03
2021-07-03¶
JPA¶
- 객체 지향 패러다임
- 객체: 방향성 O
- 파스칼 표기법
Line,Station,LineStation- 테이블: 방향성 X - 스네이크 표기법line,station,line_station- 객체는 자유롭게 객체 그래프를 탐색할 수 있어야 함
- SQL 다룰 때의 문제점 - 필드 하나만 추가하면 SQL 관련된 거 다 수정해야함 - SQL로 조회해온 엔티티를 얼마나 신뢰할 수 있을까?
- ORM - 객체와 관계형 DB를 매핑 - JPA가 자바의 표준 ORM - Hibernate가 대표적인 프레임워크 - 장점 - 패러다임 불일치 문제 해결 - 반복된 SQL문 작성을 안해도 되니 생산성 향상 - 데이터 접근 추상화 - 유지보수 (Entity 수정만 하면 쿼리는 뚝딱) - JPA가 RDB의 문제점을 해결해주는 부분 - 객체의 상속 - 연관 관계 - 객체의 참조로 연관 관계 저장 가능 - 반환한 엔티티를 신뢰하고 사용하게 해줌 - DAO에서 객체 참조 내용들까지 촤르르 다 가져왔을까?
- DB 스키마 자동 생성
- JPA는 스키마 자동 생성 시켜줌
- 속성을 추가하면 앱 실행시점에 DB 자동으로 생성함
spring.jpa.hibernate.ddl-auto=create
- JPA에 사용되는 엔티티 정의하기
@Entity //엔티티 클래스임을 정의 @Table(name = "station") //매핑할 테이블 지정 public class Station { @Id //PK 지정 @GeneratedValue(strategy = GenerationType.IDENTITY) //PK 생성 규칙 명시 private Long id; @Column(name = "name", nullable = false) //칼럼의 이름을 이용해 지정된 필드를 테이블의 칼럼과 매핑 private String name; protected Station() { } }
- Spring Data JPA
- 반복적으로 Dao나 Repository에 주구장창 CRUD 로직 했었음
- JpaRepository
을 사용해보자 - PK 기반의 기본적인 CRUD 제공 - 직접 메서드 이름으로 쿼리 생성 가능
- 영속성 컨텍스트
- 엔티티 영구 저장 환경
- 엔티티 매니저로 엔티티 저장/조회시 엔티티 매니저는 영속성 컨텍스트에 엔티티 보관/관리
- 엔티티 생명 주기 - 비영속: 영속성 컨텍스트와 전혀 관계 없음 - 영속: 영속성 컨텍스트에 저장된 상태 - 준영속: 영속성 컨텍스트에 저장되었다가 분리된 상태 - 삭제: 삭제된 상태 - 트랜잭션을 커밋하는 순간 영속성 컨텍스트 ---> DB 반영 - JPA에서 엔티티 저장할 때 연관된 엔티티는 모두 "영속" 상태여야함!
- JPA 꿀팁
-
data.sql을 통해 애플리케이션
- 연관 관계 - 객체: 단방향, 테이블: 양방향 - 객체를 양방향 연관관계로 둔다면, "연관 관계의 주인"을 찾아줘야 함 - 연관 관계는 "다대일", "일대다", "일대일", "다대다"
- 다대일
@Entity public class Line { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; } @Entity public class Station { @Id @GeneratedValue() private Long id; @Column(nullable = false) private String name; @ManyToOne //다대일 매핑 정보 제공 @JoinColumn(name = "line_id") //칼럼 이름과 외래 키가 참조할 칼럼 private Line line; }
- 일대다 + 양방향 매핑
@Entity public class Line { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @OneToMany(mappedBy = "line") //mappedBy를 통해 Station이 어떤 녀석이 FK를 가지고 있는지 말해줌 private List<Station> stations = new ArrayList<>(); } @Entity public class Station { @Id @GeneratedValue() private Long id; @Column(nullable = false) private String name; @ManyToOne //다대일 매핑 정보 제공 @JoinColumn(name = "line_id") //칼럼 이름과 외래 키가 참조할 칼럼 private Line line; }-
@OneToMany를 통해 mappedBy,,, 연관관계의 주인의 어떤 필드가 해당 FK를 관리하는지 알려줌 - mappedBy가 없다면 Line-Station 연관관계 테이블 만들어서 관리
- 연관 관계의 주인?
- 객체에서 양방향 연관관계는 없어
- 단방향 2개로 이루어진 것
- 지하철역(다) - 노선(일)
- 노선(일) - 지하철역(다)
- "외래키" 관리자!!
- KEY를 가지고 있는 사람!
- 주로 "다" 쪽이 FK를 가지고있어
- "연관 관계의 주인"만이 DB 연관 관계와 매핑되고 외래 키를 등록/수정/삭제 가능
- 주인 아닌 쪽은 읽기만 가능
@Test void fkRuler() { final Line line = lineRepository.findByName("3호선"); final Station 대화역 = stationRepository.save(new Station("대화역")); line.getStations().add(대화역); }- 해당 테스트에서 Line 같은 경우 연관관계의 주인이 아니야!! - 따라서 getStations().add() 이렇게 해도 Station에 line_id를 부여해줄 능력이 없어!- 양방향 연관 관계에서는 결국 양쪽 다 신경 써줘야함!
java public void addStations(Station station) { station.setLine(this); stations.add(station); } public void setLine(Line line) { if (Objects.nonNull(this.line)) { this.line.getStations().remove(this); } this.line = line; line.getStations().add(this); }
- FK를 쥐여주는 두 가지 방법
- 다(Station):1(Line) 에서 Station이 JoinColumn으로 가지고 있는 경우
@Entity public class Line { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; } @Entity public class Station { @Id @GeneratedValue() private Long id; @ManyToOne //다대일 매핑 정보 제공 @JoinColumn(name = "line_id") //칼럼 이름과 외래 키가 참조할 칼럼 private Line line; }- 1(Member):다(Favorite) 에서 Member가 JoinColumn으로 가지고 있는 경우 - 이러면 Favorite 저장하고, "update" 쿼리를 통해 Favorite에 member_id 우겨 넣음
@Entity public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @OneToMany @JoinColumn(name = "member_id") //Favorite에 member_id FK 추가 private List<Favorite> favorites = new ArrayList<>(); } @Entity public class Favorite { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; }
- 일대일 연관관계 - 소스 엔티티 <-> 타킷 엔티티 - FK를 어느곳에 두어야 할까?
- CASCADE - 상황에 필요하게 테스트 써가면서 차츰차츰 추가