SBE_SE_43 / 23.02.23 회고

rse·2023년 2월 23일
0

코드스테이츠_BE_43

목록 보기
41/65

오늘

  • JPA

JPA

JPA란?

Java Persistence API 라는 뜻으로 java에서 사용하는 ORM(Object-Relational Mapping) 기술의 표준 사양이다.

내부구조를 구면 JAP를 거쳐 Hibernat ORM을 통해 데이터의 저장, 조회 등의 작업이 이루어진다.

Hibernat ORM은 내부적으로 JDBC API를 이용해서 데이터베이스에 접근한다.

특징

Persistence = 즉 영속성
영속성 컨텍스트(Persistence Context)에 보관해서 애플리케이션 내에서 오래 지속 되도록 한다.
SQL 쿼리문을 직접 작성하지 않아도 된다.
또, SQL 중심의 개발에서 객체 중심 개발로 바뀐다.

Hibernate ORM은 JPA에서 정의해둔 인터페이스를 구현한 구현채이기 때문에 JPA기능 외에 Hibernate 자체적으로 사용할 수 있는 API를 지원하고 있다.

왜 써야하는가?

위에서 말한 특징과 같은 내용이다.

  • 객체 중심 개발이 가능하다.
  • 생산성 향상
SQL 스키마를 일일이 적지 않아도 자동으로 반영됨.
em.persist
em.find
em.set
em.remove
  • 성능 최적화
1차 캐시와 동일성(identity) 보장
같은 트랜잭션 안에서는 같은 엔티티를 반환하여 약간의 조회 성능을 향상 시킨다. 
즉, 같은 객체를 연달아 두번 조회할 때 SQL을 한번만 쓴다. 

트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
트랜잭션을 커밋할 때까지 insert SQL을 모으고, JDBC BATCH SQL 기능을 사용하여 한번만 SQL을 전송하도록 한다. 
JDBC BATCH SQL은 원래 코드가 복잡한데, JPA를 사용하면 옵션하나만 키면된다.

지연로딩?
= 객체가 실제 사용될 때 로딩되는 것을 말한다.

사용해보자

ddl-auto: create 를 사용하면 스키마가 자동 생성된다.
show-sql : true 를 사용하면 sql 쿼리가 출력된다.


@Entity 애너테이션과 @Id 애너테이션을 붙어주면 JPA 에서 엔티티 클래스로 인식을 한다.

JPA 영속성 컨텍스트는 EntityManager 클래스에 의해서 관리된다.
EntityManager 의 객체는 EntityManagerFactory 클래스의 객체를 Spring 으로 부터 DI(의존성 주입) 받을 수 있다.
그리고 EntityManager클래스의 객체는 EntityManagerFactory에 정의된 메서드인 createEntityManager 를 통해 EntitytManager 클래스에 객체를 얻을 수 있다.


이건 EntityManagerFactory에 정의된 메서드? 구현체? 라고 할 수 있다.

em.persist는 영속성 컨텍스트에 괄호안에 객체의 정보가 저장된다.
member에 객체들의 정보.

그리고 find를 통해서 Member.class, 1L로 조회를 하는데
사용법 find(조회할 엔티티 클래스 타입, 식별자 값)

코드를 실행해보면 Hibernate 이라고 나오는데 이게 아까 위에서 말한 Hibernate ORM 이 된다.

현재 내부적으로 테이블을 생성하고 PK도 member_id로 지정되었다.
하지만 이렇게만 하면 영속성 컨텍스트에 member객체를 저장하지만 실제 DB 테이블에는 저장을 하지않는다.


코드 실행결과를 보면 insert into member로 실제 데이터베이스에 저장하고 있는 것을 알 수 있다.

h2 사이트에서도 저장된 것을 확인.

JPA에서는 Transaction 객체를 기준으로 데이터베이스의 테이블에 데이터 저장.
현재 Transaction 객체 -> tx
em에게서 getTransaction으로 얻음.

Transaction을 시작하기 위해 begin 메서드 호출.
commit을 호출하는 시점에 영속성 컨텍스트에 저장되어 있는 member 객체를 데이터베이스의 테이블에 저장.

처음에 find 호출했을 때는 영속성 컨텍스트에 저장한 member객체를 1차 캐시에서 조회.
정보가 있기 때문에 select 쿼리 전송 안한다.

두번째에 호출했을 때는 member객체가 없기 때문에 마지막 결과는 true가 나오고, 영속성 컨텍스트에 memberId2가 없기 때문에 테이블에 직접 select 쿼리를 전송한다.


