728x90

엔티티 식별자 생성 방식

식별자 생성 방식

  • 직접 할당
    • @Id 설정 대상에 직접 값 설정
      • 사용자가 입력한 값, 규칙에 따라 생성한 값 등
      • 예) 이메일, 주문 번호
    • 저장하기 전에 생성자 할당, 보통 생성 시점에 전달
  • 식별 칼럼 방식
    • DB의 식별 칼럼에 매핑(예, MySQL 자동 증가 칼럼)
      • db가 식별자를 생성하므로 객체 생성 시에 식별 값을 설정하지 않음
    • 설정 방식
      • @GeneratedValue(strategy = Generation.Type.IDENTITY) 설정
    • Insert 쿼리를 실행해야 식별자를 알 수 있음
      • EntityManager#persist() 호출 시점에 Insert 쿼리 실행
      • persist() 실행할 때 객체에 식별자 값 할당됨
  • 시퀀스 사용 방식
    • 시퀀스 사용해서 식별자 생성
      • JPA가 식별자 생성 처리 → 객체 생성 시에 식별 값을 설정하지 않음
    • 설정 방식
      • @SequenceGenerator로 시퀀스 생성기 설정
      • @GeneratedValue로 generator로 시퀀스 생성기 지정
    • EntityManager#persist() 호출 시점에 시퀀스 사용
      • persist() 실행할 때 객체에 식별자 값 할당됨
      • Insert 쿼리는 실행하지 않음
  • 테이블 사용 방식
    • 테이블을 시퀀스처럼 사용
      • 테이블에 엔티티를 위한 키를 보관
      • 해당 테이블을 이용해서 다음 식별자 생성
    • 설정 방식
      • @TableGenerator로 테이블 생성기 설정
      • @GeneratedValue의 generator로 테이블 생성기 지정
    • EntityManager#persist() 호출 시점에 테이블 사용
      • persist() 할 때 테이블을 이용해서 식별자 구하고 이를 엔티티에 할당
      • Insert 쿼리는 실행하지 않음
    • 식별자를 생성할 때 사용할 때 테이블 구조
      • 엔티티 이름 칼럼
      • 식별자 보관 칼럼

https://www.youtube.com/watch?v=Xw9uTs72SVo&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=5 

@Embeddable

  1. 엔티티가 아닌 타입을 한 개 이상의 필드와 매핑할 때 사용
    1. 예 : Adress, Money 등 매핑
  2. 엔티티의 한 속성으로 @Embeddable 적용 타입 사용
@Embeddable
public class Adress {
	@Column(name = "addr1")
	private String address1;
	@Column(name = "addr2")
	private String address2;
	@Column(name = "zipcode")
	private String zipcode;
	
	protected Address() {
	}
	... 생성자, getter 생략

	@Entitiy
	@Table(name = "hotel_info")
	public class Hotel {
		@Id
		@Column(name = "hotel_id")
		private String id;
		....
		@Embedded
		private Adress adress;

같은 @Embeddable 타입 필드가 두 개면?

문제점
@Entity
public class Employee {
	@Id
	private String id;
	@Embedded
	private Address homeAddress;
	@Embedded
	private Address workAddress;
}
서로 같은 컬럼에 매핑이되어서 엔티티메니저팩토리를 초기화할 때 에러가 난다.
해결법
@Entity
public class Employee {
	@Id
	private String id;
	@Embedded
	private Address homeAddress;
	@AttributeOverrides({
		@AttributeOverride(name = "address1", column = @Column(name = "waddr1")),
		@AttributeOverride(name = "address2", column = @Column(name = "waddr2")),
		@AttributeOverride(name = "zipcode", column = @Column(name = "wzipcode"))
	}) 
	@Embedded
	private Address workAddress;
}

정리

  • @Embeddable을 사용하면 모델을 더 잘 표현할 수 있음
  • 개별 속성을 모아서 이해 → 타입으로 더 쉽게 이해
    • (addr1, addr2, zipcode)를 모아서 ‘이게 주소구나’ → ‘주소’네

https://www.youtube.com/watch?v=WtS5IszIueA&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=6 

@Embeddable 다른 테이블에 매핑하기

다른 테이블에 값을 저장할 때

  • @SecondaryTable + 테이블명
@Embeddable
public class Intro {
	@Column(table = "writer_intro", name = "content_type")
	private String contentType;
	
