Data

[1028 JPA ]

JPA 프로그래밍 입문 초반에 읽어서 조금 이해가 가기시작했는데 내용이 주관적인부분이 보여 자바ORM 표준 JPA 프로그래밍 으로 갈아탔습니다. 대부분의 내용이  "자바ORM 표준 JPA 프로그래밍 " 내용입니다. 

 

내용 출처 :
http://mobile.kyobobook.co.kr/showcase/book/KOR/9788960777330?OV_REFFER=https://www.google.com/

 

자바 ORM 표준 JPA 프로그래밍 - 교보문고

스프링 데이터 예제 프로젝트로 배우는 전자정부 표준 데이터베이스 프레임 | ★ 이 책에서 다루는 내용 ★■ JPA 기초 이론과 핵심 원리■ JPA로 도메인 모델을 설계하는 과정을 예제 중심으로

www.kyobobook.co.kr


http://www.yes24.com/Product/Goods/41787023

 

JPA 프로그래밍 입문 - YES24

JPA는 Java Persistence API의 약자로 자바 ORM 스펙(specification)이다. JPA는 하이버네이트를 개발한 가빈 킹(Gavin King)이 주도적으로 참여하여 만든 표준으로 1.0과 2.0을 거쳐 현재 2.1 버전까지 나온 상태이

www.yes24.com

JPA 기본개념. 

JPA 기본개념. 
JPA Java Persistence API
Java진영 ORM 기술 표준.

Entity 분석, SQL 생성, JDBC API 사용, 패러다임 불일치 해결.

하이버네이트 개발한 가빈 킹 이 주도적으로 참여 만든 표준 .
JPA 1.0 2006년 : 초기버전, 복합키, 연관관계기능 부족
JPA 2.0 2009년 : 대부분의 ORM 기능 포함하고 JPA Criteria가 추가
JPA 2.1 2013년: 스토어드 프로시져 접근, 컨버터, 엔티티 그래프 기능 추가. cf) java8 2014년, Hibernate5 2017년
JPA 2.4 의 hibernate 5.4.31(2021)

장점
생산성, 유지보수, 패러다임 불일치 해결, 성능(재사용)
데이터 접근 추상화, 벤더 독립성(다른종료 DB사용가능)

1. SQL 기존 문제점 : 반복(CRUD 계속 작성), SQL의존적 개발(필드 수정시 전체 수정, 엔티티를 신뢰할수없다), JPA와 문제해결.
2. 패러다임 불일치 : 상속(persist 1회로 해결), 연관관계(FK), 객체 그래프 탐색(지연로딩), 비교(find())
JPA 구현체 = JPA 프로바이더 = ORM 프레임워크
Hibernate 하이버네이트 << 이거 보통 사용.
EclipseLink 이클립스링크
DataNucleus 데이터 핵심(데이터 뉴클리어스)
* provider 제공자.
jpa 사용 핵심 라이브러리
hibernate-core.jar
hibernate.jpa2.1.api.jar

코드구성
1. 엔티티 메니저 팩토리 설정(EntityManagerFactory)
- 설정정보조회(persistence.xml) -> 생성(Persistence) ->생성(EntityManagerFactory) ->EntitiyManager
- 커넥션 풀도 생성하므로 비용이 아주큼, 애플리케이션 전체에서 딱한번 생성하고 공유해서 사용.
- 엔티티 매니저는 데이터베이스 커넥션과 밀접한 관계, 스레드간 공유하거나 재사용 안됨.
-사용이 끝난 엔티티 매니저는 반드시 종료, 인티티 매니저 팩토리도 반드시 종료.
2. 트랜젝선관리
3. 비지니스 로직
.persist()
.setXXX(value) :update : 변경감지(dirty checking): 플러시 호출->엔티티 스냅샷 비교=>변경되면 수정쿼리 쓰기지연 SQL 저장소 보냄. =>데이터베이스 보냄=>데이터베이스의 트랜잭션 커밋. 영속상태에서만 적용됨.
.remove()
.find(XX.class, id)
persistence context jpa가 관리하는 엔티티 객체 집합,
엔티티객체 DB 반영, DB에서 읽어온 데이터 어플리케이션에 제공
영속 콘텍스트에 보관된 객체를 persistent object 라고 부른다.
세션 단위로 생김

응용프로그램(엔티티객체) -> (EntityManger )-> 영속컨텍스트(영속객체) -> DB(레코드)
응용프로그램(엔티티객체) <- (EntityManger )<- 영속컨텍스트(영속객체) <- DB(레코드)

