JPA Annotations

GEONNY·2024년 8월 20일
post-thumbnail

JPA Annotation 을 정리해보는 시간을 갖겠습니다. 정확한 용도에 맞게 사용할 수 있도록 개념과 사용법을 알아보도록 하죠. 이 외에 유용한 Hibernate Annotions 에 대해서도 확인해보세요!

📌JPA Annotations

📍Entity 관련

🎈@Table

엔티티가 매핑될 Database 테이블의 정보를 지정합니다. 아래 나올 @Entity 와 함께 써야 의미가 있습니다.

@Table(name = "member", schema = "test_db")
public class Member {
//이하 생략..

MariaDB 에서는 schema 대신 catalog 를 사용하여 스키마를 지정해 줍니다.

@Table(name = "member", catalog = "test_db")
public class Member {
//이하 생략..

🎈@Entity

클래스가 JPA Entity 임을 선언합니다. Entity class 는 Database Table 에 매핑되며, 이를 통해 JPA는 이 클래스 인스턴스를 Database의 행으로 관리합니다. 자세히 설명하면 @Entity 가 있어야 영속성 컨텍스트에 포함이되며, JPA를 통한 상태관리, 캐싱, 변경 감지 등의 기능이 동작합니다. @Table@Entity 가 없는 경우 무시되며 아무런 효과도 없습니다.

@Table(name = "member", schema = "test_db")
@Entity
public class Member {
//이하 생략..

📍필드 관련

🎈@Column

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;
//이하 생략..

🎈@Enumerated

Java의 Enum type (열거형)을 Database column에 매핑할 때 사용합니다. 정수형, 문자열로 변환될 수 있으며, 두 옵션중 하나를 선택합니다. EnumType.STRING, EnumType.ORIGINAL

	@Column(name = "use_yn")
    @Enumerated(EnumType.STRING)
    private UseYn useYn;

🎈@Temporal

Database의 날짜, 시간 타입의 column 과 매핑할 때 사용합니다.
Java 8 이전 Date, Calendar type을 사용할 때는 적절한 속성을 사용해야 했지만, LocalDate, LocalDateTime 사용 시에는 @Temporal 자체가 필요하지 않습니다. 자동으로 타입에 맞게 매핑해주지만 명시적으로 사용해주어도 됩니다.

    @Column(name = "create_dt", updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime createDate;

🎈@Lob

큰 객체(LOB : Large Object) 를 필드에 매핑할 떄 사용됩니다. 보통 String 이나 byte[] 타입 필드 (Database 의 Blob type의 Column 과 매핑할 때)에 적용합니다.

	@Column(name = "IMG")
    @Lob
    private byte[] image;

🎈@Transient

Databse Column 과 매핑하지 않고, JPA 의 관리 대상에서 제외(영속성 대상에서 제외)하는 필드에 사용합니다. 단순히 비지니스 로직에서 활용할 목적의 필드에 설정하여 사용합니다.

	@Transient
    private String noManagement;

📍연관관계 매핑 관련

🎈@JoinColumn

Entity 간의 관계를 매핑할 때 사용합니다. FK가 참조하는 대상 컬럼이 PK 이면 referencedColumnName 속성을 설정하지 않아도 됩니다. 하지만 PK 이외의 다른 컬럼을 참조해야하면 반드시 referencedColumnName 을 설정해 주어야 합니다. PK가 아닌데 설정하지 않는 경우 자동으로 PK와 매핑을 시도하여 이상한 데이터가 조회될 수 있으니 주의해야 합니다.

    @JoinColumn(name = "authority_cd")
    private Authority authority;

🎈@OneToOne, @OneToMany, @ManyToOne, @ManyToMany

엔티티 간의 관계를 매핑할때 @JoinColumn 과 함께 사용합니다. 각각 어떤 관계인지를 표현합니다. 속성에 대한 내용은 설명을 참고하세요.

	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "authority_cd")
    private Authority authority;

🎈@OrderBy

Collection 으로 매핑된 Entity를 특정 컬럼 기준으로 정렬해서 가져와야 할 때 사용합니다.

	@OneToMany(mappedBy = "authority", fetch = FetchType.LAZY)
    @OrderBy("memberName asc")
    private Set<Member> members = new HashSet<>();

📍키 설정 관련

🎈@Id

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;
	//이하 생략..

🎈@EmbeddedId, @Embeddable

Entity의 식별자 필드를 복합키로 정의할 때 사용됩니다.
복합키의 조건

  • public class 로 정의
  • 기본 생성자 필수
  • @Embeddable 선언
  • Serializable interface 구현
  • equals(), hashCode Override

복합키는 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;
	//이하 생략..

🎈@MapsId

Entity 에서 FK 필드를 PK 필드로 사용할 때 사용합니다. 복합키를 사용하고, 복합 키 필드에 대한 연관관계 설정 시 활용합니다.
아래의 예시코드를 보면, 부모인 CodeGroup 의 키인 CodeGroupIdCode 에서 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

클래스 내의 필드로 복합키를 설정할 때 사용합니다. 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;
    
	//이하 생략..

🎈@GeneratedValue

기본 키를 자동으로 생성할 때 사용합니다. 여러 전략이 있으며 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());
    }

📍기타

🎈@Version

Entity 의 버전을 관리하기 위해 사용되며, 동시에 여러 트랜잭션이 동일한 Entity 를 수정하는 경우 데이터의 일관성을 보장하는데 도움을 줍니다.

	@Version
	private int version;

🎈@NamedQuery, @NamedQueries

@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);
}
profile
Back-end developer

0개의 댓글