2025.03.26 ~ 03.27
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
<!-- 영속성 유닛 설정 (JPA 설정 단위) -->
<persistence-unit name="persistenceUnit이름">
<!-- JPA로 관리할 엔티티 클래스 등록 -->
<class>엔티티패키지경로1.엔티티클래스명</class>
<class>엔티티패키지경로2.엔티티클래스명</class>
<properties>
<!-- JDBC 설정 -->
<property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/DB이름"/>
<property name="jakarta.persistence.jdbc.user" value="DB사용자명"/>
<property name="jakarta.persistence.jdbc.password" value="DB비밀번호"/>
<!-- SQL 출력 설정 (디버깅용) -->
<!-- 실행되는 SQL 출력 -->
<property name="hibernate.show_sql" value="true"/>
<!-- SQL 보기 좋게 정렬 -->
<property name="hibernate.format_sql" value="true"/>
<!-- DDL 자동 생성 전략 (옵션: none, create, update, create-drop 등) -->
<!-- 개발 중에는 create / 운영에서는 none 또는 validate 권장 -->
<property name="hibernate.hbm2ddl.auto" value="옵션선택(create 등)"/>
</properties>
</persistence-unit>
</persistence>
public class EntityMappingTests {
private static EntityManagerFactory entityManagerFactory;
private static EntityManager entityManager;
@BeforeAll
public static void initFactory() {
<!-- persistence.xml의 persistence-unit 이름 사용 -->
entityManagerFactory = Persistence.createEntityManagerFactory("퍼시스턴스유닛이름");
}
@BeforeEach
public void initManager() {
entityManager = entityManagerFactory.createEntityManager();
}
@AfterAll
public static void closeFactory() {
entityManagerFactory.close();
}
@AfterEach
public void closeManager() {
entityManager.close();
}
<!-- 여러 테스트 -->
}
@Test
public void createEntityTest() {
<!-- given : 테스트할 엔티티 인스턴스 생성 및 값 설정 -->
엔티티클래스명 엔티티변수명 = new 엔티티클래스명();
엔티티변수명.set기본키필드(기본키값);
엔티티변수명.set필드1("특정값1");
엔티티변수명.set필드2("특정값2");
엔티티변수명.set필드3("특정값3");
<!-- when : 엔티티 저장 (영속성 컨텍스트 등록) -->
entityManager.persist(엔티티변수명);
<!-- then : DB에 저장된 데이터와 동일한지 확인 -->
엔티티클래스명 조회할엔티티변수명 = entityManager.find(엔티티클래스명.class, 엔티티변수명.get기본키필드);
Assertions.assertEquals(엔티티변수명.get기본키필드(), 조회할엔티티변수명.get기본키필드());
}
@Entity(name = "엔티티_논리이름") <!-- 선택: 다른 클래스와 이름 겹칠 때 사용 -->
@Table(name = "테이블_이름")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter <!-- 실제 서비스에서는 Setter 사용을 제한하는 것이 일반적 (보안/불변성 등) -->
@ToString
public class 클래스명 {
@Id
@Column(name = "기본키_컬럼명")
<!-- @GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 증가 시 사용 -->
private 자료형 기본키필드;
@Column(name = "컬럼명1")
private 자료형 필드명1;
@Column(name = "컬럼명2", length = 100)
private String 필드2;
@Column(name = "컬럼명3", columnDefinition = "varchar(200) default '기본값'")
private String 필드3 = "기본값";
<!-- null 허용 여부 (nullable = false → NOT NULL 제약) -->
@Column(name = "컬럼명4", nullable = false)
private 자료형 필드4;
<!-- 숫자 precision/scale (소수 포함) -->
@Column(name = "컬럼명5", precision = 10, scale = 2)
private BigDecimal 필드5;
<!-- 날짜 타입 컬럼 -->
@Column(name = "컬럼명6")
private LocalDate 필드6;
<!-- 테이블 생성 시 제외 (영속성 컨텍스트에서는 존재하지만 DB에는 없음) -->
@Transient
private 자료형 임시필드 = "테이블에 반영되지 않음";
}
public class ColumnMappingTests {
private static EntityManagerFactory entityManagerFactory;
private static EntityManager entityManager;
@BeforeAll
public static void initFactory() {
entityManagerFactory = Persistence.createEntityManagerFactory("퍼시스턴스유닛이름");
}
@BeforeEach
public void initManager() {
entityManager = entityManagerFactory.createEntityManager();
}
@AfterAll
public static void closeFactory() {
entityManagerFactory.close();
}
@AfterEach
public void closeManager() {
entityManager.close();
}
@DisplayName("컬럼 속성 테스트")
@Test
public void columnMappingTest() {
<!-- given : 테스트할 엔티티 인스턴스 생성 -->
엔티티클래스명 엔티티변수명 = new 엔티티클래스명();
엔티티변수명.set기본키필드(특정값);
엔티티변수명.set필드1("특정값1");
엔티티변수명.set필드2("특정값2");
엔티티변수명.set필드3("특정값3");
member.setEnrollDate(LocalDate.now());
<!-- when : 트랜잭션 시작 및 저장 -->
EntityTransaction transaction변수명 = entityManager.getTransaction();
transaction변수명.begin();
try {
entityManager.persist(엔티티변수명);
transaction변수명.commit();
} catch (Exception e) {
transaction변수명.rollback();
throw new RuntimeException(e);
}
<!-- then : 저장된 엔티티를 조회하여 매핑 확인 -->
엔티티클래스명 조회된엔티티 = entityManager.find(엔티티클래스명.class, 엔티티변수명.get기본키필드());
System.out.println("조회결과 = " + 조회된엔티티);
assertEquals(엔티티변수명.get기본키필드(), 조회된엔티티.get기본키필드());
}
}
Primary key에는 @Id 어노테이션과 @GeneratedValue 어노테이션을 사용
<!-- 1. IDENTITY 전략 (MySQL) -->
@Id
@Column(name = "기본키필드명")
@GeneratedValue(strategy = GenerationType.IDENTITY)
<!-- 2. SEQUENCE 전략 (Oracle 등 - 시퀀스 객체 사용) -->
@Id
@Column(name = "기본키필드명")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "시퀀스생성기이름")
<!-- 3. TABLE 전략 (테이블 기반 키 생성) -->
<!-- 잘 사용하지 않음 -->
@Id
@Column(name = "기본키필드명")
@GeneratedValue(strategy = GenerationType.TABLE, generator = "테이블생성기이름")
<!-- 4. AUTO 전략 (자동 선택) -->
@Id
@Column(name = "기본키필드명")
@GeneratedValue(strategy = GenerationType.AUTO)
@TableGenerator(
name = "생성기이름", // @GeneratedValue에서 참조할 이름
table = "키관리테이블명", // 실제 DB에 존재하는 키 관리용 테이블 이름
pkColumnName = "식별자키컬럼명", // 시퀀스를 구분짓는 키 이름을 저장할 컬럼
valueColumnName = "현재값저장컬럼명", // 다음 값을 저장하는 컬럼
pkColumnValue = "이엔티티를구분할값" // 이 생성기가 사용할 고유한 키 값
)
@SequenceGenerator(
name = "시퀀스생성기이름", // @GeneratedValue에서 참조할 이름
sequenceName = "DB시퀀스객체이름", // DB에 있는 시퀀스 이름
initialValue = 시작값 // 시퀀스 시작 번호 (예: 100)
)
@SequenceGenerator(
name = "시퀀스생성기이름", // @GeneratedValue에서 참조할 이름
sequenceName = "DB시퀀스객체이름", // DB에 있는 시퀀스 이름
initialValue = 시작값, // 초기값 (예: 1)
allocationSize = 증가단위 // 값이 증가하는 간격 (예: 1, 10, 50)
)
@DisplayName("식별자 매핑 테스트")
@Test
public void identifierMappingTest() {
<!-- given : 엔티티 인스턴스 여러 개 생성 -->
엔티티클래스명 엔티티1 = new 엔티티클래스명();
엔티티1.set필드1("특정값1");
엔티티1.set필드2("특정값2");
엔티티클래스명 엔티티2 = new 엔티티클래스명();
엔티티2.set필드1("특정값3");
엔티티2.set필드2("특정값4");
<!-- when : 트랜잭션 시작 후 영속화 -->
EntityTransaction transaction변수명 = entityManager.getTransaction();
transaction변수명.begin();
try {
entityManager.persist(엔티티1);
entityManager.persist(엔티티2);
transaction변수명.commit();
} catch (Exception e) {
transaction변수명.rollback();
throw new RuntimeException();
}
<!-- then : JPQL, 쿼리문과 결과의 자료형을 전달 -->
<!-- 테이블명이 아닌 엔티티를 기준, 별칭은 필수 -->
String jpql = "select e.식별자필드명 from 엔티티이름 e";
<!-- 식별자료형은 조회 시 데이터 자료형 -->
List<식별자자료형> 식별자목록변수명 = entityManager.createQuery(jpql, 식별자자료형.class).getResultList();
for (식별자자료형 식별자변수명 : 식별자목록변수명) {
System.out.println(식별자변수명);
}
}
@Enumerated 어노테이션은 Enum 타입 매핑을 위해 사용
public enum 열거형이름 {
상수1, 상수2
}
@Entity(name = "엔티티_논리이름") <!-- 선택: 다른 클래스와 이름 겹칠 때 사용 -->
@Table(name = "테이블_이름")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter <!-- 실제 서비스에서는 Setter 사용을 제한하는 것이 일반적 (보안/불변성 등) -->
@ToString
public class 클래스명 {
@Id
@Column(name = "기본키_컬럼명")
@GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 증가 시 사용
private 자료형 기본키필드;
<!-- enum으로 정의 된 값 -->
@Column(name = "컬럼명")
@Enumerated(EnumType.STRING) // Enum 값을 문자열로 저장
private 열거형이름 필드명;
public class EnumTypeMappingTests {
private static EntityManagerFactory entityManagerFactory;
private static EntityManager entityManager;
@BeforeAll
public static void initFactory() {
<!-- persistence.xml의 persistence-unit 이름 사용 -->
entityManagerFactory = Persistence.createEntityManagerFactory("퍼시스턴스유닛이름");
}
@BeforeEach
public void initManager() {
entityManager = entityManagerFactory.createEntityManager();
}
@AfterAll
public static void closeFactory() {
entityManagerFactory.close();
}
@AfterEach
public void closeManager() {
entityManager.close();
}
<!-- 여러 테스트 -->
}
@Test
public void enumTypeFieldMappingTest() {
<!-- given : 테스트할 엔티티 인스턴스 생성 및 Enum 값 지정 -->
엔티티클래스명 엔티티변수 = new 엔티티클래스명();
엔티티변수.set필드1("특정값1");
엔티티변수.set필드2("특정값2");
엔티티변수.set열거형필드(열거형타입.상수명); // Enum 값 설정
<!-- when : 트랜잭션 시작 및 영속화 -->
EntityTransaction transaction변수 = entityManager.getTransaction();
transaction변수.begin();
try {
entityManager.persist(엔티티변수);
transaction변수.commit();
} catch (Exception e) {
transaction변수.rollback();
throw new RuntimeException(e);
}
<!-- then : DB에서 다시 조회 후 검증 -->
엔티티클래스명 조회결과변수 = entityManager.find(엔티티클래스명.class, 엔티티변수.get기본키());
System.out.println("조회결과 = " + 조회결과변수);
assertEquals(엔티티변수.get기본키(), 조회결과.get필드1());
}
}
@Embeddable 클래스에 복합키를 정의하고 엔티티에 @EmbeddedId 를 이용하여 매핑
@Embeddable // 복합키로 사용할 값 타입 지정, 복합키로 가능하게 한다.
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class 복합키클래스명 {
@Column(name = "컬럼명1")
private 자료형 필드1;
@Column(name = "컬럼명2")
private 자료형 필드2;
}
@Entity(name = "엔티티_논리이름") // 선택: 다른 클래스와 이름 겹칠 때 명시
@Table(name = "테이블명")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class 엔티티클래스명 {
<!-- 복합키 객체를 엔티티의 기본키로 사용 -->
@EmbeddedId
private 복합키클래스명 복합키변수명;
@Column(name = "컬럼명3")
private 자료형 필드3;
@Column(name = "컬럼명4")
private 자료형 필드4;
}
public class EmbeddedKeyTests {
private static EntityManagerFactory entityManagerFactory;
private static EntityManager entityManager;
@BeforeAll
public static void initFactory() {
<!-- persistence.xml의 persistence-unit 이름 사용 -->
entityManagerFactory = Persistence.createEntityManagerFactory("퍼시스턴스유닛이름");
}
@BeforeEach
public void initManager() {
entityManager = entityManagerFactory.createEntityManager();
}
@AfterAll
public static void closeFactory() {
entityManagerFactory.close();
}
@AfterEach
public void closeManager() {
entityManager.close();
}
<!-- 여러 테스트 -->
}
@Test
public void embeddedIdTest() {
<!-- given : 복합키 및 엔티티 인스턴스 생성 -->
엔티티클래스명 엔티티변수명 = new 엔티티클래스명();
엔티티변수명.set복합키필드(new 복합키클래스명(특정값1, 특정값2));
엔티티변수명.set일반필드1("특정값1");
엔티티변수명.set일반필드2("특정값2");
<!-- when : 트랜잭션 시작 및 영속화 -->
EntityTransaction transaction변수명 = entityManager.getTransaction();
transaction변수명.begin();
try {
entityManager.persist(엔티티변수명);
transaction변수명.commit();
} catch (Exception e) {
transaction변수명.rollback();
throw new RuntimeException();
}
<!-- then : 복합키 기준으로 다시 조회하여 동등성 검증 -->
엔티티클래스명 조회결과변수명 = entityManager.find(엔티티클래스명.class, 엔티티.get복합키필드());
assertEquals(엔티티변수명.get복합키필드(), 조회결과변수명.get복합키필드());
}
복합키를 필드로 정의한 클래스를 이용해서 클래스에 @IdClass를 매핑
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class 복합키클래스명 {
private 자료형 복합키필드1;
private 자료형 복합키필드2;
}
@Entity(name = "엔티티_논리이름")
@Table(name = "테이블명")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
<!-- 복합키 클래스 지정 -->
@IdClass(복합키클래스명.class)
public class 엔티티클래스명 {
@Id
@Column(name = "복합키컬럼1")
private 자료형 복합키필드1;
@Id
@Column(name = "복합키컬럼2")
private 자료형 복합키필드2;
@Column(name = "일반컬럼1")
private 자료형 일반필드1;
@Column(name = "일반컬럼2")
private 자료형 일반필드2;
}
public class IdClassTests {
private static EntityManagerFactory entityManagerFactory;
private static EntityManager entityManager;
@BeforeAll
public static void initFactory() {
<!-- persistence.xml의 persistence-unit 이름 사용 -->
entityManagerFactory = Persistence.createEntityManagerFactory("퍼시스턴스유닛이름");
}
@BeforeEach
public void initManager() {
entityManager = entityManagerFactory.createEntityManager();
}
@AfterAll
public static void closeFactory() {
entityManagerFactory.close();
}
@AfterEach
public void closeManager() {
entityManager.close();
}
<!-- 여러 테스트 -->
}
@DisplayName("IdClass를 이용한 복합키 테이블 매핑 테스트")
@Test
public void idClassCompositeKeyTest() {
<!-- given : 복합키 및 일반 필드 설정 -->
엔티티클래스명 엔티티변수명 = new 엔티티클래스명();
엔티티변수명.set복합키필드1(특정값1);
엔티티변수명.set복합키필드2("특정값2");
엔티티변수명.set일반필드1("특정값3");
엔티티변수명.set일반필드2("특정값4");
<!-- when : 트랜잭션 시작 및 영속화 -->
EntityTransaction transaction변수명 = entityManager.getTransaction();
transaction변수명.begin();
try {
entityManager.persist(엔티티변수명);
transaction변수명.commit();
} catch (Exception e) {
transaction변수명.rollback();
throw new RuntimeException(e);
}
<!-- then : 복합키 인라인 생성 후 조회 -->
엔티티클래스명 조회결과변수 = entityManager.find(엔티티클래스명.class, new 복합키클래스명(값1, "값2"));
assertEquals(엔티티변수명, 조회결과변수);
}
== : 주소값 비교
equals() : 값만 같은지 비교