영속 컨텍스트 : 엔티티 타입 +식별자 키로 사용하는 일종의 보관소.
JEE 컨테이너를 사용하면 실행환경에 따라 여러 EntityManager 가 한 개의 영속 컨텍스트를 공유할수도 있다. 캐시 공유.

1. EntityManagerFactory emf = Persistence.createEntityManagerFactory() ;
EntityManager em = emf.createEntityManager(); : EntityManager 생성
2. ENtityTransaction trans = em.getTransaction();
trans.beging() : 트랜잭션 시작
3. User user = new User();
em.persist(user); :EntityManager를 통해 영속 컨텍스트에 객체 추가하거나 구한다.
4. trans.commit(); / trans.rollback() :트랜젝션 커밋
5. em.close() : EntityManager 닫기.


User user = entityManager.find(User.class, "A@A.com"); user 엔티티의 아이디 A@A.com 찾기

 

환경설정 & 기본 코드
apllication.xml 전용속성
hibernate.show_sql :SQL 출력
hibernate.format_sql: 자동정렬 출력
hibernate.use_sql_commentss: 주석도 같이 출력
hibernate.id.new_generator_mapping: false(default) : jpa 표준에 맞춘 새로운 키 생성 전략을 사용. allocationSize 속성사용바뀜
hibernate.hbm2ddl.auto :
- create(스키마 자동생성)
- create-drop(create속성 + 종료후 생성한 DDL 제거 : drop +create+drop)
- update(변경사항만 수정)
- validate(변경사항 있으면 경고를 남기고 어플리케이션 실행안함)
- none(자동 생성기능을 사용하지 않음, 속성자체를 삭제 or 유효하지 않은 값 입력.)
jpa.hibernate.naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl : 카멜 -> 스네이크
spring.jpa.hibernate.use-new-id-generator-mappings =true : 키 생성 전략시, false 일 겨우 기존 하비어네이트 시스템 유지보수. true 설정시 allocatonSize 속성 사용방식 달라짐.
** 운영주의 사항 : create, create-drop, update 옵션 사용 불가 개발단계에서만
*개발시 초기 create, update, 자동화테스트 ci서버 create or create-drop, 테스트 서버 update or validate, 스테이징, 운영은 validate or none
JPA2.1부터 스키마 자동생성기능 표준으로 지원. 하지만 update, validate 옵션은 지원하지 않음. ????


JPA 어노테이션 패키지 위치
import javax.persistence.*;

JPA 실행 위한 기본 설정파일
persistence.xml
- 구현체들이 자동으로 엔티티 인식못할때 설정가능, JPA 표준속성, 하이버네이트 속성 외
- persistence-unit : DB 이름 그와 관련 DB 설정
커넥션 풀 설정 spring.datasource.hikari.connection-timeout = 20000 : 최대 커넥션 수n
spring.datasource.hikari.minimum-idle= 10 : #HikariCP 사용 유지 최소 커넥션 수
spring.datasource.hikari.maximum-pool-size= 10 : 최대 풀 사이즈
spring.datasource.hikari.idle-timeout=10000 : 최대 유휴(대기)시간
spring.datasource.hikari.max-lifetime= 1000 : 종료 후 최대 연결 시간
spring.datasource.hikari.auto-commit =true : 자동 커밋 기본설정


https://www.javadevjournal.com/spring-boot/spring-boot-hikari/#:~:text=a%20connection%20spring.-,datasource.,in%20a%20connection%20pool%20spring.
방언 dialect 방언 표준 아닐때 사용
spring.jooq.sql-dialect
https://wannaqueen.gitbook.io/spring5/spring-boot/undefined-1/31.-sql-by-ys/31.6-jooq

데이터 타입
- 가변문자 타입 MySQL(VARCHAR), 오라클(VARCHAR2)
- 숫자 타입 MySQL(integer), 오라클(Number)
다른 함수명
- SQL표준(SUBSTRING()), 오라클(SUBSTR())
페이징 처리
- MySQL(LIMIT), 오라클(ROWNUM

Hibernate- 현재 45개 데이터베이스 방언 지원. )
엔티티 엔티티
(사전) 독립체, 인간이 생각하는 개념 또는 정보의 세계에서는 의미있는 정보의 단위

엔티티 매니저 팩토리
- DB 갯수만큼
- 엔티티 매니저 팩토리는 스레드 세이프, 엔티티 매니저는 동시성 문제, 스레드간 공유 안됨.
- 하는일
1. 에플리케이션에서 관리- 직접 생성, 종료
2. 컨테이너에서 관리 - JBoss EAP, 웹로직, TomEE와 같은 JEE 컨테이너

