Database ( DB )/JPA, Querydsl

Entity Inheritence in JPA

노루아부지 2021. 8. 23. 18:00

데이터베이스 테이블을 설계하다 보면 column이 중복되는 경우가 생각보다 많습니다.

대표적으로 등록한 사람, 등록일시, 수정한 사람, 수정일시 등이 있는데요

이것을 일일히 쓰기에는 여간 귀찮은 일이 아닙니다.

이럴 때 @MappedSuperclass를 사용하면 됩니다.

 

그럼 지금부터 예제 코드를 보겠습니다.

 

먼저, BaseTme 클래스를 생성합니다.

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTime {
  @CreatedDate
  private LocalDateTime createdDate;

  @LastModifiedDate
  private LocalDateTime modifiedDate;
}

 

  • @MappedSuperclass
    • JPA Entity 클래스들이 BaseTime 클래스를 상속할 경우 필드들도 칼럼으로 인식하도록 합니다.
  • @EntityListeners(AuditingEntityListener.class)
    • BaseTime 클래스에 Auditing 기능을 포함합니다.
    • 여기서 Audit는 감시하다, 감사하다라는 뜻으로 Sping Data JPA에서 CreatedUser, CreatedDate, LastModifiedUser, LastModifiedDate에 대해서 자동으로 값을 넣어주는 기능입니다.(user 정보는 별도 설정을 해야 합니다.)
  • @CreatedDate
    • Enity가 생성되어 저장될 때 시간이 자동 저장됩니다.
  • @LastModifiedDate
    • 조회한 Entity의 값을 변경할 때 시간이 자동 저장됩니다.

 

 

그다음 TbUser 테이블에 BaseTime 클래스를 상속합니다.

import lombok.Getter;
import lombok.Setter;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Getter
@Setter
@Entity
public class TbUser extends BaseTime {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long userSeq;
  private String userId;
  private Integer age;
}

 

  • @GenerationType.AUTO
    • PK 생성 전략을 자동으로 설정합니다.
    • PK 생성 전략은 IDENTITY, SEQUENCE, TABLE, AUTO 총 네 가지가 있는데, AUTO는 상황에 따라(데이터베이스에 따라) IDENTITY, SEQUENCE, TABLE 중 하나를 선택하게 됩니다.
    • spring.jpa.hibernate.use-new-id-generator-mappings=false인 경우 사용하는 DB(Dialect)에 의해 Generator가 결정됩니다.
      • Dialect에서 supportsIdentityColumns()가 true 인 경우 IdentityGenerator를 사용하게 됩니다.
      • false 인 경우 SequenceStyleGenerator를 사용하게 됩니다.
      • 즉, IDENTITY를 지원하지 않는 Oracle의 경우 SEQUENCE 전략을 사용하게 됩니다.
    • spring.jpa.hibernate.use-new-id-generator-mappings=true인 경우 Sequence 기능을 지원하는 경우 SequenceGenerator를 사용하고, Sequence 기능을 사용하지 않는 경우 TableGenerator를 사용합니다.

 

JPA 테스트를 위해 TbUserRepository 인터페이스를 생성합니다.

import org.springframework.data.jpa.repository.JpaRepository;

public interface TbUserRepository extends JpaRepository<TbUser, Long> {
}

 

 

마지막으로 JPA Auditing을 사용할 수 있도록 Application 클래스에 @EnableJpaAuditing 어노테이션을 추가합니다.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
public class DemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

 

 

테스트 코드로 확인해보기

import com.example.demo.domain.TbUser;
import com.example.demo.domain.TbUserRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Optional;

@SpringBootTest
class DemoApplicationTests {
  @Autowired
  TbUserRepository repository;

  @Test
  void contextLoads() {
    TbUser user = new TbUser();
    user.setUserId("noroo");
    user.setAge(20);

    repository.save(user);

    Optional<TbUser> noroo = repository.findById(user.getUserSeq());
    Assertions.assertEquals(user.getAge(), noroo.get().getAge());
  }
}

 

위 테스트 코드는 등록한 사용자의 나이와 실제 데이터베이스에 insert 된 나이를 비교하는 코드입니다.

 

테스트를 실행하면 아래와 같이 성공 표시가 되고 로그가 올라온 것을 볼 수 있습니다.

Hibernate: select nextval(hibernate_sequence)
Hibernate: insert into tb_user (created_date, modified_date, age, user_id, user_seq) values (?, ?, ?, ?, ?)
Hibernate: select tbuser0_.user_seq as user_seq1_0_0_, tbuser0_.created_date as created_2_0_0_, tbuser0_.modified_date as modified3_0_0_, tbuser0_.age as age4_0_0_, tbuser0_.user_id as user_id5_0_0_ from tb_user tbuser0_ where tbuser0_.user_seq=?

 

그다음 TB_USER 테이블을 확인하면 아래와 같이 현재 시간이 잘 들어갔다는 것을 확인할 수 있습니다.

 

  • @LastModifiedDate 어노테이션이 적용된 칼럼에서 별도의 처리를 하지 않으면 insert 될 때 @LastModifiedDate가 적용된 칼럼도 현재 시간이 insert 됩니다.
  • 하지만 update 될 때는 @CreatedDate는 업데이트되지 않으니 걱정하지 않으셔도 됩니다.
728x90
loading