JPA Annotation 을 정리해보는 시간을 갖겠습니다. 정확한 용도에 맞게 사용할 수 있도록 개념과 사용법을 알아보도록 하죠. 이 외에 유용한 Hibernate Annotions 에 대해서도 확인해보세요!
엔티티가 매핑될 Database 테이블의 정보를 지정합니다. 아래 나올 @Entity 와 함께 써야 의미가 있습니다.
@Table(name = "member", schema = "test_db")
public class Member {
//이하 생략..
MariaDB 에서는 schema 대신 catalog 를 사용하여 스키마를 지정해 줍니다.
@Table(name = "member", catalog = "test_db")
public class Member {
//이하 생략..
클래스가 JPA Entity 임을 선언합니다. Entity class 는 Database Table 에 매핑되며, 이를 통해 JPA는 이 클래스 인스턴스를 Database의 행으로 관리합니다. 자세히 설명하면 @Entity 가 있어야 영속성 컨텍스트에 포함이되며, JPA를 통한 상태관리, 캐싱, 변경 감지 등의 기능이 동작합니다. @Table은 @Entity 가 없는 경우 무시되며 아무런 효과도 없습니다.
@Table(name = "member", schema = "test_db")
@Entity
public class Member {
//이하 생략..
Entity 필드를 Database column 에 매핑합니다.
주요 속성
name: column 명 지정
nullable: column 의 null 혀용 여부 지정
length: 문자열 타입의 column 길이 지정
unique: 컬럼 값의 고유 여부 지정
updatable: 수정 가능 여부 지정
@Table(name = "member", schema = "test_db")
@Entity
public class Member {
@Column(name = "member_id", nullable = false, length = 30)
private String memberId;
//이하 생략..
Java의 Enum type (열거형)을 Database column에 매핑할 때 사용합니다. 정수형, 문자열로 변환될 수 있으며, 두 옵션중 하나를 선택합니다. EnumType.STRING, EnumType.ORIGINAL
@Column(name = "use_yn")
@Enumerated(EnumType.STRING)
private UseYn useYn;
Database의 날짜, 시간 타입의 column 과 매핑할 때 사용합니다.
Java 8 이전 Date, Calendar type을 사용할 때는 적절한 속성을 사용해야 했지만, LocalDate, LocalDateTime 사용 시에는 @Temporal 자체가 필요하지 않습니다. 자동으로 타입에 맞게 매핑해주지만 명시적으로 사용해주어도 됩니다.
@Column(name = "create_dt", updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime createDate;
큰 객체(LOB : Large Object) 를 필드에 매핑할 떄 사용됩니다. 보통 String 이나 byte[] 타입 필드 (Database 의 Blob type의 Column 과 매핑할 때)에 적용합니다.
@Column(name = "IMG")
@Lob
private byte[] image;
Databse Column 과 매핑하지 않고, JPA 의 관리 대상에서 제외(영속성 대상에서 제외)하는 필드에 사용합니다. 단순히 비지니스 로직에서 활용할 목적의 필드에 설정하여 사용합니다.
@Transient
private String noManagement;
Entity 간의 관계를 매핑할 때 사용합니다. FK가 참조하는 대상 컬럼이 PK 이면 referencedColumnName 속성을 설정하지 않아도 됩니다. 하지만 PK 이외의 다른 컬럼을 참조해야하면 반드시 referencedColumnName 을 설정해 주어야 합니다. PK가 아닌데 설정하지 않는 경우 자동으로 PK와 매핑을 시도하여 이상한 데이터가 조회될 수 있으니 주의해야 합니다.
@JoinColumn(name = "authority_cd")
private Authority authority;
엔티티 간의 관계를 매핑할때 @JoinColumn 과 함께 사용합니다. 각각 어떤 관계인지를 표현합니다. 속성에 대한 내용은 설명을 참고하세요.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "authority_cd")
private Authority authority;
Collection 으로 매핑된 Entity를 특정 컬럼 기준으로 정렬해서 가져와야 할 때 사용합니다.
@OneToMany(mappedBy = "authority", fetch = FetchType.LAZY)
@OrderBy("memberName asc")
private Set<Member> members = new HashSet<>();
Entity의 기본 키(Primary Key) 필드를 지정합니다. 이 필드는 Database 에서 고유한 값으로, Entity 인스턴스를 식별하는데 사용되며 필수 입니다.
@Table(name = "member", schema = "test_db")
@Entity
public class Member {
@Id
@Column(name = "member_id", nullable = false, length = 30)
private String memberId;
//이하 생략..
Entity의 식별자 필드를 복합키로 정의할 때 사용됩니다.
복합키의 조건
복합키는 class 로 정의되어야 하며, @Embeddable이 선언되어야하고 Serializable interface를 구현해야 합니다.
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@Embeddable
public class CodeId implements Serializable {
@Column(name = "code_group_id")
private String codeGroupId;
@Column(name = "code_id")
private String codeId;
}
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table
@Entity(name = "code")
public class Code extends BaseEntity {
@EmbeddedId
private CodeId codeId;
//이하 생략..
Entity 에서 FK 필드를 PK 필드로 사용할 때 사용합니다. 복합키를 사용하고, 복합 키 필드에 대한 연관관계 설정 시 활용합니다.
아래의 예시코드를 보면, 부모인 CodeGroup 의 키인 CodeGroupId 를 Code 에서 PK 로 사용하고 있습니다.
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@Embeddable
public class CodeId implements Serializable {
@Column(name = "code_group_id")
private String codeGroupId;
@Column(name = "code_id")
private String codeId;
}
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table
@Entity(name = "code_group")
public class CodeGroup extends BaseEntity {
@Id
@Column(name = "code_group_id")
private String codeGroupId;
@Column(name = "code_group_nm")
private String codeGroupName;
@Column(name = "code_group_desc")
private String codeGroupDescription;
}
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table
@Entity(name = "code")
public class Code extends BaseEntity {
@EmbeddedId
private CodeId codeId;
@MapsId("codeGroupId") //CodeId의 codeGroupId 설정
@ManyToOne
@JoinsColumn("code_group_id")
private CodeGroup codeGroup;
//이하 생략..
클래스 내의 필드로 복합키를 설정할 때 사용합니다. IdClass 로 사용될 객체의 필드명과 @Id 로 설정할 필드명은 같아야 합니다.
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class CodeId implements Serializable {
private String codeGroupId;
private String codeId;
}
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table
@Entity(name = "code")
@IdClass(CodeId.class)
public class Code extends BaseEntity {
@Id
@Column(name = "codeGroupId")
private String codeGroupId;
@Id
@Column(name = "codeId")
private String codeId;
//이하 생략..
기본 키를 자동으로 생성할 때 사용합니다. 여러 전략이 있으며 IDENTITY, SEQUENCE, TABLE, AUTO 가 자주 사용됩니다.
주요 속성
IDENTITY: Database auto_increase 값 사용 시
SEQUENCE: Database 시퀀스 사용 시
TABLE: 별도의 키 생성 테이블 사용 시
AUTO: JPA 구현체가 적절한 전략을 자동으로 선택 시
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
JPA 에서 Entity 의 라이프사이클 이벤트를 처리하기 위해 사용되는 콜백 어노테이션들 입니다. Entity 상태에 따라 선언한 method가 특정 시점에 자동으로 호출되니, 각 시점에 필요한 기능을 추가할 수 있습니다.
@PrePersist: Entity 가 영속성 컨텍스트에 저장되기 전에 호출됩니다.
@PostPersist: Entity 가 영속성 컨텍스트에 저정된 후 호출합니다.
@PreUpdate: Entity 가 수정되기 전 호출됩니다.
@PostUpdate: Entity 가 수정 된 후 호출됩니다.
@PreRemove: Enttiy 가 삭제되기 전에 호출됩니다.
@PostRemove: Enttiy 가 삭제된 후 호출됩니다.
@PostLoad: Entity 가 Database 에서 로드된 후 호출됩니다.
@PrePersist
public void prePersist() {
System.out.println("PrePersist : "+this.getCreateDate());
}
@PostPersist
public void postPersist() {
System.out.println("PrePersist : "+this.getCreateDate());
}
Entity 의 버전을 관리하기 위해 사용되며, 동시에 여러 트랜잭션이 동일한 Entity 를 수정하는 경우 데이터의 일관성을 보장하는데 도움을 줍니다.
@Version
private int version;
@NamedQuery 는 정적 쿼리를 정의할 때 사용되며, 미리 JPQL을 정의하고 이름으로 참조하여 호출할 수 있습니다. @NamedQueries 는 @NamedQuery 를 구룹으로 지정할 때 사용됩니다. Spring-data-jpa 를 사용하면 Repository 에서 @Query 를 사용해 처리할 수 있습니다.
entity.Member
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table
@Entity(name = "member")
@NamedQueries({
@NamedQuery(name = "member.findAll", query = "SELECT m FROM member m"),
@NamedQuery(name = "member.findByMemberId"
, query = "SELECT m FROM member m where m.memberId = :memberId")
})
public class Member extends BaseEntity implements Persistable<String> {
//이하 생략...
domain.member.MemberServiceImpl
@Override
@Transactional
public MemberSearchResponse getMemberById(String memberId) {
// Member memberEntity = memberRepository.findById(memberId)
// .orElseThrow(() -> new EntityNotFoundException("
// 회원 ID 가 존재하지 않습니다. -> " + memberId));
Member memberEntity = entityManager.createNamedQuery("member.findByMemberId", Member.class)
.setParameter("memberId", memberId)
.getSingleResult();
return memberMapper.toRecord(memberEntity);
}
@Override
@Transactional
public List<MemberSearchResponse> getMembers() {
// return memberMapper.toRecordList(memberRepository.findAll());
List<Member> memberList = entityManager.createNamedQuery("member.findAll", Member.class)
.getResultList()
return memberMapper.toRecordList(memberList);
}
domain.member.MemberRepository, spring-data-jpa 사용 시
JPQL이 아닌 nativeQuery 사용 시에는 nativeQuery = true 속성을 설정합니다.
@Repository
public interface MemberRepository extends JpaRepository<Member, String> {
@Query(value = "SELECT m FROM member m")
@Nonnull
List<Member> findAll();
@Query(value = "SELECT m FROM member m where m.memberId = :memberId")
Member findByMemberId(String memberId);
}