영속성 컨텍스트
- 엔티티를 영구 저장하는 환경.
- 개념적, 엔티티 매니저 생성할 때 하나 만들어짐, 엔티티 매니저를 통해서 영속성 컨텍스트에 접근가능, 관리
- 기본적으로 하나의 엔티티 매니저에 하나의 영속성 컨텍스트 만듬.


* 엔티티매니저 팩토리 코드
public T find(Class entityClass, Object primaryKey);
먼저 1차 캐시에서 엔티티를 찾고 만약 찾는 엔티티가 1차 캐시에 없으면 데이터베이스에서 조회 한다.
존재하지 않으면 null
1차캐시 - 어플리케이션 수준의 캐시.
조회 -> 1차캐시없으면 데이터베이스 조회->조회 데이터로 엔티티 생성, 1차 캐시 저장-> 조회한 엔티티 반환
==>동일성, 동등성 모두 같은이유 , Repeatable read 등급 트랜잭션 격리 수준을 제공

public T getReference(Class entityClass, Object primaryKey);
존재하지 않으면 EntityNotFoundException
쿼리를 바로 실행하지 않고 프록시 객체를 리턴, 프록시 객체는 최초 데이터가 필요한 시점에 select 쿼리를 실행.
세션 범위 밖에서 첫 실행시 ({}) getFiled 실행시 LazyInitializationException -no Session 발생

public void persist(Object entity);
쓰기지연(transactional write-behind) : 트랜젝션 커밋 직전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쿼리 저장소에 insert 쿼리를 모아둠.


@PersistenceContext
EntityManager em; --JEE 관리하는 트랜젝션 참여.
엔티티 생명주기 엔티티 생명주기
비영속 new/transient : 영속성 컨텍스트와 전혀 관계가 없는 상태 , 객체만 생성했고 아직 저장안함.
- Member member = new Member();
영속 managed: 영속성 컨텍스트에 저장된 상태
- em.persist(member);
- em.find()
- jpql 사용 상태
준영속 detached : 영속성 컨텍스트에 저장되었다가 분리된 상태
- em.detach()
- em.close();
- em.clear() ; 영속성 초기화
삭제 removed : 삭제된 상태
- em.remove(member);

영속성 컨텍스트 특징
1. 식별자 존재
2. 보통은 트랜젝션 커밋할때 데이터베이스 반영(flush)
3. 장점: 1차 캐시, 동일성 보장, 트랜잭션을 지원하는 쓰기 지연, 변경감지(update시 dirty checking), 지연로딩

동적 update, insert
@org.hibernate.annotations.DynamicUpdate
@Dynamic Insert
컬럼이 30개 이상될때 정적수정쿼리보다 빠름. 컬럼 많을때 고려.

플러시
영속성 컨텍스트의 변경 내용을 데이터베이스에 "동기화" 하는 작업.
변경감지동작, 영속성 컨텍스트에 있는 모든 엔티티 스냅샷과 비교, 수정된 엔티티를 찾는다. 수정쿼리 SQL 젖아소, 후에 데이터베이스 전송
호출 방법: 직접호출(em.flush()), 트랜잭션 커밋 시 플러시 자동호출, JPQL & Criteria 객체지향 쿼리 실행시 플러시 자동 호출
e.setFlushMode(FlushModeType.AUTO);
FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시(기본값)
FlushModeType.COMMIT: 커밋할때만 플러시

준영속
영속->준영속
1차 캐시, 지연 SQL 저장소, 변경감지, 지연로딩, 해당 엔티티를 관리하기 위한 모든정보 제거.
식별값 가짐
em.detach()
: 특정 한 엔티티 영속성 컨텍스트 관리하지마!
em.clear();
: 해당 엔티티 영속성 컨텍스트 관리하지마! , 수정해도 DB 영향 없음
em.close()
:개발자가 실제 사용하는경우 드뭄

병합
준영속-> 영속
em.merge(member);
새로운 영속 상태의 엔티티를 반환.
cf) 영속성여부(엔티티관리여부) : em.contains(member)

비영속 병합
해당 엔티티가 없으면 생성해서 병합. 있으면 업데이트.

*context : 맥락, 문맥
엔티티 맵핑 @Entity
테이블과 매핑되는 기본 단위.
- name : jpa에서 사용할 엔티티 이름.
- 기본생성자 필수(public or protected)
- final 클래스, enum, interface, inner 클래스에서 사용불가.
- 저장할 필드에 final 사용하면안된다.

