코드 그라데이션
<보충설명> 기본키 매핑 본문
보충
이 IDENTITY 전략은 Id를 내가 직접 넣으면 안 된다.
Id가 NULL로 날아오면 그 때 DB에서 Id 값을 직접 세팅해준다.
뭐가 문제냐,id 값을 알 수 있는 시점이 DB에 값이 들어가봐야 안다는 것.그런데 영속성 컨텍스트에서 관리가 되려면 무조건 PK 값이 있어야 했다.
그래서 어떤 제약이 생기느냐...JPA는 PK를 모르니 값을 넣을 수 있는 방법이 없다는 것.
울며 겨자먹기로, IDENTITY 전략에서만 em.persist() 호출하자마자 그 시점에
Hibernate:
/* insert inflearn.exjpa.Member
*/ insert
into
Member
(id, name)
values
(null, ?)
이렇게 INSERT 쿼리를 날려버린다.
(보통 커밋시점에 INSERT 쿼리 날아간다고 함)
아이디 출력해서 로그를 확인해보면
======================
16:31:18.246 [main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
16:31:18.251 [main] DEBUG org.hibernate.SQL -
/* insert inflearn.exjpa.Member
*/ insert
into
Member
(id, name)
values
(default, ?)
Hibernate:
/* insert inflearn.exjpa.Member
*/ insert
into
Member
(id, name)
values
(default, ?)
16:31:18.256 [main] DEBUG org.hibernate.id.IdentifierGeneratorHelper - Natively generated identity: 1
16:31:18.256 [main] DEBUG org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl - HHH000387: ResultSet's statement was not registered
member.id = 1 // 이렇게 잘 나와있고
======================
이것도 잘 들어가 있음.
Select 쿼리는 왜 안 나오느냐?
Insert 쿼리를 했을 때 이런 경우에
값을 넣고 리턴을 받는 로직이 내부적으로 이미 다 구현이 되어 있다.
그래서 DB에 INSERT하는 시점에 이 Id 값을 바로 알 수 있는 것.
따라서. 모아서 INSERT 하는 게 IDENTITY 전략에서는 불가능하다!
한 트랜잭션 안에서 INSERT 쿼리가 여러번 나간다고 해서 성능에 비약적인 차이를 초래하거나 하진 않으니
크게 상관은 없는 부분이다.
만약 SEQUENCE로 바꾸거나 하면 실제 커밋하는 시점에 쿼리가 날아간다.
SEQUENCE
SEQUENCE 전략 - 매핑
일단 이렇게 바꾸고 실행을 시켜보면,
drop table if exists Member CASCADE
Hibernate:
drop table if exists Member CASCADE
16:44:52.082 [main] INFO org.hibernate.orm.connections.access - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@85ec632] 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.
16:44:52.084 [main] DEBUG org.hibernate.SQL -
drop sequence if exists MEMBER_SEQ
Hibernate:
drop sequence if exists MEMBER_SEQ
16:44:52.087 [main] DEBUG org.hibernate.SQL - create sequence MEMBER_SEQ start with 1 increment by 1
Hibernate: create sequence MEMBER_SEQ start with 1 increment by 1
16:44:52.087 [main] INFO org.hibernate.orm.connections.access - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@1c4ee95c] 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.
16:44:52.089 [main] DEBUG org.hibernate.SQL -
create table Member (
id bigint not null,
name varchar(255) not null,
primary key (id)
)
Hibernate:
create table Member (
id bigint not null,
name varchar(255) not null,
primary key (id)
)
16:44:52.091 [main] DEBUG org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - No JtaPlatform was specified, checking resolver
16:44:52.092 [main] DEBUG org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformResolverInitiator - No JtaPlatformResolver was specified, using default [org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver]
16:44:52.096 [main] DEBUG org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver - Could not resolve JtaPlatform, using default [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
16:44:52.096 [main] INFO org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
16:44:52.100 [main] DEBUG org.hibernate.service.internal.SessionFactoryServiceRegistryImpl - EventListenerRegistry access via ServiceRegistry is deprecated. Use `sessionFactory.getEventEngine().getListenerRegistry()` instead
16:44:52.102 [main] DEBUG org.hibernate.hql.internal.QueryTranslatorFactoryInitiator - QueryTranslatorFactory: org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory@7668d560
16:44:52.105 [main] DEBUG org.hibernate.query.spi.NamedQueryRepository - Checking 0 named HQL queries
16:44:52.105 [main] DEBUG org.hibernate.query.spi.NamedQueryRepository - Checking 0 named SQL queries
16:44:52.106 [main] DEBUG org.hibernate.internal.SessionFactoryRegistry - Initializing SessionFactoryRegistry : org.hibernate.internal.SessionFactoryRegistry@54e81b21
16:44:52.108 [main] DEBUG org.hibernate.internal.SessionFactoryRegistry - Registering SessionFactory: d66652e6-f53e-4616-bd06-30c21817f735 (<unnamed>)
16:44:52.108 [main] DEBUG org.hibernate.internal.SessionFactoryRegistry - Not binding SessionFactory to JNDI, no JNDI name configured
16:44:52.164 [main] DEBUG org.hibernate.stat.internal.StatisticsInitiator - Statistics initialized [enabled=false]
16:44:52.169 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
16:44:52.169 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl - begin
=======================
16:44:52.174 [main] DEBUG org.hibernate.SQL -
call next value for MEMBER_SEQ
Hibernate:
call next value for MEMBER_SEQ
16:44:52.177 [main] DEBUG org.hibernate.id.enhanced.SequenceStructure - Sequence value obtained: 1
16:44:52.178 [main] DEBUG org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl - HHH000387: ResultSet's statement was not registered
16:44:52.181 [main] DEBUG org.hibernate.event.internal.AbstractSaveEventListener - Generated identifier: 1, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
member.id = 1
=======================
create sequence MEMBER_SEQ start with 1 increment by 1 이 부분이 있다.
1부터 시작하고 1씩 증가시키라는 것
그런데 이 아이디도 시퀀스는 DB에서 관리하기 때문에 DB를 까봐야 알 수 있다.
이 값을 애플리케이션으로 가져와야 함.
em.persist(member);를 하려면 일단 영속성 컨텍스트에 넣어야 하는데,
그럼 반드시 PK를 알아야 해서.시퀀스에서 미리 pk를 가져옴
그다음에 영속성 컨텍스트에 저장.
아직 DB에 인서트 쿼리는 안 날아가고 실제 커밋 시점에 날아간다.
자꾸 네트워크를 왔다갔다 해야 하지 않느냐. 성능 문제는??
이걸 활
SEQUENCE - @SequenceGenerator
@Entity
@NoArgsConstructor
@SequenceGenerator(
name = "MEMBER_SEQ_GENERATOR",
sequenceName = "MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
initialValue = 1, allocationSize = 50)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "MEMBER_SEQ_GENERATOR")
private Long id;
}
이걸 저장을 할 때마다 next call로 가져오면 성능 문제가 나타날 수 있기 때문에
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member1 = new Member();
member1.setUsername("A");
Member member2 = new Member();
member2.setUsername("B");
Member member3 = new Member();
member3.setUsername("C");
System.out.println("==============");
em.persist(member1);
em.persist(member2);
em.persist(member3);
System.out.println("==============");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
allocationSize = 50 이렇게 미리 DB에 50개를 얹어놓고, 메모리는 1씩 쓰는 것.
DB에 미리 올려놓고 메모리에서는 그 개수만큼 쓰는 방식이다.
em.persist는 빼고 일단 실행 눌러보면
drop table if exists Member CASCADE
Hibernate:
drop table if exists Member CASCADE
17:22:34.525 [main] INFO org.hibernate.orm.connections.access - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@1a1d3c1a] 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.
17:22:34.527 [main] DEBUG org.hibernate.SQL -
drop sequence if exists MEMBER_SEQ
Hibernate:
drop sequence if exists MEMBER_SEQ
17:22:34.531 [main] DEBUG org.hibernate.SQL - create sequence MEMBER_SEQ start with 1 increment by 50
Hibernate: create sequence MEMBER_SEQ start with 1 increment by 50
17:22:34.531 [main] INFO org.hibernate.orm.connections.access - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@6ac4944a] 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.
17:22:34.533 [main] DEBUG org.hibernate.SQL -
create table Member (
id bigint not null,
name varchar(255) not null,
primary key (id)
)
Hibernate:
create table Member (
id bigint not null,
name varchar(255) not null,
primary key (id)
)
create sequence MEMBER_SEQ start with 1 increment by 50 이 메시지 확인할 수 있다.
DB열어보면
여기서 em.persist(member1); 만 풀어서 실행해보면
Hibernate:
call next value for MEMBER_SEQ 이게 두 번 호출이 되고
바뀐다.
이제 다 풀어서 3개 다 em.persist() 하면
em.persist(member1); // 1, 51
em.persist(member2); // 메모리에서
em.persist(member3); // 메모리에서
51번을 만나는 순간 넥스트 콜이 한 번 더 실행된다는 느낌.
일단 이정도로 이해
@TableGenerator 속성에서도 똑같다.
이거 말이다.
간단히 정리하자면
Table에 AUTO_INCREMENT를 50개를 미리 쫙 해놓고 웹 서버에서 50개를 쭉 써가는 방식이라고 이해하면 편하다.
미리 값을 올려두는 방식이기 때문에 서버가 여러대이더라도 괜찮다.
'Spring > JPA 공부' 카테고리의 다른 글
단방향 연관관계 (0) | 2023.08.18 |
---|---|
실전 예제 1 - 요구사항 분석과 기본 매핑 (0) | 2023.08.17 |
엔티티 매핑 4 - 기본키 매핑 (0) | 2023.08.16 |
엔티티 매핑 3 - 필드와 컬럼 매핑 (0) | 2023.08.15 |
엔티티 매핑 2 - 데이터베이스 스키마 자동 생성 (0) | 2023.08.15 |