일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- GitHub
- 스파르타내일배움캠프TIL
- 클래스
- 인스턴스
- 해우소
- Java
- Github_token
- 스레드
- 내일배움캠프
- Token
- 객체지향 언어
- 스파르타내일배움캠프
- #스파르타내일배움캠프TIL
- 메서드
- 회고록
- #스파르타내일배움캠프
- JVM
- 감사기록
- TiL_1st_0419
- 성장기록
- #내일배움캠프
- Git
- Java의 이점
- 변수의 다양성
- Diary 해우소
- diary
- KPT
- static
- 포맷은 최후의 보루
- 생성자
- Today
- Total
몬그로이
Entity-Table 에 사용하기 좋은 어노테이션 기능 본문
<contents>
1. Auditing
- 기본 사용법
- 감사기능(By)까지 추가하여 사용하는 방법 두 가지
2. Dynamic Insert/Update
Auditing
누가/언제 생성/수정 했는지 Column에 기록하는 기능
1. 메인 application 에 @EnableJpaAuditing 를 달아준다
@EnableJpaAuditing
@SpringBootApplication
public class Application {
2. Auditing 기능을 사용할 Entity 클래스 위에 @EntityListeners(AudigingEntity.class) 를 달아준다
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class TimeStamp {
3. Auditing 기능을 적용할 변수 위에 사용할 기능에 맞춰 @CreatedDate 등을 위에 적어준다
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class TimeStamp {
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime modifiedAt;
}
* createdAt, modifiedAt 은 구현체 없이 동작하지만 createdBy, modifiedBy 는 구현체가 필요함
즉, By는 '누군가에 의해서' 그렇게 됐는지를 명시하는 것이므로, '누구'인지 확인하는 절차가 들어간 로직이 필요함
Spring Data JPA에서 사용자 정의 감사 기능을 추가하기
1. AuditorAware 인터페이스의 구현체를 만들어 적용하는 방법 - 예시 2가지
2. @EntityListeners 를 사용하여 Entity 리스너를 등록하는 방법
- Entity 리스너만으로 감사 기능 구현하기
- AuditorAware 와 Entity 리스너 를 이용하여 감사 기능 구현하기
AuditorAware 인터페이스의 구현체를 만드는 방법 - 예시1
1. AuditorAware 인터페이스
public interface AuditorAware<T> {
Optional<T> getCurrentAuditor(); // 단일 메서드 getCurrentAuditor()만을 정의하는 인터페이스
}
2. AuditorAware 구현체 생성
실제로 사용할 사용자 정보를 가져오는 로직 작성
public class SpringSecurityAuditorAware implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
// Spring Security를 통해 인증된 사용자의 이름을 가져옴
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
.filter(authentication -> authentication.isAuthenticated())
.map(authentication -> {
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else {
return principal.toString();
}
});
}
}
* HttpServletRequest를 통한 사용자 정보 가져오거나 다른 다양한 방법들도 가능함
3. @EnableJpaAuditing 에 AuditorAware 빈 이름 등록 - Spring 이 관리해야 사용되므로
AuditorAware 를 @Component나 @Configuration을 통해 빈으로 등록하는 절차
@Configuration
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class JpaAuditingConfiguration {
@Bean
public AuditorAware<String> springSecurityAuditorAware() {
return new SpringSecurityAuditorAware();
}
}
AuditorAware 인터페이스의 구현체를 만드는 방법 - 예시2
pringSecurity 의 SecurityContextHolder 에서 인증정보안에 담긴 UserDetailsImpl 을 사용하여
user 객체를 가져와서 넣어준다
// JwtAuthFilter.java
@Slf4j
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = jwtUtil.resolveToken(request);
if(token != null) {
if(!jwtUtil.validateToken(token)){
jwtExceptionHandler(response, "Token Error", HttpStatus.UNAUTHORIZED.value());
return;
}
Claims info = jwtUtil.getUserInfoFromToken(token);
// 인증정보 세팅함수 호출
setAuthentication(info.getSubject());
}
try {
filterChain.doFilter(request, response);
}catch(FileUploadException e){
jwtExceptionHandler(response,"File Upload Error",400);
}
}
public void setAuthentication(String username) {
// SecurityContextHolder 는 threadLocal 로 구현되어 요청쓰레드 내에서 공유할 수 있다
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = jwtUtil.createAuthentication(username);
context.setAuthentication(authentication); //인증정보(계정정보) 담아주기
SecurityContextHolder.setContext(context);
}
...
}
2.
@Service
public class UserAuditorAware implements AuditorAware<User> {
@Override
public Optional<User> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return Optional.empty();
}
return Optional.of(((UserDetailsImpl) authentication.getPrincipal()).getUser());
}
}
@EnableJpaAuditing(auditorAwareRef = "userAuditorAware") // auditorAware 의 빈이름을 넣어준다.
@SpringBootApplication
public class Application {
위 두 예시 이후
@Entity
@Table(name = "members")
@EntityListeners(AuditingEntityListener.class) // 커스텀 하기 전과 같이 이렇게만 적어줘도 By 기능까지 사용 가능
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
// 감사 정보
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime modifiedAt;
@CreatedBy
private String createdBy;
@LastModifiedBy
private String modifiedBy;
}
@EntityListeners 만 사용하여 Entity 리스너를 등록하는 방법
1. AuditEntityListener 클래스를 생성하여 정의하기
@PrePersist와 @PreUpdate 어노테이션을 사용하여 엔티티의 생성 및 수정 시점에 필요한 작업을 수행
@Component
public class AuditEntityListener {
@PrePersist
public void prePersist(Object target) {
if (target instanceof Auditable) {
Auditable auditable = (Auditable) target;
LocalDateTime now = LocalDateTime.now();
auditable.setCreatedAt(now);
auditable.setModifiedAt(now);
}
}
@PreUpdate
public void preUpdate(Object target) {
if (target instanceof Auditable) {
Auditable auditable = (Auditable) target;
auditable.setModifiedAt(LocalDateTime.now());
}
}
}
2. Auditable 인터페이스 생성
감사 기능을 적용할 Auditable 인터페이스를 정의
이 인터페이스는 생성일자(createdAt)와 수정일자(modifiedAt)를 관리할 수 있는 메서드를 제공함
import java.time.LocalDateTime;
public interface Auditable {
LocalDateTime getCreatedAt();
void setCreatedAt(LocalDateTime createdAt);
LocalDateTime getModifiedAt();
void setModifiedAt(LocalDateTime modifiedAt);
}
3. 엔티티에 감사 기능 적용
@Entity
@EntityListeners(AuditEntityListener.class) // 엔티티 리스너 등록
public class Member implements Auditable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
// Getter, Setter 생략
@Override
public LocalDateTime getCreatedAt() {
return createdAt;
}
@Override
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
@Override
public LocalDateTime getModifiedAt() {
return modifiedAt;
}
@Override
public void setModifiedAt(LocalDateTime modifiedAt) {
this.modifiedAt = modifiedAt;
}
}
AuditorAware 와 EntityListener 를 함께 이용하여 감사 기능 구현하기
1. AuditorAware 인터페이스 구현
public class CustomAuditorAware implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
// 사용자 식별자 또는 이름을 반환하는 로직을 구현
return Optional.of("System"); // 예시로 "System"을 고정적으로 반환
}
}
2. Entity 리스너 구현
@Component
public class AuditingEntityListener {
@Autowired
private CustomAuditorAware customAuditorAware;
@PrePersist
public void setCreatedBy(Object entity) {
if (entity instanceof Auditable) {
Auditable auditable = (Auditable) entity;
auditable.setCreatedBy(customAuditorAware.getCurrentAuditor().orElse("System"));
}
}
@PreUpdate
public void setModifiedBy(Object entity) {
if (entity instanceof Auditable) {
Auditable auditable = (Auditable) entity;
auditable.setModifiedBy(customAuditorAware.getCurrentAuditor().orElse("System"));
}
}
}
3. Entity에 EntityListeners 등록
@Entity
@Table(name = "members")
@EntityListeners(AuditingEntityListener.class)
public class Member implements Auditable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
private String createdBy;
private String modifiedBy;
// Getters and Setters 생략
}
Dynamic Insert/Update
@DynamicInsert 와 @DynamicUpdate 를 Entity 위에 달아주면
Insert 쿼리 사용시 null 인 값을 제외하고 쿼리문이 만들어짐
필드가 많을 수록 효과적
@DynamicInsert 예시
@DynamicInsert
public class User {
...
}
테스트
@Test
void dynamicInsertTest() {
// given
var newUser = User.builder().username("user").build();
// when
userRepository.save(newUser);
// then
// 부분 생성 쿼리
}
적용전
Hibernate:
insert
into
users
(password, username, id)
values
(?, ?, ?) // 141ms 소요
적용후
Hibernate:
insert
into
users
(username, id)
values
(?, ?) // 133ms 소요
@DynamicUpdate 예시
@DynamicUpdate
public class User {
...
}
테스트
@Test
void dynamicUpdateTest() {
// given
var newUser = User.builder().username("user").password("password").build();
userRepository.save(newUser);
// when
newUser.updatePassword("new password");
userRepository.save(newUser);
// then
// 부분 수정 쿼리
}
적용전
Hibernate:
update
users
set
password=?,
username=?
where
id=? // 149ms
적용후
Hibernate:
update
users
set
password=?
where
id=? // 134ms
'Organizing Docs > Spring Docs' 카테고리의 다른 글
Jpa repository 메서드 명명규칙 - 공식문서 참고 (0) | 2024.07.12 |
---|---|
QueryDSL (0) | 2024.06.30 |
MVC 의 annotation 들 (0) | 2024.05.25 |
Bean의 생명주기와 그것을 알게 되었을 때의 이점 (0) | 2024.05.23 |
HTTP 메서드 (0) | 2024.05.20 |