JPA에서 중요한게 영속성 개념이랑 엔티티 매핑 개념이다. 여기선 이게 뭔지 좀 소개한다.
@Entity는 JPA에서 관리해주는 클래스다. 그래서 JPA를 사용해서 테이블과 매칭할 때는 @Entity를 붙여야 한다. 주의점은 얘가 동적인 작업을 내부에서 수행하기 때문에 기본 생성자는 만들어야 하고, fianl, enum, interface, inner는 안되고 필드 안에서 final 사용은 안된다.
속성은 name이 있는데 jpa 내부에서 구분하기 위한 거다. 그냥 기본값 쓰거나 겹치면 바꾸면 된다.
@Table이 붙은건 엔티티와 매핑할 테이블을 지정해주는 것. @Entity만 해도 테이블을 만들지만 테이블 이름을 다르게 해야 하는 등의 문제가 있을 때 속성을 직접 바꿔줄 수 있다.
@Entity이 붙은 엔티티를 자동생성 하게 할 수 있는 hibernate.hbm2ddl.auto 속성이 있고 옵션은 create, create-drop, update, validate, none 5개가 있다. 이걸 사용하면 지금까지 h2 콘솔에 가서 create로 테이블 만들고 실행하던 것과 달리 직접 만들어서 한다는 것.
create는 있던걸 drop하고 처음부터 새로 만든다. create-drop은 이 drop을 끝에도 한다.
update는 변경분만 새로 만들어서 한다. 없어지는 부분은 drop하지 않는다.
validate는 이전에 내가 미리 콘솔에 가서 sql문 쳐서 테이블을 만들거나 수정해야 하고 이것과 엔티티와 매칭되는지 보고안되면 에러뜨고 종료된다.
none은 그냥 아무것도 안하는 것.
... 사실 보면 알겠지만 자바에서 몇 글자 바꾸는 것만으로 db가 자동으로 alter, update, drop하는 행위들은 정말 정말 굉장히 위험하다. 그래서 개발을 위한 로컬 서버에서만 create를 사용하고, 테스트 서버 및 운영서버에선 절대 절대 사용하지 말자. 써봤자 validate정도지만, 얘도 서버 돌리는데 에러가 뜨는것 자체가 좀 위험하기 때문에 그냥 안쓰는게 나을 것 같다. 그럼 sql 스크립트는 귀찮은데 어떻게 짜냐 하겠지만 로컬 서버에서 create를 해보고 나온 sql문을 직접 보고 적절히 수정해서 운영서버에 치는 방식으로 실수를 방지하자.
@Column 속성으로 제약조거을 추가할 수도 있다.
이 DDL 생성 기능은 DDL을 자동으로 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다. sql문 몇 줄만 더 친다는 소리.
이 @Column 말고도 다른 타입들도 있다.
@Column에선 nullable 정도만 사용한다. unique는 뒤에 붙은 이름도 알 수 없어서 @Table로 사용하는 편임.
@Enumerate는 enum 지정하는 거인데 무조건 STRING으로 하기. @Temporal은 날짜인데 요즘은 그냥 LocalDateTime 쓰면 된다. @Lob은 매핑하는 필드타입이 문자(String, char[], java.sql.CLOB)면 clob, 나머지는 blob(bytep[,java.sql.BLOB)의 이름으로 컬럼이 지정됨. @Transient는 컬럼에 저장하지 않기. 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용한다고 한다.
다음은 pk인 Id 생성 전략. 직접 할당은 해왔던 것 처럼 id를 직접 지정하는 거지만 자동 생성이 있다. 일단 기본적인 @GeneratedValue로 db가 알아서 생성해주는걸 보자. 사실 자동생성 종류는 3가지 IDENTITY, SEQUENCE, TABLE이 있고 이 중 알아서 선택한는 AUTO가 있다.
일단 쉬운 IDENTITY를 보자.
기본적으로 자동 생성으로 바꾸면 db에 넘길 때 id값을 null로 하여 넘긴다. 그래서 db가 자동 생성한다. sql문을 jpa가 적어서 보댈 때도 generated by default as identity가 붙은 걸 볼 수 있다.
SEQUENCE를 적용하면 지정한 값부터 차례대로 올라간다. 이런 문법도 역시 방언이라 각 db마다 조금씩 다르다. 잘 볼만한 건 start with 1 increment by 1로 1에서 시작하고 1만큼만 올린다는 걸 눈여겨보자. 이따 sequence generator에서 미리 할당하는 전략과 관련이 있다.
제약조건도 잘 하고 pk는 주민번호 같은 비즈니스 값 말고 다른 생성 전략에 Long 타입을 처음부터 해주는것이 나중에 id값을 바꾸지 않아 미래에 피곤한 일이 일어나지 않는다.
사실 여기까지만 알아도 id 자동 생성을 사용할 수 있다. generate는 성능 최적화 영역이기 때문. 그래서 generate는 정의 안하고 그냥 기본만 써도 되지만 안의 원리는 알아두어야 한다. 그래서 아까 넘어간 @GeneratedValue의 SEQUENCE와 TABlE의 generator를 보자.
엔티티의 어노테이션으로 generate를 적용하고 어떤(id) 컬럼에 어떤 generator를 적용할건지 설정할 수 있다. 기본적으론 allocationSize가 50인데, 이게 무슨 말이냐면 id를 자동생성으로 바꾸면 컨텍스트의 1차 캐시에서 값을 저장하는 방식이 id가 key고 value가 엔티티 객체인데 id값은 db에 저장되어야 알 수 있다. 즉, commit하기 전에 1차 캐시에 넣는 행위인 persist를 해도 commit을 하여 저장하여 나온 id를 가지고 와서 사용한다는 것. 근데 이러면 캐시 써가며 최적화 한 의미가 없으니 갔다 올 때 allocationSize만큼을 내가 쓸 거라고 미리 할당받고 온다. 그래서 1차 캐시에 allocationSize만큼이 쌓일 때 까지는 db에 보내지 않고 저장했다가 한번에 보낸다. 이런 식으로 작동하여 캐시에 올릴 때마다 통신하는 성능 낭비를 줄일 수 있다. 이게 가상이 아니라 db에서 실제로 존재하는 기능을 사용하는 거다.
잘 보면 start with 1 increment by 50으로 되어있어 처음에 id 얻을려고 db에 가서 call next value for member_seq로 id값 받아온 뒤에 두번째는 굳이 id값을 얻으려 가지 않는걸 볼 수 있다.
table 전략은 id를 생성하는 순서를 db table를 만들어 따로 저장하는 방식이다. table를 따로 만드는 부담도 있고 해서 사실 잘 안쓴다고 함.
이제 실습 프로젝트를 만들면서 하나씩 늘려나가는 방식으로 한다.
그래서 일단 요구사항은 회원은 상품을 주문할 수 있고, 주문 시 여러 종류의 상품을 선택할 수 있다.
이 엔티티 설계대로 실제 정의해본다.
id 이름은 테이블 이름에 뒤에 _id 붙이는 게 관례임. 회사 사정에 따라 바뀔 수 있다.
이렇게 하고 maven으로 만든거라 persistence.xml 설정파일 건드려서 auto create로 바꾸고 실행하면 sql문 작성해서 잘 만들어진다.
Hibernate:
drop table Item if exists
Hibernate:
drop table Member if exists
Hibernate:
drop table OrderItem if exists
Hibernate:
drop table ORDERS if exists
Hibernate:
drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate:
create table Item (
ITEM_ID bigint not null,
name varchar(255),
price integer not null,
stockQuantity integer not null,
primary key (ITEM_ID)
)
Hibernate:
create table Member (
MEMBER_ID bigint not null,
city varchar(255),
name varchar(255),
street varchar(255),
zipcode varchar(255),
primary key (MEMBER_ID)
)
Hibernate:
create table OrderItem (
ORDER_ITEM_ID bigint not null,
count integer not null,
ITEM_ID bigint,
ORDER_ID bigint,
orderPrice integer not null,
primary key (ORDER_ITEM_ID)
)
Hibernate:
create table ORDERS (
ORDER_ID bigint not null,
MEMBER_ID bigint,
orderDate timestamp,
status varchar(255),
primary key (ORDER_ID)
)
9월 17, 2023 12:08:49 오전 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@7c447c76] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
9월 17, 2023 12:08:49 오전 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@6009bea] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
9월 17, 2023 12:08:49 오전 org.hibernate.tool.schema.internal.SchemaCreatorImpl applyImportSources
INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@664e5dee'
9월 17, 2023 12:08:49 오전 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH10001008: Cleaning up connection pool [jdbc:h2:tcp://localhost/~/jpashop]
Process finished with exit code 0
언듯 보면 잘 된 것 같지만 구조적인 문제가 남아있다.
jpa를 도입해서 자바 객체로써만 생각하려고 했는데 id를 참조하는 방식이다 보니 주문에서 멤버를 가져오려면 멤버 아이디를 알아내고 이걸로 다시 찾고.. 하는걸 해야 한다. 이런걸 데이터 중심 설계라고 함. 그러나 자바 방식이라면 주문에서 바로 멤버를 꺼내와야 한다. 결국 자바 객체적인 접근도 못하고 이렇게 참조해야 한다는 내용도 없으므로 UML도 잘못되었다.
그래서 다음에 연관관계 매핑에 대해 알아볼거임.
'CS > 김영한 스프링 강의' 카테고리의 다른 글
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 섹션 6. 다양한 연관관계 매핑 (0) | 2023.09.21 |
---|---|
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 섹션 5. 연관관계 매핑 기초 (0) | 2023.09.18 |
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 섹션 3. 영속성 관리 - 내부 동작 방식 (0) | 2023.09.15 |
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 섹션 2. JPA 시작하기 (0) | 2023.09.14 |
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 섹션7. 웹 계층 개발 (0) | 2023.09.14 |