객체지향적으로 설계를 할수록 관계형데이터베이스에 데이터를 저장하고 가져왔을 때 개발자가 설계한대로 객체 지향적으로 코드가 동작하기 위해서는 수 많은 sql문을
작성해야 했었다.(객체와 관계형데이터베이스간의 패러다임의 불일치) 그래서 대부분이 SQL 중심적으로 개발을 했었고 이를 해결하고 싶어 나온것이 바로 JPA
(Java Collection을 다루는 것 처럼 Entity를 다루자!)
기존 이를 해결하기 위해 자바진영에서 나왔던 EJB가 있었지만 오류도 많고 동작도 제대로 하지 않아 개빈 킹이라는 개발자가 하이버네이트를 만들었고 이를 많은 개발자들이
동참해 오픈소스화가 된 이후 다시 자바 진영에서 하이버네이트에서 조금 더 다듬고 표준으로 JPA를 들고나왔다.
EJB -> 하이버네이트 -> JPA
==
비교 시 같은 주소를 가진 객체를 참고한 것 처럼 true, 1차 캐싱 때문에 가능User user1 = em.find(User.class, 1L);
User user2 = em.find(User.class, 1L);
System.out.println(user1 == user2); // 결과 값 true 반환
entityManager.persist(user);
를 하게 되면 insert query를 바로 Db에 보내지 않고 차곡 차곡 쓰기 지연 SQL 저장소에 저장하고 transaction.commit();
하면 저장해뒀던 query가 Db에 전송transaction.commit();
시 jpa 내부적으로 flush()
가 호출되고 1차 캐시에 저장돼있는 스냅샷과 Entity를 비교해 바뀐flush();
: 영속성 컨텍스트의 상태와 데이터베이스의 상태를 맞추는 것(변경 내용 동기화)flush();
호출시 캐시는 지워지지 않고 쓰기 지연 SQL 저장소에 있는 query만 전송 됌 (영속성 컨텍스트가 지워지는게 아님)flush()
옵션은 transaction.commit();
과 JPQL 쿼리에서는 자동 호출되고 em.flush();
는 직접 호출을em.setFlushMode(FlushModeType.COMMIT)
으로 커밋할 때만 자동 호출 되도록 할 수 있다.User user = new User();
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
EntityManager em = emf.createEntityManager();
User user = new User();
em.getTransaction().begin();
em.persist(user); // EntityManager에 의해 User를 영속화
em.getTransaction().commit();
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
EntityManager em = emf.createEntityManager();
User user = new User();
em.getTransaction().begin();
em.persist(user);
em.detach(user); // 특정 Entity만 준영속 상태로
em.clear(); // EntityManager에 있는 영속성 컨텍스트 전부를 준영속 상태로
em.close(); // 영속성 컨텍스트를 종료
em.getTransaction().commit();
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
EntityManager em = emf.createEntityManager();
User user = new User();
em.getTransaction().begin();
em.persist(user);
em.remove(user);
em.getTransaction().commit();
@Entity
: JPA가 해당 class를 관리하도록 하는 어노테이션name
: 매핑할 테이블 이름을 지정할 수 있지만 일반적으로 class와 테이블 이름을 같도록 하기 때문에 기본값을 많이 사용함catalog
: Db catalog 매핑schema
: Db schema 매핑 uniqueConstraints
: DDL 생성 시 유니크 제약 조건 생성@Entity
public class User {
@Id
private Long id;
private String name;
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
create
: application 실행 시 기존 테이블을 삭제 후 다시 생성create-drop
: create
와는 조금 다르게 application 종료 시 테이블 삭제update
: 추가되는 사항들만 적용됨 삭제가 불가능validate
: Entity와 테이블이 정상적으로 매핑됐는지 확인 할 수 있음none
: 해당 기능을 사용하지 않음, 해당 속성을 주석해도 되지만 관례상 사용@Column(length= 10)
운영 장비에서는 절대 네버 create, create-drop, update를 사용하면 안된다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@Id
: 유일한 값을 가지는 기본키인 PK와 필드를 매핑하는 어노테이션 @Column
: 클래스 필드의 이름과 Db에서 사용하는 컬럼을 매핑하는 어노테이션@Enumerated
: enum 타입을 필드로 사용할 경우 Db 컬럼에는 enum 타입이 대부분 없기 때문에 매핑해서 사용할 수 있음 기본값은 EnumType.ORDINAL
이다. @Enumerated(EnumType.ORDINAL)
: enum의 순서를 숫자형 타입으로 Db에 저장@Enumerated(EnumType.STRING)
: enum의 이름을 문자열 타입으로 Db에 저장, 권장하는 방법@Temporal(TemporalType.?)
: 날짜 타입으로 Java의 DateType은 날짜, 시간이 다 들어있지만 Db에서는 각 각 따로 구분해서 쓰기 때문에 맞춰주기 위함. (최신버전에서는 JPA가 필드 속성을 보고 맞춰줌)DATE
: 날짜 Db date 타입, TIME
: 시간 Db time 타입, TIMESTAMP
: 날짜와 시간을 Db timestamp 타입 3가지 타입이 있음@Lob
: 큰 컨텐츠를 사용하고 싶을 때 LOB 타입의 컬럼을 사용할 수 있다.@Transient
: Db와는 연관없이 메모리에서만 사용하고 싶은 필드일 때 사용PK를 직접 할당하려면 @Id
어노테이션만 달아주면 되지만 대부분 @GeneratedValue
자동 생성을 많이 사용하기 때문에 이에 관한건 아래표를 참고하자.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(name = "USER_NAME", length = 10)
private String name;
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
@GeneratedValue 속성 | 설명 |
---|---|
IDENTITY | Db에 위임 Mysql AUTO_INCREMENT와 같음, 새 객체가 영속성 컨텍스트에 의해 관리되는 시점에 PK값을 확인하기 위해 insert query가 Db에 전송됨 |
SEQUENCE | Db sequence object 사용 @SequenceGenerator() 을 이용해 직접 sequence을 설정할 수 있음, 새 객체가 영속성 컨텍스트에 의해 관리되는 시점에 PK값을 확인하기 위해 sequence call next value로 값을 가져와 객체의 Id를 초기화 한 후 영속성 컨텍스트에 저장 |
TABLE | 키를 생성하는 테이블을 만들어 Db Sequence 따라하는 방법으로 모든 Db에 적용할 수 있으나 성능이 떨어짐 |
AUTO | Db 방언에 맞춰 위 3개 속성 중 선택해 자동 생성 |
SEQUENCE
속성
name
: 식별자 생성기 이름으로 필수!sequenceName
: Db에 등록돼 있는 sequence 이름 기본값은 hibernate_sequenceinitialValue
: DDL 생성 시에만 사용됨, 처음 시작하는 수를 지정 기본값은 1allocationSize
: sequence를 한번 호출 할 때 증가하는 수를 지정(성능 최적화에 사용 call next value를 줄일 수 있음), Db sequence 값이 1씩 증가하도록 설정되있는 경우 반드시 1로 설정해야 함! 기본값은 50catalog, schema
: Db의 catalog와 schema의 이름Table
속성
name
: 식별자 생성기 이름 필수 값table
: 키 생성 테이블 명, 기본 값은 hibernate_sequencespkColumnName
: 시퀸스 컬럼 명, 기본 값은 sequence_namevalueColumn
: 시퀸스 값 컬럼 명, 기본 값은 next_valpkColumnValue
: 키로 사용할 값 이름, 기본 값은 Entity의 이름initialValue
: 초기 값, 마지막으로 생성된 값이 기준, 기본 값은 0allocationSize
: sequence를 한번 호출 할 때 증가하는 수(성능최적화에 사용), 기본값은 50catalog, schema
: Db의 catalog와 schema의 이름uniqueConstraints(DDL)
: unique 제약 조건 설정기본 키는 유일하고 절대 변하면 안된다. 하지만 미래에 이 조건을 만족하는 자연키를 찾기는 어려우므로 대체키를 사용하는것이 좋다.
ex) Long형 + 대체키 + Custom Key 생성 전략
@Column 속성 | 설명 | 기본값 |
---|---|---|
name | 객체 필드명과 Db 테이블의 컬럼명이 다를 경우 컬럼명을 넣어서 매핑할 수 있음 | 객체의 필드 이름 |
insertable, updatable | 해당 컬럼이 insert 되거나 update될 경우 이를 Db에 반영할 것인지 | TRUE |
nullable(DDL) | 해당 컬럼에 null을 허용할 것인지 설정할 수 있음 | |
unique(DDL) | 해당 컬럼에 Unique 제약 조건을 걸 수 있으나 이름이 실제 운영에선 사용하기 어렵게 생성됨 (@Table의 uniqueConstraints는 이름까지 설정 가능) | |
length | 길이 제약 조건을 설정할 수 있고 String 타입에만 사용 | 255 |
columnDefinition(DDL) | Db 컬럼을 직접 설정 | 필드의 타입과 방언 정보 사용 |
precision | BicDecimal 타입에서 사용(BigInteger도 가능) 소수점을 포함한 전체 자릿수 (아주 큰 숫자 또는 정밀한 소수를 다룰 때 사용하기 때문에 double, float 타입에는 적용되지 않음) | 19 |
scale(DDL) | BicDecimal 타입에서 사용(BigInteger도 가능) 소수의 자릿수 (아주 큰 숫자 또는 정밀한 소수를 다룰 때 사용하기 때문에 double, float 타입에는 적용되지 않음) | 2 |