[Spring Boot] JPA 강의 정리-8 (엔티티 매핑)

Posted by 김성철

SpringBoot - JPA 강의 정리-8(엔티티 매핑)

객체와 테이블 매핑

@Entity  
	@Entity가 붙은 클래스는 JPA가 관리하는 엔티티라 한다.  
	JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 가 필수임  
	※ 주의사항  
		- 기본 생성자 필수 (파라미터가 없는 public 또는 protected 생성자)  
		- final 클래스 , enum , interface , inner 클래스는 사용하면 안됨  
		- 저장할 필드에 final 사용하면 안됨  
  
	속성 :  
		@Table(Name ="MEMBER") : JPA에서 사용할 엔티티 이름을 지정한다.  
			기본값 : 클래스 이름을 그대로 사용(예:Member)  
			같은 클래스 이름이 없으면 가급적 기본값을 사용한다.  
		catalog : 데이터베이스 catalog 매핑  
		schema : 데이터베이스 schema 매핑  
		uniqueConstraints(DDL) : DDL 생성 시에 유니크 제약조건 생성  

데이터베이스 스키마 자동 생성

- DDL을 어플리케이션 실행 시점에 자동 생성  
- 테이블 중심 -> 객체 중심  
- 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성  
- 이렇게 생성된 DDL은 개발 장비에서만 사용  
- 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용  
  
속성 :  
	아래와 같이 사용함  
	<property name="hibernate.hbm2ddl.auto" value="validate"/>  
  
	create : 기존테이블 삭제 후 다시 생성 (DROP+CREATE)  
  
	create-drop : CREATE 와 같으나 종료 시점에 테이블 DROP  
  
	update : 변경분만 반영 (운영DB에는 사용하면 안됨)  
		alter table 쿼리문을 생성함, 지우는것은 안되며 추가만 가능  
  
	validate : 엔티티와 테이블이 정상 매핑되었는지만 확인  
  
	none : 사용하지 않음, 관례상 사용함  
  
※ 주의사항  
	- 운영 장비에서는 절대 create , create-drop , update 사용하면 안됨  
	- 개발 초기 단계에는 create 또는 update  
	- 테스트 서버는 update 또는 validate  
	- 스테이징과 운영 서버는 validate 또는 none  
  
	왠만하면 본인이 직접 테이블 생성 쿼리를 작성해서 DBA 에게 전달.  
  
