1. 객체와 테이블 매핑: @Entity, @Table
◍ @Entity
○ JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수
○ 주의
◼ 기본생성자 필수(파라미터가 없는 public 또는 protected 생성자)
◼ final 클래스, enum, interface, innner 클래스에는 사용X
◼ 저장할 필드에 final 사용 X
○ 속성: name
◼ 기본값: 클래스의 이름을 그대로 사용.
◼ 같은 클래스의 이름이 없으면 가급적 기본값 사용.
◍ @Table
○ 엔티티와 매핑할 테이블 지정
◼ name: 매핑할 테이블 이름(엔티티 이름을 사용)
◼ catalog: 데이터베이스 catalog 매핑
◼ schema: 데이터베이스 schema 매핑
◼ uniqueConstraints(DDL): DDL 생성시 유니크 제약조건 생성
◍ 데이터 베이스 스키마 자동생성
○ DDL을 애플리케이션 실행 시점에 자동 생성
○ 테이블 중심 -> 객체 중심
○ 데이터베이스 방언을 활용해서 데이터 베이스에 맞는 적절한 DDL 생성 -> 개발 장비에서만 사용
○ 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용
○ 옵션
◼ create: 기존 테이블 삭제후 다시 생성(drop + create)
◼ create-drop: create와 같으나 종료시점에 테이블 drop
◼ update: 변경분만 반영(운영 DB에는 사용하면 안됨.)
◼ validate: 엔티티와 테이블이 정상 매핑되었는지만 확인
◼ none: 사용하지 않음.
○ 주의할 점.
◼ 운영장비에는 절대 create, create-drop,update 안 됨
◼ 개발 초기단계는 create 또는 update
◼ 테스트 서버는 update 또는 validate
◼ 스테이징과 운영 서버는 validate 또는 none
◍ DDL 생성기능
○ DDL 생성 기능은 DDL을 자동 생성 할때만 사용되고 JPA실행 로직에는 영향을 주지 않는다.
◼ 예시1: @Column(nullable=false, length =10)
◼ 예시2: Table(uniqueConstraints = {@UniqueConstraint(name="NAME_AGE_UNIQUE",
columnNames={"NAME", "AGE"} )}}
2. 필드와 컬럼 매핑
◍ 종류
○ @Column: 컬럼매핑
◼ name: 필드와 매핑할 테이블의 컬럼 이름 (기본: 객체의 필드이름)
◼ insertable, updatable: 등록,변경 가능여부( 기본: TRUE)
◼ nullable(DDL): null값의 허용 여부. false로 설정하면 DDL 생성시 not null 제약 조건
◼ unique(DDL): @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약 조건 걸 때 사용
◼ columnDefinition: 데이터 베이스 컬럼 정보. ex) varchar(100) default 'EMPTY'
(기본: 필드의 자바 타입과 방언 정보를 사용)
◼ length(DDL): 문자의 길이 제약조건, String 타입에만 사용(기본: 255)
◼ precision, scale(DDL): BigDecimal 타입에서 사용(BigInteger도).
◻ precision 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수다.
◻ 참고로 double, float타입에는 적요ㅕㅇ 되지 않는다. 아주 큰 숫자나 정밀한 소수를 다루어야 할 떄만 사용.
◻ (기본 precision = 19, scale = 2)
○ @Temporal: 날짜 타입 매핑
◼ 참고: LocalDate, LocalDateTime을 사용할 때는 생략가능
◼ 날짜타입(java.util.Date, java.util.Calendar)을 매핑 할 때 사용
◼ value
◻ TemporalType.DATE: 날짜, 데이터베이스 date타입과 매핑(예: 2013-10-11)
◻ TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑(예: 11:11:11)
◻ TemporalType.TIMESTAMP: 날짜와 시간, 데이터베이스 timestamp 타입과 매핑(예:2013-10-11 11:11:11)
○ @Enumerated: enum타입 매핑
◼ 주의! ORDINAL사용하지 않기
◼ value: EnumType.ORDIAL: enum순서를 데이터베이스 저장(기본)/ EnumType.String: enum이름 데이터 베이스 저장.
○ @lob: BLOB, CLOB 매핑
◼ @Lob 에는 지정할 수 있는 속성이 없다.
◼ 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑.
◻ CLOB: String, char[], java.sql.CLOB
◻ BLOB: byte[], java.sql.BLOB
○ @Transient 특정 필드를 컬럼에 매핑하지 않음(매핑 무시)
◼ 필드 매핑 안함
◼ 데이터 베이스에 저장 및 조회 안함
◼ 메모리상에서만 임시로 어떤 값 보관하고 싶을 때 사용.
3. 기본키 매핑
◍ @Id
○ 직접할당: @Id 만 사요ㅕㅇ
◍ @GenerateValue (자동생성)
○ IDENTITY: 데이터베이스에 위임, MYSQL
◼ 기본키 생성을 데이터 베이스에 위임
◼ 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
◼ 문제점
◻ JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행
◻ AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID값을 알 수 있음.
◻ IDENTITY 전략은 em.persit() 시점에 즉시 INSERT SQL 실행하고 DB에서 식별자를 조회.
◻ ==> 트랜젝션에서 이런 경우가 많아지면 성능 이슈가 있을 수 있으나 insert쿼리가 여러번 날라간다고 큰 성능 이슈 X
@Entity
public class Member{
@Id
@GeneratedValue(strategy=GenerationType.IDENNTITY)
private Long id;
○ SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용, ORACLE
◼ @SequenceGenerator 필요.
◻ name: 식별자 생성기 이름(기본: 필수)
◻ sequenceName: 데이터베이스에 등록되어 있는 시쿼스 이름(기본: hibernate_sequence)
◻ initialValue: DDL 생성시에만 사용됨. 시퀀스 DDL을 생성할 때 처음 1 시작하는 수를 지정. (기본:1)
◻ allocationSize: 시퀀스 한 번 호출에 증가하는 수(기본:50)
(성능 최적화에 사용됨. 데이터 베이스 시퀀스 값이 하나 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야함.)
◻ catalog, schema: 데이터베이스 catalog, schema 이름
◼ 데이터 베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브잭트
◼ 오라클, PostgreSQL, DB2, H12 에서 사용
◼ 성능 최적화 위해 em.persist()때 call next value for 로 50까지 가져온후 (첫번째:1, 두번째:50) 메모리에서 증가 시킴.
@Entity
@SequenceGenerator(
name ="MEMBER_SEQ_GENERATOR"
sequenceName="MEMBER_SEQ"
initialValue = 1, allocationSize = 1)
public class Member{
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "MEMBER_SEQ_GENERATOR")
private Long id;
○ TABLE:키 생성용 테이블 사용, 모든 DB에서 사용
◼ @TableGenerator 필요
◻ name: 식별자 생성기 이름(기본: 필수)
◻ table: 키생성 테이블명(기본: hibernate_sequcnees)
◻ pkColumnName: 시퀀스 컬럼명(기본: sequence_name)
◻ pkColumnValue: 키로 사용할 값 이름(기본: 엔티티 이름)
◻ valueColumnNa: 시쿼스 값 컬럼명(기본: next_val)
◻ initialValue: 초기 값, 마지막으로 생성된 값이 기준 (기본: 0)
◻ allocationSize: 시퀀스 한번에 호출에 증갓하는 수(성능 최적화에 사용(기본: 50)
◻ catalog,schema: catalog, schema 이름
◼ 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
◼ 장점: 모든 데이터베이스에 적용가능
◼ 단점: 성능
create table MY_SEQUENCE(
sequence_name varchar(255) not null, next_val bigint, primary key( sequence_name )
@Entity
@TableGenerator(
name="MEMBER_SEQ_GENERATOR",
table = "MY_SEQUENCES"
pkColumnValue="MEMBER_SEQ", allocationSize = 1)
public class Member {
@Id
@Generatedvalue(strategy = GenerationType.TABLE,
generator="MEMBER_SEQ_GENERATOR")
private Long id;
○ AUTO: 방언에 따라 자동 지정 (기본값)
◍ 권장하는 식별자 전략
○ 기본키 제약조건: null 아님, 유일, 변하면 안됨
○ 미래까지 이 조건을 만족하는 자연키 찾기 어렵다면 대리키 사용.
◼ 예: 주민등록번호도 기본 키로 적절치 않음.
◼ 권장: Long형 + 대체키 + 키 생성전략 사용.
**스프링부트 사용시 변수이름이 orderDate(카멜케이스) 라도 order_date(언더스코어)로 바꾸어 줌
4. 연관관계 매핑 : @ManyToOne, @JoinColumn
◍ 단방향 연관관계
○ 일대다라면 @ManyToOne을 '다'쪽에 붙이면 됨 / commit이나 flush하지 않으면 영속성 컨텍스트에서 가져옴.
◍ 양방향 연관관계
○ DB와 객체의 차이
◼ DB는 외래키로 FK의 정보를 가져올수 있지만 객체는 일대다라면 일쪽에 @OneToMany(mappedBy="객체이름")을
List와함께 붙여줘야함.
@OneToMany(mappedBy="team")
private List<Member> members = new ArrayList<>();
◼ 객체와 테이블의 연관관계 차이
◻ 객체: 회원 -> 팀 단방향 1개 / 팀 -> 회원 연관관계 1개(단방향)
◻ 테이븛: 회원 <-> 팀 연관관계 1개 (양방향)
◼ mappedBy 이유
◻ 둘 중 하나로 외래 키를 관리해야 한다. = 연관관계 주인
◻ 연관관계 주인만이 외래 키를 관리(등록, 수정)
◻ 주인이 아닌 쪽은 읽기만 가능. 주인이 아니면 mappedBy 속성으로 주인 지정.
◻ 어디를 주인으로 해야 하는지: 외래키가 있는 쪽을 주인으로 관리하라.
-> 직관적인 설계(하나의 엔티티에 접근해서 바꿀때 업데이트 됨)/ 성능이슈.
○ 양방향 매핑시 가장 많이 하는 실수(연관관게의 주인에 값 미입력.)
◼ 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member9);
member.setName("member1");
//2. member.setTeam(team) 필요
//1. 역방향만 관계 설정.
team.getMembers().add(member);
em.getpersist(member);
//3. 그러나 add없이 setTeam만 하는 것도 문제
// em.flush(); // 여기서 플러시나 클리어 강제로 시키지 않으면 아래 for문 비어있음.
// em.clear(); // find는 DB에서 가져오는데 setTeam한 내역은 아직 commit되지 않음.
//그래서 setTeam과 add둘다 세팅하는게 좋다.
Team findTeam = em.find(Team.class, team.getId()); //1차 캐시
List<Member> members = findTeam.getMembers();
for (Member m : members){
System.out.println("m ="
}
tx.commit()
◼ solution: 연관관계 편의 메소드 생성하자. (둘 중 선택, 그러나 둘다는 X)
//Member 클래스
public void changeTeam(Team teama){//setter와 다르게 구별 주기 위해
this.team = team;
team.getMembers().add(this);
}
//Team 클래스
public void addMember(Member member){
member.setTeam(this);
members.add(member)
}
◼ 양방향 매핑시 무한 루프를 조심하자.
◻ **lombok의 toString(), JSON 생성 라이브러리 등 조심 (컨트롤러로 엔티티 반환하지 않기. DTO로 변해서 보내기)
◼ 정리
◻ 처음은 단방향 매핑으로 이미 연관관계 객체 매핑 완료 -> 이후 양방향 매핑 필요시 추가.
◻ 필요한 경우: 객체 그래프탐색 기능 추가. JPQL에서 역방향 탐색시.
◍ 다중성
○ @JoinColumn
◼ 속성
◻ name: 매핑할 외래키 이름(기본: 필드명 + _ + 참조하는 테이블의 기본키 컬럼명)
◻ referencedColumnName: 외래 키가 참조하는 대상 테이블의 컬럼명 (기본: 참조하는 테이블의 기본 키 컬럼명)
◻ foreignKey(DDL): 외랰 키 제약 조건을 직접 지정할 수 있다. 이 속성은 테이블 생상할 때만 사용.
◻ unique, nullable insertable, updatable, columnmDefinition, table @Column 속성과 같음.
○ 다대일: @ManyToOne (가장 많이 사용)
◼ 양방향시 외래키가 있는 쪽이 연관관계 주인
◼ 속성
◻ optional: false로 설정하면 연관된 엔티티가 항상 있어야 한다. (기본: TRUE)
◻ fetch: 글로벌 페치 전략을 설정(기본: @MantToOne=FetchType.EAGER/ @OneToMany=FetchType.LAZY)
◻ cascade: 영속성 전이 기능을 사용.
◻ targetEntity: 연관된 엔티티 타입 정보 설정. 이 기능 거의 사용하지 않고 컬렉션을 사용해도 제네릭으로 타입정보 알 수 있다.
○ 일대다: @OneToMany
◼ 단방향
◻ 팀은 맴버를 알고 싶지만 맴버는 팀을 알고 싶지 않음.
◻ 일대다 단방향은 일대다(1:N)에서 1이 연관관계의 주인
◻ 테이블 일다다 관계는 항상 다(N)쪽에 외래키가 있을 수 밖에 없음(구조상)
◻ @JoinColumn을 꼭 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함 (중간 테이블 하나 추가 됨.)
◻ 단점1: 엔티티가 관리하는 외래 키가 다른 테이블에 있음 (객체와 테이블 차이때문에 반대편의 외래키를 관리.)
◻ 단점2: 연관관계 관리를 위해 추가로 UPDATE SQL 실행
◻ 일대다 단방향 매핑보다 다대일 양방향 매핑을 사용!
◻ 사용 권장 하지 않음.
◼ 양방향
◻ 공식적으로 존재하지 않음.
◻ @JoinColumn(insertrable=false, updateable=false)
◻ 읽기 전용 필드를 사용해서 양방향처럼 사용하는 방법
◻ 다대일 양방향을 사용 권장.
◼ 속성
◻ mappedBy: 연관관계의 주인 필드를 선택.
◻ fetch: 글로벌 페치 전략을 설정(기본: @MantToOne=FetchType.EAGER/ @OneToMany=FetchType.LAZY)
◻ cascade: 영속성 전이 기능을 사용.
◻ targetEntity: 연관된 엔티티 타입 정보 설정. 이 기능 거의 사용하지 않고 컬렉션을 사용해도 제네릭으로 타입정보 알 수 있다.
○ 일대일: @OneToOne
◼ 일대일 관계는 그 반대도 일대일
◼ 주 테이블이나 대상 테이블 중에 외래키 선택가능/ 방법
◻ 주 테이블에 외래 키 단방향: 다대일(@ManyToOne)단방향 매핑과 유사
◻ 주 테이블에 외래키 양방향: 다대일 양방향 매핑 처럼 외래키가 있는 곳이 연관관계의 주인.
◻ 대상 테이블 외래키 단방향: 단방향 관계 JPA 지원 X
◻ 대상 테이블 외래키 양방향: 일대일 주 테이블에 외래키 양방향 매핑 방법과 같음.
◼ 외래 키에 데이터베이스 UNIQUE 제약조건 추가.
◼ trade-off..?
◼ 주 테이블에 외래 키
◻ 주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래키를 두고 대상 테이블을 찾음.
◻ 객체지향 개발자 선호
◻ JPA매핑 편리
◻ 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지
◻ 단점: 값이 없으면 외래 키에 null허용
◼ 대상 테이블에 외래키
◻ 전통적인 데이터베이스 개발자 선호
◻ 장점: 주 테이블과 대상 테이블을 일대일 에서 일대다 관계로 변경할때 테이블 구조 유지.
◻ 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩 됨.
○ 다대다: @ManyToMany
◼ 관계형 데이터 베이스는 정규화된 테이블 2개로 다대다 관계 표현할 수 없ㅇ.ㅁ
◼ 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야함.
◼ 객체는 컬렉션을 사용해서 객체 2개로 다대다 관계 가능.
◼ @ManyToMany사용하고 @JoinTable로 연결 테이블 지정(단방향, 양방향 가능)
◼ 한계
◻ 편리해 보이지만 실무에서 사용 안됨.
◻연결 테이블이 단순히 연결만하고 끝나지 않음. 다른 데이터가 들어 올 수 있음.
◼ 극복
◻ 연결 테이블용 엩티티 추가(연결 테이블을 엔티티로 승격)
◻ @ManyToMany -> @OneToMany, @ManyToOne
◼ 실무에서 미사용 권장.
'Spring > JPA' 카테고리의 다른 글
JPA Basic - 값 타입 (0) | 2024.01.09 |
---|---|
JPA Basic - 프록시, CASCADE, 고아객체 (0) | 2024.01.09 |
JPA Basic - 상속관계 매핑 (0) | 2024.01.09 |
JPA Basic - 영속성 관리 (0) | 2024.01.08 |
JPA Basic - Intro (0) | 2024.01.08 |