QueryDSL : Entity 의 매핑정보를 활용하여 쿼리에 적합하도록 쿼리 전용 클래스(Q클래스)로 재구성해주는 기술
- JPAQueryFactory 을 통한 Q클래스를 활용할 수 있는 기능들을 제공
JPAQueryFactory : 재구성한 Q클래스를 통해 문자열이 아닌 객체 또는 함수로 쿼리를 작성하고 실행하게 해주는 기술
@PersistenceContext
EntityManager em;
public List<User> selectUserByUsernameAndPassword(String username, String password){
JPAQueryFactory jqf = new JPAQueryFactory(em);
QUser user = QUser.user;
List<Person> userList = jpf
.selectFrom(user)
.where(person.username.eq(username)
.and(person.password.eq(password))
.fetch();
return userList;
}
QueryDSL 적용 방법
JPAQueryFactory 사용을 위해 추가해야할 코드
JPAQueryFactory 에 entityManager 를 주입해서 Bean 으로 등록해줘야 한다
// configuration 패키지안에 추가
@Configuration
public class JPAConfiguration {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
테이블 객체 방명록 설정하기 (Auditing)
Auditing : 사용하면 엔티티를 누가 언제 생성/마지막 수정 했는지 자동으로 기록되게 할 수 있다.
@CreatedDate
private Date created;
@LastModifiedDate
private Date updated;
@CreatedBy
@ManyToOne
private Account createdBy;
@LastModifiedBy
@ManyToOne
private Account updatedBy;
Auditing 적용 방법
1. 메인 애플리케이션 위에 @EnableJpaAuditing 추가
@EnableJpaAuditing
@SpringBootApplication
public class Application {
2. 엔티티 클래스 위에 @EntityListeners(AuditingEntityListener.class) 추가
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class TimeStamp {
@CreatedDate
private LocalDateTime createdAt;
@CreatedBy
@ManyToOne
private User createdBy;
@LastModifiedDate
private LocalDateTime modifiedAt;
@LastModifiedBy
@ManyToOne
private User modifiedBy;
}
3. AuditorAware 구현체 만들기
1) createdAt, modifiedAt 은 구현체 없이 동작하지만 createdBy, modifiedBy 는 구현체가 필요하다.
2) SpringSecurity 의 SecurityContextHolder 에서 인증정보안에 담긴 UserDetailsImpl 을 사용하여 user 객체를 가져와서 넣어준다.
SpringSecurity의 JwtFilter에서 우리가 저장해주는 부분 코드
// 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);
}
...
}
@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());
}
}
4. @EnableJpaAuditing에 Auditor 빈 이름 설정하기
@EnableJpaAuditing(auditorAwareRef = "userAuditorAware") // auditorAware 의 빈이름을 넣어준다.
@SpringBootApplication
public class Application {
필요한 부분만 갱신하기 (Dynamic Insert/ Update)
@DynamicInsert
- 이 어노테이션을 엔티티에 적용하게 되면 Insert 쿼리를 날릴 때 null 인 값은 제외하고 쿼리문이 만들어진다
적용 방법
- Entity에 @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
- 이 어노테이션을 엔티티에 적용하게 되면 Update 쿼리를 날릴 때 null인 값은 제외하고 쿼리문이 만들어진다.
적용 방법
@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
'Springboot' 카테고리의 다른 글
Your local changes to the following files would be overwritten by merge: 해결방법 (0) | 2024.07.31 |
---|---|
AWS # Identity and Access Management (0) | 2024.07.05 |
테이블 객체 만들기 (0) | 2024.06.28 |
쿼리 파일 만들기 (QueryMapper) (0) | 2024.06.28 |
데이터베이스 연결(Driver) (0) | 2024.06.27 |