대부분 프로젝트의 Database Table 에는 등록일시, 수정일시와 같이 중복되는 컬럼들이 있습니다. JPA 에서는 @MappedSuperclass
를 사용하여 중복되는 컬럼들을 추상화 클래스의 필드로 추가하고 이를 상속받아 사용할 수 있습니다.
이전에 생성했던 Member 와 Authority Entity 에도 createDate 와 updateDate 가 있었습니다. 이 필드들을 BaseEntity를 생성하여 이동하고 Member, Authority entity에서 이를 상속받도록 수정해 주겠습니다.
entity.base.BaseEntity
@MappedSuperclass
@Getter
public abstract class BaseEntity {
@Column(name = "update_dt")
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime updateDate;
@Column(name = "create_dt", updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime createDate;
}
BaseEntity 는 실제 Entity 가 아니고 공통 필드의 묶음이기 때문에 new BaseEntity();
와 같이 인스턴스 생성을 막기 위해 추상 클래스
로 생성합니다.
entity.Member
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table
@Entity(name = "member")
public class Member extends BaseEntity {
@Id
@Column(name = "member_id")
private String memberId;
@Column(name = "member_pw")
private String password;
@Column(name = "member_nm")
private String memberName;
@Column(name = "use_yn")
private String useYn;
@Column(name = "authority_cd")
private String authorityCode;
}
entity.Authority
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table
@Entity(name = "authority")
public class Authority extends BaseEntity {
@Id
@Column(name = "authority_cd")
private String authorityCode;
@Column(name = "authority_nm")
private String authorityName;
}
Auditing 은 감사 : 감독하고 검사함.
라는 의미로 JPA 에서는 데이터의 변경이력 감사하는 의미로 이해하시면 됩니다.
@EnableJpaAuditing 은 Springboot main class 에 추가하여 기능을 활성화 해야 합니다.
@SpringBootApplication
@EnableJpaAuditing
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
Auditing 이 가능한 필드의 종류에는 4가지가 있습니다.
@CreatedBy
Entity 영속 시 생성한 사용자 정보 저장
@CreatedDate
Entity 영속 시 생성일시 저장
@LastModifiedBy
영속 상태의 Entity 수정 시 수정한 사용자 정보 저장
@LastModifiedDate
영속 상태의 Entity 수정 시 수정일시 저장
Auditing 할 필드가 있는 Entity 에 @EntityListeners(AuditingEntityListener.class)
를 추가해 주고 적용할 필드에 위 4 가지 Annotaion 을 기능에 맞게 추가합니다.
BaseEntity 에 적용해 보도록 하죠.
entity.base.BaseEntity
@MappedSuperclass
@Getter
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@Column(name = "update_dt")
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
private LocalDateTime updateDate;
@Column(name = "create_dt", updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private LocalDateTime createDate;
}
다음에는 Member CUD Operation 을 구현하면서 Auditing 이 정상적으로 동작하는지 확인해 보도록 하겠습니다.😄
JPA Entity 의 생명주기에는 4가지가 있습니다.
비영속
: JPA 와 관계 없이 Entity만 생성한 상태를 의미
// 단순히 Entity 만 생성 (비영속)
Member member = new Member("meber1", "1234", "회원1");
영속
: JPA에 의해서 관리되는 상태를 의미
//영속 상태의 객체를 조회 (영속)
Member member = memberRepository.findById(String memberId);
//새로 생성한 객체를 영속 상태로 변경
Member member = new Member("meber1", "1234", "회원1");
memberRepository.save(member); // EntityManager.persist(member); 가 호출되면서 영속 상태가 됨.
참고) save method
@Override
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) {
entityManager.persist(entity); //영속 상태로 만듬.
return entity;
} else {
return entityManager.merge(entity);
}
}
준영속
: 영속 상태의 Entity를 해제 (detach)
Optional<Member> member = memberRepository.findById(memberId); //영속
entityManager.detach(member.get()); // 준영속 상태
삭제
: 1차 캐시와 Database 에서 모두 삭제된 상태를 의미
Optional<Member> member = memberRepository.findById(memberId); //영속
memberRepository.delete(member.get()); // 삭제
참고) delete method
@Override
@Transactional
@SuppressWarnings("unchecked")
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) {
return;
}
Class<?> type = ProxyUtils.getUserClass(entity);
T existing = (T) entityManager.find(type, entityInformation.getId(entity));
// if the entity to be deleted doesn't exist, delete is a NOOP
if (existing == null) {
return;
}
entityManager.remove(entityManager.contains(entity) // 삭제
? entity
: entityManager.merge(entity));
}