@Table
클래스가 어떤 테이블과 매핑되는지 설정
- name :매핑할 테이블이름
- catalog : catalog 기능있는 데이터베이스에서 매핑
- schema
- uniqueConstraints : DDL 생성시 유니크 제약조건.
uniqueConstraints={@UniqueConstraint(name="NAME_AGE_UNIQUE", columnNames={"NAME", "AGE"})}

@Id

엔티티 식별자
- 직접할당
자바 기본형, 자바 래퍼형, String, java.util.Date, java.sql.Date, java.math.BigDecimal, java.math.BigInteger
- IDENTITY 전략:자동 키생성 : 주로 MySQL,, PostgreSQL, SQL Server, DB2에서 사용.
@GeneratedValue(strategy=GenerationType.IDENTITY)
DB 식별 컬럼 사용해서 식별자 생성:persist() 저장 시점 insert 쿼리 실행, 쓰기지연 동작안함.
spring.jpa.hibernate.use-new-id-generator-mappings =true
- SEQUENCE 전략 : 데이터베이스 시퀀스 사용 : 오라클, PostgreSQL, DB2, H2
@SequenceGenerato(name="review_seq_gen", sequenceName="hotel_review_seq", allocationSize=1)
@GeneratedValue(generator="review_seq_gen")
persist() 저장 시점 시퀀스 추가 쿼리 실행-> 트랜젝션 commit 시 insert
allocationSize 기본값 50 : 시퀀스 전략. 50개를 메모리에 미리 할당받아 호출수를 줄임.
단 두개 이상 jvm 을 사용하면 충돌 우려 allocationSize =1 사용하여 해결.
- TABLE 키생성 테이블 사용 :
@TableGenerator(name="idgen", table="id_gen", pkColumnName="entity", pkColumnValue="city", valueColumnName="nextid", initialValue=0, allocationSize=1)
GenerateValue(generator="idgen")
1. id_gen테이블에서 식별자 구함. 레코드 존재 않을 때 insert 실행, 다음식별자 id_gen 반영
-AUTO전략
@GeneratedValue(strategy=GenerationType.AUTO) 오라클 sequence, mysql identity를 사용
* 자연키 : natural key, : 비즈니스에 의미가 있는 키 ex) 주민번호, 이메일, 전화번호
* 대리키 : surrogate key : 비즈니스와 관련없는 임의로 만들어진 키, 대체키,ex) 오라클 시퀀스, auto_increment, 키생성 테이블 사용.
대리키 권장


@Basic
보통 생략되어 사용, int, long, String과 같은 기본타입, 래퍼타입, BigInteger, BigDecimal, Calendar, Time, Timestamp, enum, 상기의 배열

@Timestamp

java.util.Date 사용시
@Temporal(TemporalType.TIMESTAMP)
java.sql.Timestamp , java.util.Date로 변환
DATE : java.sql.Date
TIME : java.sql.Time
TIMESTAMP : java.sql.Timestamp : Mysql : datetime, H2,오라클, postgreSQL : timestamp
하이버 네이트 5.2 LocalDateTime 사용

@Column
없으면 타입명과 컬럼값 같음
- name
- nullable=false
- length=10
- insertable=false :면 id값 빼고 insert
- updatable=false : 면 update 대상 제외
-table 하나의 엔티티 두개이상 테이블 매핑.
- unique
-columnDefinition
- precision, scale : BigDeciaml 타입에서 사용한다. BingInteger도 사용가능, precision 소숫점포함 전체 자릿수, scale 소수의 자릿수 아주 큰숫자, 정밀 한소수 다루어야 할때 사용.

@Enumerated(EnumType.STRING)
STRING 설정안하면 기본타입으로 저장.
@Lob
필드길이 제한 없음, CLOB: 필드 타입 문자 BLOB : 나머지

@Access(AccessType.PROPERTY) 필드 상단
프로퍼티 접근방식, get, set 메서드 통해서 접근 == @id 를 get 메소드 선언 과 같음
@Access(AccessType.FIELD) get 메서드 상단
필드 접근방식 set 메서드를 사용하지 않음. == @id 를 필드에 선언 과 같음
* 필드에 id 를 선언하고 메서드에 @Access 프로퍼티 선언 하면 동시사용 가능.

@transient
transient private long timestamp = System.currentTimeMillis();
사전 일시적인, 트랜션트
영속대상 제외

기본생성자
필수, public or protected

final class 불가.
프록시 객체를 생성 부락.

