Open skarltjr opened 3 years ago
-emf = entitymanagerFactory
• 1차 캐시 - 엔티티를 조회 시 조회하고자하는 엔티티가 이미 영속성 컨텍스트에 올라가있다면 db까지 접근하지 않고 영속성컨텍스트에 있는 것을 반환 -> 효율적 • 동일성(identity) 보장 -> 1차 캐쉬 덕분에 추가적으로 동일성도 보장 • 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind) - commit을 할 때 한번에 • 변경 감지(Dirty Checking)
디폴트 생성자필수
디폴트 생성자의 이유 = 하이버네이트 등의 프레임워크에서 리플렉션을 통해 해당 객체를 생성할 때 필요
자바의 Reflection은 JVM에서 실행되는 애플리케이션의 런타임 동작을 검사하거나 수정할 수 있는 기능이 필요한 프로그램에서 사용.
DDL = Data Definition Language
운영 장비에는 절대 create, create-drop, update 사용하면 안된다. • 개발 초기 단계는 create 또는 update • 테스트 서버는 update 또는 validate • 스테이징과 운영 서버는 validate 또는 none
DDL 생성기능 : DDL을 자동생성할 때만 사용되고 JPA 실행로직에는 영향X
제약조건 @Column(nullable = false, length = 10) ...
@Enumerated : 절대 Ordinal 사용 x -> EnumType.STRING사용 -> Ordinal은 이름이아닌 순서index로 저장하다보니 꼬이기 매우쉬움
@Transient : 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용
테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다
객체는 참조를 사용하여 연관된 객체를 찾는다.
객체 연관관계 2개 -A->B 단방향 1개 + -B->A 단방향1개
테이블연관관계 1개 -A<->B양방향
-단방향 매핑만으로도 이미 연관관계매핑은 완료 -조회를 양방향으로 가능토록하기위해
-둘 중 한명이 주인이되어 외래키를 관리 -> 주인쪽에서만 수정,등록등이 가능->반대쪽은 읽기만 가능
-순환참조 주의 - toString(), lombok, JSON 생성 라이브러리
단방향
양방향
• 일대다 단방향은 일대다(1:N)에서 일(1)이 연관관계의 주인 • 테이블 일대다 관계는 항상 다(N) 쪽에 외래 키가 있음 • 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하 는 특이한 구조 • @JoinColumn을 꼭 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함(중간에 테이블을 하나 추가함)
외래키가 다 쪽 (즉 다른 테이블에있다)
연관관계 관리를위해 update sql을 추가적으로 실행
따라서 일대다보단 다대일 !!!
일대다의 양방향
공식적으로 존재x - 알댜더 양방향보단 다대일의 양방향을 사용하자
주 테이블이나 대상 테이블 중 외래 키 선택가능
외래키에 db 유니크제약 제약조건 추가
주 테이블에 외래 키 • 주 객체가 대상 객체의 참조를 가지는 것 처럼 주 테이블에 외래 키를 두고 대상 테이블을 찾음 • 객체지향 개발자 선호 • JPA 매핑 편리 • 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능 • 단점: 값이 없으면 외래 키에 null 허용
대상 테이블에 외래 키 • 대상 테이블에 외래 키가 존재 • 전통적인 데이터베이스 개발자 선호 • 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지 • 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됨
• 연결 테이블이 단순히 연결만 하고 끝나지 않음 • 주문시간, 수량 같은 데이터가 들어올 수 있음 - 추가적인 필드 변경이 x된다 = 외래키가 연결테이블에 존재하기때문
상속관계 매핑
관계형 데이터베이스는 상속관계 x
대신 슈퍼타입 -서브타입이라는 모델링기법이 존재
슈퍼타입 - 서브타입 논리모델을 실제 물리모델로 구현하는 방법 • 각각 테이블로 변환 -> 조인 전략 • 통합 테이블로 변환 -> 단일 테이블 전략 • 서브타입 테이블로 변환 -> 구현 클래스마다 테이블 전략
• JOINED: 조인 전략 • SINGLE_TABLE: 단일 테이블 전략 • TABLE_PER_CLASS: 구현 클래스마다 테이블 전략 -> 사용 xxxxxx + • @DiscriminatorColumn(name=“DTYPE”) • @DiscriminatorValue(“XXX”)
조인전략
단일 테이블 전략
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="dtype")
@Getter @Setter
public abstract class Item
{
@Id @GeneratedValue
@Column(name="item_id")
private Long id;
private String name;
private int price;
private int stockQuantity;
}
@Entity
@DiscriminatorValue("B")//싱글테이블이라서 무보클래스에서 dtype했으니 뭘로 구분할지
@Getter @Setter
public class Book extends Item{
private String author;
private String isbn;
}
• 실제 클래스를 상속 받아서 만들어짐 • 실제 클래스와 겉 모양이 같다. • 사용하는 입장에서는 진짜 객체인지프록시 객체인지 구분하지 않고 사용하면 됨(이론상) • 프록시 객체는 실제 객체의 참조(target)를 보관 • 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드 호출 • 프록시 객체는 처음 사용할 때 한 번만 초기화 • 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초 기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능 • 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함 (== 비 교 실패, 대신 instance of 사용)
추가로
@NamedEntityGraph(name = "Account.withTagsAndZones",attributeNodes = {
@NamedAttributeNode("tags"),
@NamedAttributeNode("zones")
})
@Entity @Builder
@Getter @Setter
@NoArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Account {
@Id @Column(name = "account_id")
@GeneratedValue
private Long id;
@Email
@Column(unique = true)
private String email;
@Column(unique = true)
private String nickname;
private String password;
..
..
..
@ManyToMany
private Set<Tag> tags = new HashSet<>();
@ManyToMany
private Set<Zone> zones = new HashSet<>();
• 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들도 싶을 때
• @Embeddable: 값 타입을 정의하는 곳에 표시 • @Embedded: 값 타입을 사용하는 곳에 표시 • 기본 생성자 필수
• 재사용 • 높은 응집도 • Period.isWork()처럼 해당 값 타입만 사용하는 의미 있는 메소드를 만들 수 있음 • 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존함
수정할 수 없도록 setter를 막는다.
대신 처음 생성할 때 생성자 파라미터로
값 타입 컬렉션
값 타입은 엔티티와 다르게 식벽자가 없고 -> 값이 변경되면 추적이 매우 어렵다
따라서 일대다 관계를 고려
String jpql = "select m From Member m where m.name like ‘%hello%'";
List<Member> result = em.createQuery(jpql, Member.class).getResultList();
https://jojoldu.tistory.com/457를 참고하여 학습 후 적용
-> 여행 예산 프로젝트 검색 기능 구현 중 발생 -> 하나의 여행 계획(plan)에는 여러개의 post가 존재한다 -> 검색은 공개된 plan 중 plan의 타이틀에 keyword가 포함된 모든 여행들을 검색 ( 가격 검색은 현재 상황에서 제외) -> 여기서 plan 하위의 여러 post 중 post의 타이틀이 keyword를 포함한 plan도 검색에 포함시켜보고 싶었다. -> plan과 post 양방향 건 후 -> 모두 fetch join 적용
-> jpa에서 fetch join은 ~ToMany의 경우 1개만 가능
-> 문제 해결을 위해 spring.jpa.properties.hibernate.default_batch_fetch_size=1000
를 사용 후 잘 동작하는 것을 확인
-> 당연히 나머지 부분에서는 가능한 최적화는 수행하기! (위처럼 2개 이상의 ~ToMany에 fetch join이 필요할 때 batch_fetch_size 설정 후 둘 중 좀 더 효율적이라고 판단되는 곳에 fetch join을 설정하고 나머지는 설정을 활용하는 쪽으로.) -> 이 때 여기서 나는 planTag + tag쪽에 fetch join을 설정하는 것이 더 좋다고 판단하여
Transactional(readOnly=true)
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "team_id", nullable = false)
JPA인덱스 정의
@Entity
@Table(
name = "files",
indexes = [Index(name = "file_url_index", columnList = "url")]
)
class File(
@field:Id
@field:GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
@field:Column(name = "url", nullable = false)
val url: String,
@field:Column(name = "ext", nullable = false)
val ext: String
) {
}
코틀린에서의 jpa와 lazy loading
김영한님 강의를 통해 학습
jpa . 왜 사용하는가
그러나 객체와 관계형 데이터베이스는 차이가 존재 / 데이터베이스로 커버하기 어려운 영역이 존재
-> 객체를 테이블에 맞춰(sql중심적) 모델링하게된다. -> ex) 객체는 참조
member.getTeam();
/단방향만 가능
-> 반면 sql에서는 외래키(FK)를 사용하여 Join 쿼리를 통해 연관 관계를 찾는다 -> Ex)JOIN ON M.TEAM_ID = T.TEAM_ID
-> 양방향으로 모두 조회가 가능하다. 즉, 단방향이 존재하지 않는다객체를 sql중심적으로가 아닌 자바 컬렉션에 저장하듯이 db에 저장할 순 없는가?
JPA Java Persistence API : 자바 진영의 orm 기술 표준
ORM : object -relational mapping
객체는 객체대로 설계
관계형 데이터베이스는 관계형 데이터베이스대로 설계
ORM 프레임워크가 중간에서 매핑
![화면 캡처 2021-01-23 010043](https://user-images.githubusercontent.com/62214428/105514114-79db2200-5d16-11eb-9504-bd248b838904.png)
결론적으로 왜 jpa를 사용하는가?
JPA의 성능 최적화 기능
jpa는 특정 db에 종속적이지 않다.
jpa의 모든 데이터 변경은 트랜잭션 안에서 수행