	@Column(table = "writer_intro")
	private String content;
	...
}

@Entity
@SecondaryTable(name = "writer_intro", 
	pkJoinColumns = @PrimaryKeyJoinColumn(
		name = "writer_id", // writer_intro 테이블 칼럼
		referencedColumnName = "id" // writer 테이블 칼럼
	)
)
public class Writer {
	...
	@Embedded
	private Intro intro;
}
  • @SecondaryTable + @AttributeOverride
@Embeddable
public class Address {
	@Column(name = "addr1")
	private String address1;
	@Column(name = "addr2")
	private String address2;
	@Column(name = "zipcode")
	private String zipcode;
}

@Entity
@SecondaryTables({
	@SecondaryTable(name = "writer_address", 
		pkJoinColumns = @PrimaryKeyJoinColumn(
			name = "writer_id",
			referencedColumnName = "id"
	),
	...
})
public class Writer {
	....
	@Embedded
	@AttributeOverrides({
		@AttributeOverride(name = "address1", 
			column = @Column(table= "writer_address", name = "addr1")),
		@AttributeOverride(name = "address2", 
			column = @Column(able= "writer_address", name = "addr2")),
		@AttributeOverride(name = "zipcode", 
			column = @Column(nable= "writer_address"))
	})
	private Address address; 	

정리

  • @SecondaryTable
    • 다른 테이블에 저장된 데이터를 @Embeddable로 매핑 가능
    • 다른 테이블에 저장된 데이터가 개념적으로 밸류(값)일 때 사용
      • 1 - 1 관계인 두 테이블을 매핑할 때 종종 출현

https://www.youtube.com/watch?v=3_sdQGfL2Lg&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=7 

값 컬렉션 Set 매핑

  • 단순 값 Set 매핑
@Entity
@Table(name = "role")
public class Role {
	@Id
	private String id;
	private String name;
	
	@ElementCollection
	@CollectionTable(
		name = "role_perm",
		joinColumns = @JoinColumn(name = "role_id")
	)
	@Column(name = "perm")
	private Set<String> permission = new HashSet<>();

정리

  • 컬렉션 테이블을 이용한 값 set 매핑
    • @ElementCollection과 @CollectionTable이면 끝

https://www.youtube.com/watch?v=lQ4-kVeHVGk&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=8 

값 컬렉션 List 매핑

  • 단순 값 List 매핑
@Entity
@Table(name = "question")
public class Question {
	@Id
	private String id;
	private String text;

	@ElementCollection
	@CollectionTable(
		name = "question_choice",
		joinColumns = JoinColumn(name = "question_id")
	)
	@OrderColumn(name = "idx")
	@Column(name = "text")
	private List<String> choices;

정리

  • 컬렉션 테이블을 이용한 값 List 매핑
    • @ElementCollection과 @CollectionTable, @OrderColumn이면 끝

https://www.youtube.com/watch?v=Wq4B5RpIeAY&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=9 

값 컬렉션 Map 매핑

  • 앞에 Set하고 동일
@Entity
@Table(name = "doc")
public class Role {
	@Id
	private String id;
	private String name;
	private String content;
	@ElementCollection
	@CollectionTable(
		name = "doc_prop",
		joinColumns = @JoinColumn(name = "doc_id")
	)
	@MapKeyColumn(name = "name")
	@Column(name = "value")
	private Map<String, String> props = new HashMap<>();

정리

  • 컬렉션 테이블을 이용한 값 Map 매핑
    • @ElementCollection과 @CollectionTable, @MapKeyColumn이면 끝

https://www.youtube.com/watch?v=CPIgicoqLnM&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=10 

 

728x90
728x90

영속 단위 기준으로 초기화

EntitiyManagerFactory emf = Persistence.createEntityManagerFactory("jpabegin");

persistence.xml 파일에 정의한 영속 단위 기준으로 초기화

필요한 자원 생성

emf.close();

팩토리 닫음

사용한 자원 반환

EntityManager로 DB 연동

EntityManager entityManager = emf.createEntityManager(); //EntityManager 생성
EntityTransaction transaction = entityManager.getTransaction(); //EntityTransaction 구함
try {
	transaction.begin();  // 트렌젝션 시작
	...entityManager로 DB 작업
	transaction.commit(); // 트렌젝션 커밋
} catch (Exception ex) {
	transaction.rollback(); // 트렌젝션 롤백
} finally {
	entityManager.close(); // 엔티티매니저 닫음
}

저장과 쿼리 실행 시점

transaction.begin();
User user = new User("user@user.com", "user", LocalDateTime.now());
entityManager.persist(user);
log.info("EntityManager.persist 호출함");
transaction.commit();
log.info("EntityTransaction.commit 호출함");

persist때 insert쿼리문이 실행되지 않고 commit 시점에 insert 쿼리가 실행된다.

수정도 commit 시점에 알맞게 실행된다.

영속 컨텍스트

EntityManager 단위로 영속 컨텍스트 관리

커밋 시점에 영속 컨텍스트의 변경 내역을 DB에 반영(변경 쿼리 실행)

정리

기본구조

  • EntityManagerFactory 초기화
  • DB 작업이 필요할 때마다
    • EntityManager 생성
    • EntityManager로 DB 조작
    • EntityTransactio으로 트렌젝션 관리
  • 하지만 스프링과 연동할 때는
    • 대부분 스프링이 대신 처리하므로 매핑 설정 중심으로 작업

영속성 컨텍스트

  • 엔티티를 메모리에 보관
  • 변경을 추적해서 트렌젝션 커밋 시점에 DB에 반영

https://www.youtube.com/watch?v=7ljqL8ThUts&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=2 

엔티티 CRUD 처리

영속 컨텍스트는 트렌젝션 범위 안에서 유지가 된다.

EntityManager가 제공하는 메서드 이용

  • persist() : 저장
  • find(클래스, 식별자) : 조회
    • 엔티티 타입, ID 타입이 맞아야 함
    • 일치하지 않으면 익셉션
  • 수정 : 트렌젝션 범위 내에서 변경된 값을 자동 반영
  • remove() : 삭제, find메서드로 읽어온 객체를 전달해야 된다.
  • merge()

https://www.youtube.com/watch?v=kmCKAwOie_I&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=3 

엔티티 매핑

  1. 기본 어노테이션
    1. @Entity : 엔티티 클래스에 설정, 필수
    2. @Table : 매핑할 테이블 지정
      1. 애노테이션을 생략하면 클래스 이름과 동일한 이름에 매핑
      2. 속성
        1. name : 테이블 이름(생략 하면 클래스 이름과 동일한 이름)
        2. catalog : 카탈로그 이름 (예, MySQL DB 이름)
        3. schema : 스키마 이름 (예, 오라클 스키마 이름)
    3. @Id : 식별자 속성에 설정, 필수
    4. @Column : 매핑할 칼럼명 지정
      1. 지정하지 않으면 필드명/프로퍼티명 사용
    5. @Enumerated : enum 타입 매핑할 때 설정
      1. 설정 값
        1. EnumType.ORDINAL(기본값) : enum타입의 값의 순서를 저장
          1. 문자열 타입 칼럼에 매핑
        2. EnumType.STRING : enum 타입 값 이름을 저장
          1. 숫자 타입 칼럼에 매핑
    6. @Temporal : java.util.Date, java.util.Calender 매핑
      1. 자바 8 시간/날짜 타입 등장 이후로 거의 안 씀
    7. @Basic : 기본 지원 타입 매핑(거의 안 씀)

엔티티 클래스 제약 조건(스펙 기준)

  1. @Entity 적용해야 함
  2. @Id 적용해야 함
  3. 인자 없는 기본 생성자 필요
  4. 기본 생성자는 public이나 protected여야 함
  5. 최상의 클래스여야 함
  6. final이면 안됨

접근 타입

  1. 두 개의 접근 타입
    1. 필드 접근 : 필드 값을 사용해서 매핑
    2. 프로퍼티 접근 : getter/setter 메서드를 사용해서 매핑
  2. 설정 방법
    1. @Id 애노테이션을 필드에 붙이면 필드 접근
    2. @Id 애노테이션을 getter 메서드에 붙이면 프로퍼티 접근
    3. @Access 애노테이션을 사용해서 명시적으로 지정
      1. 클래스 / 개별 필드에 적용 가능
      2. @Access(AccessType.PROPERTY) / @Access(AccessType.FIELD)
    4. 개인적으로 필드 접근 선호
      1. 불필요한 setter 메서드를 만들 필요 없음

https://www.youtube.com/watch?v=SbMJVuv8Iyo&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=4 

 

 

728x90
728x90

이 블로그는 최범균 님의 JPA 기초 강의를 듣고 작성한 블로그입니다.

일단 해보기 (jpa 3.0 기준)

JPA 특징

  1. 애노테이션을 이용한 매핑 설정
    • xml 파일을 이용한 매핑 설정도 가능
  2. String, int, LocalDate 등 기본적인 타입에 대한 매핑 지원
  3. 커스텀 타입 변환기 지원
    • 내가 만든 Money 타입을 DB 칼럼에 매핑 가능
  4. 벨류 타입 매핑 지원
    • 한 개 이상 칼럼을 한 개 타입으로 매핑 가능
  5. 클래스 간 연간 지원 : 1 - 1, 1 - N, N - 1, N - M
  6. 상속에 대한 매핑 지원
DB USER 생성

create database jpabegin CHARACTER SET utf8mb4;

CREATE USER 'jpauser'@'localhost' IDENTIFIED BY 'jpapass';
CREATE USER 'jpauser'@'%' IDENTIFIED BY 'jpapass';

GRANT ALL PRIVILEGES ON jpabegin.* TO 'jpauser'@'localhost';
GRANT ALL PRIVILEGES ON jpabegin.* TO 'jpauser'@'%'; 
create table jpabegin.user (
	email varchar(50) not null primary key,
	name varchar(50),
	create_date datetime,
) engine innodb character set utf8mb4; 

JPA 코드

@Entity DB 테이블과 매핑할 대상
@Table(name = "user") user 테이블과 매핑
public class User {
	@Id 식별자에 대응
	private String email;
	private String name;
	@Column(name = "create_date") create_date 칼럼과 매핑 칼럼이름과 필드 이름이 다를때 사용
	private LocalDateTime createDate;

일단 해보기 정리

  1. 간단한 설정으로 클래스와 테이블 간 매핑 처리
  2. EntityManager를 이용해서 DB 연동 처리
  3. 객체 변경만으로 DB 테이블 업데이트
  4. 쿼리 작성 x

https://www.youtube.com/watch?v=Zwq2McbFOn4&list=PLwouWTPuIjUi9Sih9mEci4Rqhz1VqiQXX&index=1 

728x90

+ Recent posts