Serializable 인터페이스
캐시 구현기술 사용시. 필요

exclude-unlisted-classes true 시 자동추가 비활성화

cf) 필드에서만 쓸수있는게 아니라 get 메소드에 선언가능
트랜젝션 ACID : Atomic 원자성, Consistency 일관성, Isolation 격리성, Durability 지속성
Read Uncommited   커밋되지 않는 읽기 : 커밋안된 데이터  다른트렌젝션 update, insert, delete 읽기
                                Dirty reads :  Rollback 시 정합성 깨짐
Read Commited       커밋된 읽기 :  다른 트랜젝션  update insert delete 읽기
                                Non-Repeatable read :  업데이트 영향으로 첫번째 조회 값과 두번째 조회값 다름
Repeatable Read     반복 읽기 : 다른 트랜젝션 insert, delete 읽기
                                 Phantom read :  insert, delete 영향으로 첫번째 조회 값과 두번째 조회가 있다없음
Serilization                직렬화 : 락을 걸어 트랜잭션 이용하는 부분 사용 못함. 병렬성 저하. 


JPA 낙관적 락 : 트랜잭션 충돌이 거의 발생하지 않는다. 커밋 되는 시점에 충돌여부 알수있다. 
LockModeType.NONE : 커밋시 version정보 update 쿼리로 : 두번의 갱신문제 해결
                                    > 조회 ~ 수정 트랜잭션 격리
LockModeType.Optimistic:  커밋시 version 정보를 select 쿼리로 보냄:   dirty read, Non-Repeatable Read 해결                    +스칼라 타입 으로 조회 시 소프트락 적용안됨 ?? 스칼라타입: 단하나, <->컴포지트
Optimistic.Force_increment : 커밋시 version 정보를 update  쿼리로 : 연관관계 엔티티 함께 버전 관리

JPA 비관적 락 : 타임아웃. 
PESSIMISTIC_WRITE
PESSIMISTIC_FORCE_INCREMENT

출처 : https://ojt90902.tistory.com/723

RESOURCE_LOCAL

: 변경내용 영속 컨텍스트에 보관, 트랜잭션을 커밋하는 시점에서 영속 컨텍스트로 추적한 변경을 DB에 반영.

JTA 트랜잭션 타입.
Java Transzction API
UserTransaction utx = (UserTransaction)new InitaialContext().lookup("java:comp/UserTransaction");
utx.begin();
EntityManager em = emf.createEntityManager();
em.joinTransaction; (이부분은 필히 작성안해도됨. )

utx.commit()
utx.rollback()
@JoinTable 조인테이블 = 연결 테이블 = 링크 테이블 
다대다 관계 설정. 
cf) 연관관계 설정 - @JoinColumn or JoinTable

일대일, 일대다, 다대일, 다대다

벨류 객체 한개의 값, 다른 벨류 객체와 구분하기 위한 식별자를 갖지 않는다.
자신만의 라이프 사이클을 갖지 않는다. 자신이 속한 객체가 생성될 때 함께 생성/삭제시 삭제

생성시 모든 프로퍼티를 파라미터로 전달받는다.
읽기전용 프로퍼티만 제공한다.
equals() 메서드를 재정의 한다.
각 프로퍼티의 값을 이용, 해시코드를 생성하도록 hashCode()메서드를 재정의 한다.

public class ValueObject{
private String a;
private String b;
private String c;

public ValueObject(String a, String b, String c){
this.a = a;
this.b = b;
this.c = c;
}

public String getA(){return a;}
public String getB(){return b;}
public String getC(){return c;}

@Override
public boolean equals(Object o){
if(this==o) return true;
if(o==null || getClass() !=o.getClass()) return false;
ValueObject vo = (ValueObject)o;
return Objects.equals(a, vo.a) && Objects.equals(b, vo.b) & Objects.equals(c, vo.c);
}

@Override
public int hashCode(){
return Objects.hash(a,b,c);
}
}
@Embeddable
@Embedded
@AttributeOverrides
@secondaryTable
@Embeddable
public class ValueObject{
}

@Entity
public class UsingObject{

@Id
private String id;

@Embedded
privae valueObject;
}

매핑 대상이 되도록 설정. update 시 트랜젝션 내에서 변경되면 변경내역 DB 반영

@Embedded
@AttributeOverrides({
@AttributeOverride(name..., column=@Column(name="valueObject")), @AttributeOverride ...})
private Address valueObject2;

동일 테이블 사용하는 밸류클래스 이름 재정의 사용