테이블에 정상적으로 데이터가 저장된 것을 확인 할 수 있다.

이미 저장되어 있는 데이터의 정보를 업데이트(수정) 하려면 set을 이용해 메서드로 값을 변경하면 된다.

코드를 실행하면 update가 잘 된 것을 볼 수 있다.

set을 이용해서 값을 변경하고 commit을 해줬을 뿐인데?

update 쿼리가 실행되는 이유는 영속성 컨텍스트에 엔티티가 저장될 경우에는 저장되는 시점의 상태를 그대로 가지고 있는 스냅샷을 생성한다.

그 후에 setter를 이용해서 값을 변경한 후 tx.commit을 하면 변경된 엔티티와 이전 스냅샷을 비교한 뒤 변경된 값이 있으면 SQL저장소에 update쿼리를 등록하고 실행하는 것이다.


이번에는 member 객체를 삭제해줬다.
remove를 이용해서 쉽게 삭제 할 수 있다.

Entity 매핑

기본키 생성 전략

  1. 기본키 직접 할당
  2. 자동 생성
  • IDENTITY
  • SEQUENCE
  • TABLE

직접 할당 전략 코드

엔티티에 직접 할당 (@Id)

직접 할당해서 저장.

이번에는 자동 생성으로 해보자.
IDENTITY

SEQUENCE

잘 생성된 것을 확인 할 수 있다.

AUTO

JPA가 데이터베이스의 Dialect에 따라서 적절한 적략을 자동으로 선택해줌.

Dialect?

표준 SQL 등이 아닌 특정 데이터베이스에 특화된 고유한 기능을 말한다.

추가 사항

IDENTITY = 데이터 하나를 넣을 때마다 ID를 증가

commit을 하지 않아도 데이터베이스에 입력이 된다.
1차 캐시에는 식별자가 반드시 있어야 하는데 idntity는 식별자를 입력한 후 생성.
그렇기 때문에 데이터베이스에 반영이 됨. 자동으로.

반면 SEQUENCE는 반드시 COMMIT을 해야지 데이터베이스에 반영됨.

필드(멤버 변수) 컬럼 간의 매핑

@Column 필드와 컬럼을 매핑해주는 애너테이션.
만약 @Column 애너테이션이 없고 필드에 정의만 되어있다면 JPA는 기본적으로 테이블의 컬럼과 매핑되는 필드라고 간주. 애트리뷰트 값은 디폴트로 적용.
@nullable

null 허용할지 말지
디폴트 값 : true

@updatable

수정이 가능한지 여부
디폴트 값 : true

@unique

고유한 값
디폴트 값 : false

@Column 애너테이션이 생략되거나 애트리뷰트가 기본값일 경우 주의사항

int나 long 같은 원시 타입일 경우 애너테이션이 생략되면 기본적으로 nullable = false.
기본값이 false라는 뜻.
그렇기에 설명없이 @Column 애너테이션만 붙여버리면 int나 long타입 같은 경우 nullable이 true가 됨.

그냥 @Column(nullable=false)라고 정하던지 아니면 안붙이는게 좋다.

현재 set을 이용해 값을 변경하려고 했지만 update=false를 했기 때문에 수정이 되지 않은 모습이다.


@length = 문자 길이 (디폴트 값 : 255)
LocalDateTime = 회원 정보가 등록될 때의 시간, 날짜
@Transient = 테이블 컬럼과 매핑하지 않겠다는 의미. 주로 임시 데이터를 메모리에서 사용하기 위한 용도로 많이 사용.

coffee, order 엔티티 클래스도 매핑.

@Enumerated = enum 타입과 매핑 할 때 사용.

  • EnumType.ORDINAL : 순서를 나타내는 숫자를 테이블에 저장.
  • EnumType.STRING : 이름을 테이블에 저장.

Entity 와 table 매핑 권장 사용 방법

  • 클래스 이름 중복 등의 다른 특별한 이유가 없다면 @Entity와 @Id 애너테이션만 사용하자.
  • 기본키 생성 전략은 데이터 베이스에서 지원해주는 auto_increment 또는 sequence를 이용 하자. -> IDENTITY, SEQUENCE
  • Entity 클래스 필드 타입이 원시타입(int, long등) 일 경우 @Column 애너테이션을 생략하는 방식보다는 nullable = false 설정을 하는 것이 좋다.
  • @EnumType.ORDINAL 을 사용할 경우 ENUM의 순서가 뒤바뀔 가능성도 있으므로 처음부터 STRING 타입을 사용하도록 하자.
profile
기록을 합시다

0개의 댓글