엔티티를 만들고 데이터를 삽입하고 조작할 때 create date와 last modified date를 별도로 업데이트 해주면서 관리하였다.
하지만 이번에 JPA를 공부하면서 별도의 작업 없이 JPA의 Auditing 기능을 사용하면 데이터를 삽입하고 수정할 때 자동으로 날짜를 수정하도록 할 수 있는 기능이 있는 것을 확인했다.
1. Configuration
JPA Auditing을 사용하기 위해서는 기능을 자동으로 활성화 해주는 어노테이션을 붙혀주면 된다. 처음에는 @Configuration을 사용하는 클래스에 함께 선언해주었는데 정상적으로 적용이 되지 않아서 @SpringBootApplication을 사용하는 곳에 적용했더니 성공적으로 적용되었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package com.wedul.springboottest; import com.wedul.common.exception.ValidationException; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.context.annotation.Bean; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.web.context.request.WebRequest; import java.util.Map; @SpringBootApplication @EnableJpaAuditing public class SpringboottestApplication { public static void main(String[] args) { SpringApplication.run(SpringboottestApplication.class, args); } @Bean public ErrorAttributes errorAttributes() { return new DefaultErrorAttributes() { @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace); Throwable error = getError(webRequest); // validatijon Exception에 경우 별도의 처리를 진행한 에러 데이터 추가 if (error instanceof ValidationException) { errorAttributes.put("errors", ((ValidationException)error).getErrors()); } return errorAttributes; } }; } } | cs |
2. Entity에 CreatedDate, LastModifiedDate
Auditing을 사용할 엔티티에는 몇가지 어노테이션이 사용된다.
먼저 설정을위해서 사용되는 @MappedSuperclass, @EntityListeners(AuditingEntityListener.class)이다. 첫 번재 어노테이션은 이후에 사용될 createdDate, modifiedDate와 같은 필드들을 컬럼으로 인식하게 도와주는 역할을 하고 두 번째 어노테이션은 해당 Entity에 Auditing기능을 포함한다라는 명시를 한다.
처음에는 필요한 Entity에만 CreatedDate, lastModifiedDate가 포함하도록 Entity마다 기재해주었다.하지만 많은 Entity에서 필요로 할 것 같고 필요할 때마다 새로 써주기가 비효율적인 것 같아서 추상클래스로 만들고 필요한 Entity에서 이를 상속받아서 사용하도록 하였다.
- 추상클래스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package com.wedul.common.dto; import lombok.Getter; 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; /** * 모든 Entity들의 상위 클래스가 되어 Entity들의 createdDate, modifiedDate를 자동으로 관리 * * @author wedul * @since 2018. 8. 14. **/ @Getter @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class TimeEntity { @CreatedDate private LocalDateTime createdDate; @LastModifiedDate private LocalDateTime modifiedDate; } | cs |
- 상속받아서 사용하는 Product Entity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package com.wedul.springboottest.product.dto; import com.wedul.common.dto.BaseTimeEntity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.*; import java.io.Serializable; import java.sql.Timestamp; /** * 상품 정보 * * @author wedul * @since 2018. 08. 12 **/ @Data @AllArgsConstructor @NoArgsConstructor @Entity // class (hibernate) @Table(name = "product") public class ProductDto extends TimeEntity implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long productId; @Column(nullable = false, unique = true) private String productName; @Column(nullable = false) private long price; public ProductDto(ProductRequestDto req) { this.productName = req.getProductName(); this.price = req.getPrice(); } } | cs |
테스트 코드를 작성하여 정상적으로 시간값이 들어가고 또 변경되는지 확인해보자.
1. 처음 데이터 삽입시 시간값 입력 테스트
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | /** * Product Test * * @author wedul * @since 2018. 8. 14. **/ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest // rolleback 설정 @Rollback(value=true) public class ProductTest { private MockMvc mockMvc; private final MediaType mediaType = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); @Autowired ProductCtrl productCtrl; @Autowired ProductServiceI productService; @Before // before 클래스는 한번만 실해되고 before는 각 테스트마다 실행된다. public void beforeClass() { this.mockMvc = standaloneSetup(productCtrl).build(); } @Test public void insertTest_reqestBody() throws Exception { // Mock Request Builder MockHttpServletRequestBuilder req = post("/api/product/new") .content(CommonUtil.getJsonStrFromObject(new ProductRequestDto("i-mac", 2220011L))) .contentType(mediaType); // 테스트 MvcResult result = mockMvc.perform(req).andExpect(status().isOk()).andReturn(); ResultDto resultDto = CommonUtil.getObjectFromJsonStr(result.getResponse().getContentAsString(), ResultDto.class); assertTrue(resultDto.isResult()); } } | cs |
정상적으로 날짜값이 들어가 있는 것을 확인 할 수있다. 이제 수정 테스트를 진행해보자.
2. 수정 후 last modified date 시간 변경여부 테스트
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Test public void updateTest_requestBody() throws Exception { // Mock Request Builder MockHttpServletRequestBuilder req = put("/api/product/edit") .content(CommonUtil.getJsonStrFromObject(new ProductRequestDto(1,"macbook pro", 2312111L))) .contentType(mediaType); // 테스트 MvcResult result = mockMvc.perform(req).andExpect(status().isOk()).andReturn(); ResultDto resultDto = CommonUtil.getObjectFromJsonStr(result.getResponse().getContentAsString(), ResultDto.class); assertTrue(resultDto.isResult()); } | cs |
업데이트 후에 정상적으로 modified_date만 변경된 것을 확인할 수있다.
좋은 기능인것같다. JPA를 공부하고 있는데 Mybatis보다 솔직히 불편하다 그런데 한번 잘 구축해놓으면 편하기는 하다. 그래도 무엇하나 변경된다면 결국 너무 손이 많이가고 러닝커브가 좀 심하다.
https://github.com/weduls/spring_boot_test
'web > Spring' 카테고리의 다른 글
Facebook Javascript plugin과 spring security를 이용한 페이스북 로그인 (0) | 2018.10.04 |
---|---|
Spring Boot application.properties 암호화 내역 복호화 방법 (0) | 2018.10.04 |
Spring Validation을 이용해서 요청 검증처리 (0) | 2018.08.09 |
Spring Application Test 정리 (0) | 2018.08.07 |
Java 9 이후 deprecated된 Spring @PostConstruct와 @PreDestory 대안소개 (0) | 2018.08.07 |