@Entity
@SecondaryTable(
name="sight_detail",
pkJoinColumns=@PrimaryKeyJoinColumn(name="sight_id",refferencedColumnName="id" )
)
public class Sight{

@Embeded
@AttributeOverrides({
@AttributeOverride
})
private SightDetail detail;
}

복합키 사용 벨류테이블
1. equals()메서드, hashCode() 메서드 재정의
2. Serializable 인터페이스 상속.
(3. 생성자. null 이면 IllegalArgumentException. )
@Entity
@public class MonthChage{
@Id
privateMonChageId id;
}
@Embeddable
public classMonChagedId implements Serializable{
@Column

}

 

방향 & 다중성 & 연관관계 주인
설명  방향 :direction : 단방향, 양방향
다중성 :multiplicity: 다대일(@ManyToOne), 일대다(@OneToMany), 일대일(@OneToOne), 다대다(@ManyToMany)
연관관계 주인 :owner: 양방향 연관관계의 주인

@JoinColumn
name : 매핑할 외래 키 이름
referencedColumnName :외래키 참조하는 대상 테이블 컬럼명
foreignKey : 외래키 제약조건. 테이블 생성시만 사용
그외 columne 속성과 같음

@ManyToOne : 다대일 관계 사용
optional : false : 항상 연관된 테이블 있음.
fetch : 글로벌 패치 전략 @ManyToONe = FetchType.EAGER, @OneToMany=FetchType.LAZY
cascade : 영속성 전의 기능
targetEntity : 연관된 엔티티 타입 정보 설정, 거의 사용안함.
@OneToMany
private List members;
@OneToMany(targetEntity=Member.class)
private List members;


단방향
회원 - 팀.
객체 그래프 탐색 : 참조를 통해서 연관관계를 탐색.  ex) member.getTeam()
저장 , 수정
em.persist(team1)
member1.setTeam(team1)
em.persist(member1)
조회
member.getTeam()  : 객체 그래프 탐색
삭제
Member member1 = em.find(Member.class, "member1");
member1.setTeam(null);  ///1. 연관관계  모두 제거.
em.remove(team)               ///2. 해당 엔티티 삭제

양방향
연관관계의 주인은 테이블에 외래키가 있는곳.
** 양방향 연관관계의 주의점 : 객체 관점에서 양쪽방향에 모두 값을 입력해주는 것이 가장 안전.
** 무한루프 주의 toString 시 member, team 에서 서로 호출. Lombok 사용시 필히.
em.persist(team1)
member1.setTeam(team1)                     // 객체 , 실제 데이터 저장
team1.getMembers().add(member1)  //객체 정보 저장
em.persiste(member1)
+)
public void setTeam(Team team){
  if(this.team !=null) this.team.getMembers().remove(this);
  this.team = team;
  team.getMembers().add(this);
}
조회
List<Member> members = team.getMembers();


cf)
JPQL
단방향 연관관계 : 다대 일
String jpql = "select m from Member m join m.team t where t.name=:teamName";
List resultList = em.createQuery(jpql, Member.class).setParameter("teamName", "팀1").getResultList();
다중성 DB ERD 객체 단방향 객체 양방향
다대일
@ManyToOne
연관관계맵핑 @ManyToOne
@JoinColumn(name="TEAM_ID")
privae Team team;   //cf) 0..1   : zero to one 0~1맵핑
@OneToMany(mappedBy = "team")
private List<Member> memers;
//cf) Collection, List, Set, Map 가능
일대다
@OneToMany
연관관계맵핑 @OneToMany
@JoinColumn(name="TEAM_ID")
private List<Member> members;
@ManyToOne
@JoinColumn(name="TEAM_ID", insertable=false, updatable=false)
private Team team; //읽기전용
일대일
@OneToOne
연관관계맵핑 @OneToOne
@JoinColumn(name="TEAM_ID")
private Team team;
@OneToOne(mappedBy = "member")
private Member member;
다대다
@ManyToMany
 
연관관계맵핑 @ManyToMany
@JoinTable(name="Member_has_TEAM"
    , joinColumns = @JoinColumn(name="MEMBER_ID")
   , inverseJoinColumns = @JoinColumn(name="TEAM_ID"))
private List<Team> teams;
@ManyToMany(mappedBy ="team")
private List<Member> members;

다대다: 매핑 한계 ; 연결 엔티티 사용
연관관계맵핑 @Entity
@IdClass(MemberTeam.class)
public class MemberTeam{

  @id
  @ManyToOne
  @JoinColumn(name="MEMBER_ID")
  private Member member;