DDL 생성 기능 :  
	- 제약 조건 추가  
		@Cloumn(unique=true, length =10, name= "USERNAME")  
	- 유니크 제약 조건 추가  
		@Table(uniqueConstraints = {@UniqueConstraint(name ="NAME_AGE_UNIQUE", columnNames{"NAME","AGE})}  
  
	DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않음  

필드와 컬럼 매핑

@Column : 컬럼 매핑  
	name : 필드와 매핑할 테이블의 컬럼 이름 (default : 객체의 필드 이름)  
  
	insertable , updatable : 등록 변경 가능 여부 (default : TRUE)  
  
	nullable(DDL) : null 값의 허용 여부를 설정함, false로 설정하면 DDL 생성시에 not null 제약조건이 붙는다.  
  
	unique(DDL) : @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 떄 사용함  
		이름을 알아볼수 없게 나옴, 그래서 잘 안씀 @Table에서 사용하는것을 선호함  
  
	columnDefinition(DDL) : 데이터베이스 컬럼 정보를 직접 줄 수 있다.  
		ex) varchar(100) default 'EMPTY'  
  
	length(DDL) : 문자 길이 제약 조건 , String 타입에만 사용함 (default : 255)  
  
	precision , scale(DDL) : BigDecimal 타입에 사용함(BigInteger도 사용가능)  
		precision은 소수점을 포함한 전체 자릿수를 scale은 소수즤 자릿수임.  
		참고로 double ,float 타입에는 적용되지 않음  
		정밀한 소수를 다루어야 할때만 사용함  
  
@Temporal : 날짜 타입 매핑  
	TemporalType.DATE  
  
	TemporalType.TIME  
  
	TemporalType.TIMESTAMP  
  
	LocalDate , LocalDateTime 사용시에는 생략이 가능함 (최신 하이버네이트 지원)  
  
	LocalDate는 date로 지정됨  
	LocalDateTime은 timstamp로 지정됨  
  
@Enumerated : enum 타입 매핑, 해당 어노테이션 사용시 EnumType.STRING을 무조건 지정  
	EnumType.STRING : enum 이름을 데이터 베이스에 저장,  
  
	EnumType.ORDINAL : enum 순서를 데이터베이스에 저장, 쓰지말자  
  
	defalut : EnumType.ORDINAL  
  
	※ EnumType.ORDINAL 는 사용하면 안됨, 순서로 들어감  
	요구사항이 추가되어서 enum 타입이 추가되면 순서가 꼬임, 지옥감  
  
@Lob : 지정할수 있는 속성이 없음  
	문자면 CLOB 매핑 (String , char[] , java,sql.CLOB)  
	나머지는 BLOB 매핑 (byte[] , java.sql.BLOB)  
  
@Transient : 특정 필드를 컬럼에 매핑하고 싶지 않을때  

기본 키 매핑

직접 할당 : @Id  
자동 생성 : @GeneratedValue  
	IDENTITY : 데이터베이스에 위임 , MYSQL  
		기본키 생성을 데이터베이스에 위임  
		@GeneratedValue(strategy = GenerationType.IDENTITY)  
		- 주로 MySQL , PostgreSQL , SQL Server , DB2에서 사용  
			(MySQL의 AUTO_INCREMENT)  
		-JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행  
		-AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있음  
		-IDENTITY 전략은 em.persist()시점에 즉시 INSERT SQL 실행하고 DB에서 식별자를 조회  
  
		특징 :  
			DB에 들어가야 ID 값을 알 수 있음  
			그래서 얘는 em.persist 하는시점에 데이터베이스에 쿼리를 날림  
  
	SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용 , ORACLE  
		@GeneratedValue(strategy = GenerationType.SEQUENCE)  
		@SequenceGenerator 필요, 시퀀스제네레이터는 클래스 위에 붙임  
			- 사용 가능 속성  
			name : 식별자 생성기 이름  
			sequenceName : 데이터베이스에 등록되어 있는 시퀀스 이름  
			initialValue : DDL 생성 시에만 사용되며, 시퀀스 DDL을 생성할 때 처음 1 시작하는 수로 지정  
			allocationSize : 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨)  
				데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값은 반드시 1로 설정해야 함 (기본값 50)  
			catalog , schema : 데이터베이스catalog , schema 이름  
		=====================================================================  
		@SequenceGenerator(name="member_seq_generator" , sequenceName = "member_seq")  
		public class Member {  
			@Id  
			@Column(name="id")  
			@GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "member_seq_generator")  
			private Long id;  
		=====================================================================  
  
		특징 :  
			DB에서 시퀀스를 먼저 조회해 온뒤 영속성 컨텍스트에 추가 후  나중에 커밋하는 시점에 쿼리를 날림  
  
	TABLE : 키 생성용 테이블 사용 , 모든 DB에서 사용  
		@GeneratedValue(strategy = GenerationType.TABLE)  
		@TableGenerator 필요 ,  클래스 위에붙임  
		키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략  
		장점 : 모든 데이터베이스에 적용 가능  
		단점 : 성능  
			- 사용 가능 속성  
				name : 식별자 생성기 이름  
				table : 키생성 테이블명 (hibernate_sequences)  
				pkColumnName : 시퀀스 컬럼명 (sequence_name)  
				valueColumnNa : 시퀀스 값 컬럼명 (next_val)  
				pkColumnValue : 키로 사용할 값 이름 (엔티티 이름)  
				initialValue : 초기 값, 마지막으로 생성된 갓이 기준 (기본값 0)  
				allocationSize : 시퀀스 한번에 호출하는 수 (기본값 50)  
				catalog, schema : 데이터베이스 catalog , schema 이름  
				uniqueConstraints(DDL) : 유니크 제약 조건  
		=====================================================================  
		@TableGenerator(  
				name = "MEMBER_SEQ_GENERATOR",  
				table = "MY_SEQUENCES",  
				pkColumnValue = "MEMBER_SEQ", allocationSize = 1  
		)  
		public class Member {  
		=====================================================================  
	AUTO : 방언에 따라 자동 지정  
		@GeneratedValue(strategy = GenerationType.AUTO)  

권장하는 식별자 전략

- 기본 키 제약 조건: null아님, 유일 , 변하면 안된다  
- 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자.  
- 예를 들어 주민등록번호도 기본 키로 적절하지 않다.  
  
권장 : Long형 + 대체키 + 키 생성전략 사용  

테이블 중심 설계의 문제점

- 현재 방식은 객체 설계를 테이블 설계에 맞춘 방식  
- 테이블의 외래키를 객체에 그대로 가져옴  
- 객체 그래프 탐색이 불가능  
- 참조가 없으므로 UML도 잘못됨  

연관관계 매핑

@ManyToOne  
@JoinColumn