JPA는 데이터의 생성일, 수정일 등 을 자동으로 넣어주는 Audit라는 기능을 제공합니다.
Auditing을 이용하여 엔티티를 언제 생성/수정 했는지 기록하게 할 수 있습니다.
Mybatis Interceptor를 이용하여 Message객체를 DB에 저장할 때 생성일을 (CreatedDate) 자동으로 추가 시켜보겠습니다.
application.properties
mybatis.config-location=classpath:mybatis-config.xml
- mybatis-config의 위치를 설정해줍니다.
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="본인프로젝트.AuditingInterceptor"/>
</plugins>
</configuration>
- AuditingInterceptor라는 Interceptor를 만들어서 플러그인 등록 해줍니다.
Message
public class Message {
private Long id;
private String content;
private LocalDateTime createdDate;
}
- 쉬운 예제를 위해 다른 필드들은 생략하고 createdDate를 Interceptor로 자동으로 넣어보겠습니다.
AuditingInterceptor
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class AuditingInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
if (mappedStatement.getSqlCommandType() == SqlCommandType.INSERT) {
Object parameter = invocation.getArgs()[1];
// Message 객체인지 확인하고 CreatedDate 필드 설정
if (parameter instanceof Message) {
Message message = (Message) parameter;
message.setCreatedDate(LocalDateTime.now());
}
}
return invocation.proceed();
}
}
- Message 객체를 저장 할 때만 createdDate를 세팅 해주기 위해, Message 객체인지 판단하는 로직을 넣고 setCreatedDate를 해줍니다.
- Signature 어노테이션 : 인터셉터가 적용되는 메서드의 정보를 지정하는 어노테이션입니다.
- type : 인터셉터가 적용되는 인터페이스 또는 클래스를 지정합니다
- Executor 인터페이스는 MyBatis에서 SQL 쿼리를 실행하는 데 사용되는 인터페이스입니다. 따라서, Signature 어노테이션은 Executor 인터페이스의 모든 메서드에 인터셉터가 적용됨을 의미합니다.
- method : 인터셉터가 적용되는 메서드의 이름을 지정합니다.
- update 메서드는 insert, update, delete 쿼리를 실행하는 메서드입니다. 따라서, Signature 어노테이션은 update, insert, delete 쿼리를 실행할 때 인터셉터가 적용됨을 의미합니다.
- args : 인터셉터가 적용되는 메서드의 인자 타입을 지정합니다.
- MappedStatement는 SQL 쿼리와 그 쿼리를 실행하는 방법에 대한 정보를 포함하는 객체입니다. 따라서, Signature 어노테이션은 인터셉터가 적용되는 메서드의 인자로 MappedStatement 객체와 Object 객체가 전달됨을 의미합니다.
MessageRepository
public class MessageRepository {
private final MessageMapper messageMapper;
public Long save(Message message) {
messageMapper.save(message);
return message.getId();
}
- MessageRepository의 save 함수는 MessageMapper의 save를 호출합니다.
MessageMapper
@Mapper
public interface MessageMapper {
public void save(Message message);
}
- MessageMapper의 save함수는 MessageMapper.xml 에 정의 된 SQL을 호출합니다.
MessageMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="프로젝트경로.mapper.MessageMapper">
<insert id="save" useGeneratedKeys="true" keyProperty="id" parameterType="com.seojs.ptmanager.domain.message.Message">
insert into message (content, created_date)
values (#{content}, #{createdDate})
</insert>
</mapper>
- save SQL이 정의 된 MessageMapper.xml 입니다.
메세지를 저장 할 클래스 및 함수들은 다 정의 되었고
테스트 에서 MessageRepository의 save를 호출 할 때
createdDate를 넣지 않고 content만 넣어서 SQL을 호출 하면 어떻게 될까요?
messageRepository.save(new Message("message");
Interceptor에서 Message 객체에 setCreatedDate를 해주었기 때문에
insert SQL values에 createdDate가 들어가서 호출 됩니다.
출력 로그
==> Parameters: message(String), false(Boolean), 1(Long) , 2024-02-01T00:28:46.500004(LocalDateTime)