이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
기초개념 › JPA
엔티티는 무엇이고, 일반 클래스와 무엇이 다른가
엔티티는 데이터베이스 테이블과 짝이 되는 자바 클래스이고, 그 객체 하나가 테이블의 한 행과 짝이 됩니다. 코드로 보면 class로 선언한 평범한 자바 클래스인데, 일반 클래스와 다른 곳은 두 군데입니다. 클래스 위의 @Entity는 이 클래스의 객체를 테이블에 저장한다는 표시이고, 필드 위의 @Id는 그 필드가 기본 키(행을 구별하는 열)라는 표시입니다. 이 둘이 있으면 JPA(여기서는 Hibernate)가 클래스의 필드를 보고 테이블의 열과 짝을 짓고, em.persist로 객체를 넘겨 두면 커밋할 때 INSERT 문을 대신 만들어 실행합니다. 우리가 SQL을 쓰지 않습니다. 여기서 em은 JPA에 저장을 시키는 EntityManager 객체입니다. 이 실습은 두 표시를 하나씩 지워 가며 같은 저장 코드를 실행해, 테이블이 만들어지는지와 INSERT 문이 나가는지를 봅니다.
실행으로 대조 · Hibernate 7.4.5와 H2 2.4.240에서 여덟 가지 조합을 소스째 컴파일해 JPA를 실제로 시작하고 저장해 대조
// 표시: 이 클래스의 객체는 테이블의 한 행으로 저장된다
@Entity
public class Member {
// 표시: 이 필드가 기본 키다
@Id
private Long id;
private String name;
protected Member() { }
public Member(Long id, String name) {
this.id = id;
this.name = name;
}
}
// 저장하는 쪽
em.getTransaction().begin();
em.persist(new Member(1L, "김철수"));
em.getTransaction().commit();
Hibernate 7.4.5와 H2 2.4.240(메모리 DB)에서 실행해 대조했습니다. 스프링은 쓰지 않고 Hibernate를 코드로 직접 시작했습니다(persistence.xml 없이 Configuration에 클래스를 등록). 여덟 가지 조합을 모두, 화면의 Member 소스와 같은 글(package와 import 줄만 더함)을 컴파일하고 자바 프로세스를 새로 띄워 확인했습니다. @Entity가 없는 조합에서도 같은 방식으로 Member를 등록했습니다. 화면의 "저장하는 쪽" 세 줄은 실제로는 main 안의 코드이고, EntityManager(em)를 얻는 줄, 예외를 잡아 기록하는 줄, 측정하는 줄은 화면에서 줄였습니다. 대조한 것은 JPA가 시작하다가 실패하는지와 예외의 종류, 시작 직후와 실행 뒤 H2에 있는 테이블과 열(INFORMATION_SCHEMA.COLUMNS), StatementInspector로 잡은 INSERT 문의 수와 글(persist 줄이 끝난 때와 commit 줄이 끝난 때), 실행 뒤 테이블의 행 수와 내용입니다. 테이블 이름은 H2가 MEMBER로, 열 이름은 ID, NAME, EMAIL로 저장하는데, 화면에는 읽기 쉽게 테이블 이름은 첫 글자만 대문자로, 열 이름은 소문자로 적었습니다. 열은 필드를 적은 순서대로 만들어지지 않았습니다(이 실행에서는 id, email, name 순서). 순서를 정하는 규칙은 확인하지 않았습니다. 문서가 정한 것은 Jakarta Persistence 3.2 명세 2.1절(엔티티 클래스는 @Entity를 갖고 public 또는 protected의 인자 없는 생성자를 가져야 하며 final이 아니어야 한다)과 2.4절(모든 엔티티에는 기본 키가 있어야 하고 단순한 기본 키는 필드에 @Id를 붙여 선언한다), Hibernate ORM 7.0 사용자 가이드 3.4절(Entity types)의 3.4.1, 3.4.3, 3.4.5, 3.4.6항(테이블 이름은 기본으로 엔티티 이름을 따르고, 필드에 @Id를 붙이면 필드 접근이다)입니다. 이 문서들은 WebFetch로 열었고, 돌려받은 것은 원문이 아니라 요약이어서 문장을 하나하나 원문과 대조하지는 못했습니다. 명세는 앞부분만(2.1절과 2.4절이 있는 앞 10만 자) 읽었습니다. 아래는 문서가 정한 것이 아니라 이 버전에서 관찰한 동작입니다. @Entity가 없는 클래스를 등록하면 오류 없이 시작을 마치고(JPA가 아는 엔티티는 없음) em.persist에서 IllegalArgumentException이 나며, 이때 @Id가 있어도 결과가 같습니다. @Entity는 있고 @Id가 없으면 시작할 때 AnnotationException이 나서 테이블이 만들어지지 않습니다(Hibernate 7.0 사용자 가이드 3.4.5항은 식별자를 생략하는 것이 예전에는 허용됐으나 폐지 예정이라고 하는데, 7.4.5에서는 오류였습니다). 테이블은 persist나 commit 때가 아니라 JPA가 시작할 때 만들어지며(hibernate.hbm2ddl.auto=create-drop 설정), INSERT 문은 persist 줄이 아니라 commit 줄에서 나갔습니다. 문장은 insert into Member (name,id) values (?,?) 모양이고 Hibernate 버전에 따라 글자가 다를 수 있으며, 그 INSERT가 persist 때 나가는지 commit 때 나가는지 같은 세부는 "영속성 컨텍스트" 실습에서 다룹니다. 화면의 코드에서 줄인 것은 package와 import 줄이고, 필드는 id, name, email만 다뤘습니다. 이름과 열의 짝을 바꾸는 @Table, @Column, 기본 키 값을 자동으로 만드는 @GeneratedValue, 접근자 메서드, 엔티티 클래스의 나머지 조건(final이 아니어야 한다 등)은 다루지 않았습니다. MySQL에서의 실행 대조는 하지 않았습니다.
줄을 그은 곳은 처음 코드에서 지운 것이고, 색을 칠한 곳은 새로 쓴 것입니다.