Tech

JPA Entity 기초

알 수 없는 사용자 2022. 7. 8. 18:08
 
 
JPA(Java Persistence API)는 Java ORM 표준 기술입니다.
JPA를 처음 접할 때, 제일 먼저 할일은 Entity와 DB Table간의 연관성을 파악하는 것입니다.
이 글에서는 Entity를 처음 만들 때 필요한 개념을 설명하면서 DB Table과의 관계가 어떻게 되는지 살펴보는 시간을 가져보겠습니다.

 

Entity는?

@Entity는 식별성이 필요한(Identity) 객체를 만들 때 사용합니다. 

이와 반대 개념으로 식별성이 필요 없는 객체를 만들 때에는 Value Object(VO)로 만듭니다.

 

Entity를 만든다는 것은 RDB의 테이블과 테이블 칼럼으로 매핑하는 것입니다.

Entity의 Class명이 RDB의 테이블 이름이 되고 Entity의 field값이 테이블의 칼럼과 매핑되게 됩니다.

각 각의 컨벤션에 의거해 카멜케이스(WebTagPublishing)를 스네이크케이스(web_tag_publishing)로 변환하는 것을 변환의 기본 전략으로 삼고 있는데, 개발자는 NameStrategy를 변경해 커스터마이징 할 수 있습니다.

 

 

 

1. Entity의 식별성은 어떻게 구현할까?

@Entity
@EqualsAndHashCode(of = {"id"}, callSuper = false)
public class WebTagPublishing implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}

 

자바에서 A와 B의 객체가 다름을 구분하는 것은 메모리 주소가 같은지(==), 값(equals)이 같은지를 비교하는 것입니다.

 

String, Wrapper(Integer, Long..), Date, File 객체는 자바에서 기본적으로 재정의 하기 때문에 equals의 경우 메모리 주소가 같은지가 아니라 값이 같다면 같다고 판별한다.

Entity는 equals로 비교 하는데 @ID의 필드 값이 같다면 이 Entity는 같은 Entity라고 보는 것이죠. 그래서 기본적으로 이에 필요한 Equals와 HashCode 작성은 Entity 작성의 필수가 됩니다. Lombok을 이용하면 @EqualsAndHashCode를 사용해 쉽게 작성할 수 있습니다.

@EqualsAndHashCode(of = {"id"}, callSuper = false)

물론, 자동 증가 값으로 생성된 Id 이외로 식별성을 구분할 필요도 있는 경우(UniKey, Business Key) 이를 사용하면 됩니다.

 

 

 

2. Serializable 사용하기

public class WebTagPublishing implements Serializable {
    private static final long serialVersionUID = 1L;

JPA는 분산 자바 환경에서 동작하도록 스펙으로 정의 되어 있습니다. 그래서 객체를 다른 JVM과 주고받을 수 있도록 Serializable를 선언해야 합니다. 그래서 객체가 바이트 스트림으로 네트워크를 통해 다른 JVM과 통신할 수 있게 해야 합니다.

 

 

3. ID 만들기

public class WebTagPublishing implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
 

@Id를 선언하면 JPA는 이 필드를 ID로 인식하게 됩니다.

추가로 @GeneratedValue(strategy = GenerationType.IDENTITY)를 달면 JPA는 이 필드값이 자동 증가 값으로 인식하게 됩니다.(MySQL 기준)

이 것은 JPA에서 인식한다는 것이지, 개발자는 RDB에서 해당 필드가 자동 증가 값이라는 것을 선언해야 하는 것이 달라지지 않습니다.

 

id INT(11) NOT NULL AUTO_INCREMENT COMMENT 'ID'

 

 

 

4. 칼럼 정의

필드에 @Column을 달면 칼럼의 세부 속성을 정의할 수 있습니다. 주로 Entity 필드명과 Table의 칼럼명이 다른 경우 혹은 세부 필드에 속성을 지정할 때 사용합니다. 만약 JPA를 이용해 RDB의 테이블을 자동 생성하는 기능을 이용할 경우 이곳에 기술한 속성 정보를 참고해 생성하게 됩니다.

 

아래 예제는 DB에 insert_user_id으로 작성된 필드를 Entity의 createBy칼럼과 매핑하겠다는 뜻이 됩니다. updatable = false 속성은 JPA에 이 값은 수정하지 않는다고 알려 주는 것이다. (create Id므로)

    @Column(name = "insert_user_id", updatable = false)
    protected Long createdBy;
 

일반적으로 코드의 깔끔함을 위해 @Column 속성을 생략하고 사용합니다.

String email;
Long accountId;

 

 

 

5. Auditing

우리는 table에 보통 감사정보(Auditing)를 넣습니다. (ex. 생성자,생성일,수정자,수정일)

Entity에 모두 이 값을 넣는다면 중복 코드를 생성하므로, 보통은 이 정보를 상위 클래스로 만든 뒤 상속해서 사용하게 됩니다.

 

 

 

 

 

Auditing class 작성법

1. @MappedSuperclass를 사용해 상위 클래스 임을 선언합니다.

2. @EntityListeners(AuditingEntityListener.class)를 선언하면, 생성자, 수정자를 JPA에서 자동으로 입력해줍니다.

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class Auditing {

    @CreatedBy
    @Column(updatable = false)
    protected Long createdBy;

    @CreatedDate
    @Column(updatable = false)
    protected LocalDateTime createdDate;
    
    @LastModifiedBy
    protected Long lastModifiedBy;

    @LastModifiedDate
    protected LocalDateTime lastModifiedDate;
}
 

 

사용법

상속(extends)으로 Auditing 칼럼들을 상속받습니다.

@Entity
@EqualsAndHashCode(of = {"id"}, callSuper = false)
public class WebTagPublishing extends Auditing implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
}
 
 
 
 
설정하는 법

@EntityListeners(AuditingEntityListener.class)를 사용하기 위해서는 아래처럼 2개를 작성합니다.

1. @EnableJpaAuditing를 선언

2. AuditorAwareBean 구현

 

  •  @EnableJpaAuditing
@Configuration
@EnableJpaAuditing
public class JpaConfig {

    @Bean
    AuditorAware<Long> auditorAware() {
        return new AuditorAwareBean();
    }
}
 
  •  AuditorAwareBean 구현

로그인한 경우와 아닌 경우를 구분합니다.

  • 로그인한 경우
    • 리턴할 사용자 정보는 주로 USER_ID가 될 것입니다.
    • 예시 : Long.valueOf(authentication.getName())
  • 로그인 하지 않은 경우는 아래와 같이 기본 값을 넣어줍니다.
    • 예시 : 0L
public class AuditorAwareBean implements AuditorAware<Long> {
    @Override
    public Optional<Long> getCurrentAuditor() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (!isAnonymous(authentication)) {
            return Optional.of(Long.valueOf(authentication.getName()));
        } else {
            return Optional.of(0L);
        }
    }

    private boolean isAnonymous(Authentication authentication) {
        if (authentication == null) return true;
        return authentication.getName().equals("anonymousUser");
    }
}
 
 
 
 
 
 
 
이상 기초적인 JPA Entity 작성법이었습니다.
 
다음에는 보다 복잡한 Entity 작성법으로 찾아 뵙겠습니다.