  @id
  @ManyToOne
  @JoinColumn(name="TEAM_ID")
  private Team team;

  private String desc;
}
public class MemberTeamId impliments Serializable{

  private int member;
  private int product;

  //hashCode, equals 수정.

  // 기본생성자
}


@OneToMany(mappedBy = "member")
private List<MemberTeam> memberTeam;
다대다: 새로운 기본키 사용    
  @Entity
@IdClass(MemberTeam.class)
public class MemberTeam{

  @id
  @GeneratedValue
  private Long id;

  @ManyToOne
  @JoinColumn(name="MEMBER_ID")
  private Member member;

  @ManyToOne
  @JoinColumn(name="TEAM_ID")
  private Team team;

  private String desc;
}
@OneToMany(mappedBy="member")
private List<MemberTeam> membersTeams ;



Self Join   @Column(name = "RESOLVE_GOAL") 
private Integer resolveGoal; 
@ManyToOne(fetch = FetchType.LAZY) 
@JoinColumn(name = "RESOLVE_GOAL", referencedColumnName = "RESOLVE_SN", insertable = false, updatable = false) private ResolveModel solutionResolve; 
@OneToMany(mappedBy = "solutionResolve", fetch = FetchType.LAZY) 
private List<ResolveModel> resolveSolution;
@ToString(exclude = {"resolveSolution", "solutionResolve"}, callSuper = true)

서로 호출 처리 주의 

* 0..1  : zero to one : 0~1개 맵핑

고급매핑- 상속관계 매핑 : 슈퍼타입, 서브타입 논리모델
- 조인전략
@Inheritance(strategy=InheritanceType.JOIND)
- 각각의 테이블로 변환, 타입컨럼 추가. 

@Entity
@Inheritance(strategy=InheritanceType.JOIND)
@DiscriminatorColumn(name="DTYPE")
public abstract class Item{
  @Id @GeneratedValue
  @Column(name="ITEM_ID")
  private Long id;
  //생략
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item{
  //생략
}

// 기본키 컬럼 재정의
@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name="BOOK_ID")  //ID 재정의
public class Book extends Item{
  //생략
}

장점: 테이블이 정규화, 외래키 참조 무결성 제약조건활용, 저장공간 효율적
단점 : 조회 할때 조인 많이 사용 성능저하, 조회 쿼리 복잡, 등록시 insert 두번실행. 

cf ) 구현클래스마다 테이블 전략(@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS))
- DB , ORM 양쪽 시선으로도 비효율적. 
 
- 단일 테이블 전략
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
조인이 없어 빠르고 단순하지만, 자식엔티티가 매핑한 컨럼은 모두 null 허용.  테이블이 커지면서 저하 우려. 
구분컬럼  지정하지 않으면 기본으로 엔티티이름 사용. 

@Entity() 
@Table(name="LINK") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="LINK_TYPE", length = 1) 
public class AbstractLinkModel extends BaseEntity{
 생략
@Entity 
@DiscriminatorValue("B") 
public class LinkBookModel extends AbstractLinkModel { 
- 구현클래스마다 테이블 전략

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
일반적으로 비추천. 구분컬럼 미사용, 티에블 통합 쿼리 어려움.  orm db 양쪽에서 모두 비추 
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Item{
하기 동일. 
테이블 상관없이 특정정보 자식에게 상속
@MappedSuperclass
공통사용 매핑정보 모아줌 - ex) 등록일자, 수정일자, 등록자, 수정자. 
@MappedSuperclass

public abstract class Item{
@IdClass

@IdClass  : 데이터베이스에 가까움
@Entity
@IdClass(PId.class)
public class Parent{
  @Id
  @Column(name="P_ID1")
  privae String id1;

  @Id
  @Column(name="P_ID2")
  privae String id2;

// 생략
}

public class PId implements Serializable{
  private String id1;
  private String id2;
  public PId(){} 
  public ParentId(String id1, String id2){this.id1=id1; this.id2=id2;}
  @Override
  public boolean equals(Object o){...}
  @Override
  public int hashCode(){...}
}
@EmbeddedId @Entity
public class Parent{
  
  @EmbeddedId
  private PId id;

// 생략
}

@Embeddable
public class PId implements Serializable{
  @Column(name="P_ID1")
  private String id1;
 @Column(name="P_ID2")
  private String id2;
  public PId(){} 
  public ParentId(String id1, String id2){this.id1=id1; this.id2=id2;}
  @Override
  public boolean equals(Object o){...}
  @Override
  public int hashCode(){...}
}

** equeals, hashCode  구현 안하면 예상과 다른엔티티 조회되거나 엔티티를 찾을수없어짐. 
복합키는 @GenerateValue 불가.  
식별 비식별 * 식별관계 : Identifying Relationship : 받아온 식별자를 기본키 +외래키로 사용
* 비식별관계 : Non-Identifyng Relationship : 받아온 식별자는 외래키로만 사용, 새로운 식별자 추가.  Null 허용시 선택적 비식별(Optional) , 허용안하면  필수적 비식별(Mandatory)

** equeals, hashCode  구현 안하면 예상과 다른엔티티 조회되거나 엔티티를 찾을수없어짐.
비식별 고나계를 사용하고 long 타입 대리키 사용하는것 추천. 
@IdClass
(비식별)
 데이터베이스에 가까움
@Entity
@IdClass(PId.class)
public class Parent{
  @Id
  @Column(name="P_ID1")
  privae String id1;
  @Id
  @Column(name="P_ID2")
  privae String id2;

// 생략
}

public class PId implements Serializable{
  private String id1;
  private String id2;
  public PId(){} 
  public ParentId(String id1, String id2){this.id1=id1; this.id2=id2;}
  @Override
  public boolean equals(Object o){...}
  @Override
  public int hashCode(){...}
}
@EmbeddedId
(비식별)
객체지향적, JPQL 작성시 불리할수있다. 

@Entity

public class Parent{
  
  @EmbeddedId
  private PId id;

// 생략
}

@Embeddable
public class PId implements Serializable{
  @Column(name="P_ID1")
  private String id1;
 @Column(name="P_ID2")
  private String id2;
  public PId(){} 
  public ParentId(String id1, String id2){this.id1=id1; this.id2=id2;}
  @Override
  public boolean equals(Object o){...}
  @Override
  public int hashCode(){...}
}

복합키는 @GenerateValue 사용불가
@IdClass
(식별)
@Entity
public class Parent{
  @Id
  @Column(name="P_ID")
  privae String id;
// 생략
}

public class ChidId impliments Serializable{
  private String parent; 
  private String childId;

  //equals, hashcode
}


@Entity
@IdClass(ChildId.class)
public class Child{
  @Id
  @ManyToOne
  @JoinColumn(name="P_ID")
  privae Parent parent;

  @Id @Column(name="CHILD_ID")
  private String childId;
// 생략
}
@EmbeddedId
(비식별)
@Entity
public class Parent{
  @Id
  @Column(name="P_ID")
  privae String id;
// 생략
}

@Embeddable
public class ChidId impliments Serializable{
  private String parentId;
  @Column(name="CHILD_ID")
  private String id;

  //equals, hashcode
}


@Entity
public class Child{
  @EmbeddedId
  private ChildId id;

  @MapsId("parentId")
  @ManyToOne
  @JoinColumn(name="P_ID")
  public Parent parent;
// 생략
}
일대일 식별 @Entity
public class Board{
  @Id @GeneratedValue
  @Column(name="BOARD_ID")
  private Long id;

  @OneToONe (mappedBy ="board")
  private BoardDetail boardDetail;
}

@Entity
public class BoardDetail{
  @ID
  private Long boardId;
 
  @MapsId
  @OneToONe
  @JOinColumn(name="BOARD_ID")
  private Board board
}

 

프록시와 연관관계  
em.getReference( 실제 사용 하는 시점(targer.getName() , member.getOrders().get(0))까지 데이터베이스 조회 미룸.  접근을 위임한 프록시 객체 반환. 이미 엔티티가 있으면 엔티티 반환. 
- 준영속시 에러 발생   LazyInitializationException
- 연관관계 설정시 데이터 베이스 접근횟수 줄임. 
- PersistenceUnitUtil.isLoaded(entity); 프록시 인지 엔티티인지 확인. 
즉시로딩 vs 지연로딩 즉시로딩 :EAGER: @ManyToOne(fetch=FetchType.EAGER)
지연로딩 :LAZY: @ManyToOne(fetch=FetchType.LAZY)

기본설정값
@ManyToOne, @OneToOne:  즉시로딩 EAGER
optional=false : 내부조인
optional=true : 외부조인
@OneToMany, @ManyToMany :  지연로딩. LAZY
optional=fals: 외부조인
optional=true : 내부조인 

추천은 모두 지연로딩 사용후 완료단계에서 즉시로딩으로 최적화 
컬랙션이 많을경우 즉시로딩 미권장. 
즉시로딩은 항상 외부조인 사용. 
CASECADE  
   
   



hash code
threadLocal