プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやる

Spring Dataを使って、自動的に監査証跡を保存する

業務アプリケーションを作っていると、監査証跡ということで、作成者、作成日時、更新者、更新日時を保存するということがあると思います。Spring Dataのアノテーションを使うと、自動でセットしてくれるので、アプリケーションで決まりきったコードを書かなくて済むということみたいです。

Task.java(モデルクラス)

package sample.model;

import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.util.Date;

@Entity
@EntityListeners(value = AuditingEntityListener.class)
public class Task {

    @Id
    @GeneratedValue
    private Long id;

    private String taskName;

    @CreatedBy
    private String createdBy;

    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    private Date createdDate;

    @LastModifiedBy
    private String modifiedBy;

    @Temporal(TemporalType.TIMESTAMP)
    @LastModifiedDate
    private Date modifiedDate;

    public Task() {
    }

    @Override
    public String toString() {
        return "Task{" +
                "id=" + getId() +
                ", taskName='" + getTaskName() + '\'' +
                ", createdBy='" + getCreatedBy() + '\'' +
                ", createdDate=" + getCreatedDate() +
                ", modifiedBy='" + getModifiedBy() + '\'' +
                ", modifiedDate=" + getModifiedDate() +
                '}';
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTaskName() {
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public String getModifiedBy() {
        return modifiedBy;
    }

    public void setModifiedBy(String modifiedBy) {
        this.modifiedBy = modifiedBy;
    }

    public Date getModifiedDate() {
        return modifiedDate;
    }

    public void setModifiedDate(Date modifiedDate) {
        this.modifiedDate = modifiedDate;
    }
}

StartupRunner.java(呼び側)

package sample;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import sample.jpa.TaskRepository;
import sample.model.Task;

public class StartupRunner implements CommandLineRunner {
    protected final Log logger = LogFactory.getLog(getClass());

    @Autowired
    private TaskRepository taskRepository;

    @Override
    public void run(String... args) throws Exception {
        // Generate a new record.
        Task task = new Task();
        task.setTaskName("task1");
        Task savedTask = taskRepository.save(task);
        System.out.println(savedTask);

        Thread.sleep(1000);

        // Update the record after 1sec.
        savedTask.setTaskName("task2");
        Task savedTask2 = taskRepository.save(savedTask);
        System.out.println(savedTask2);
    }
}

AuditingConfig(監査証跡の設定クラス)

package sample.jpa;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@Configuration
public class AuditingConfig {
    @Bean
    public AuditorAware<String> createAuditorProvider() {
        return new SecurityAuditor();
    }

    @Bean
    public AuditingEntityListener createAuditingListener() {
        return new AuditingEntityListener();
    }

    public static class SecurityAuditor implements AuditorAware<String> {
        @Override
        public String getCurrentAuditor() {
            return "taka2";
        }
    }
}

実行結果(抜粋)

Task{id=1, taskName='task1', createdBy='taka2', createdDate=Sat Jul 23 23:38:44 JST 2016, modifiedBy='taka2', modifiedDate=Sat Jul 23 23:38:44 JST 2016}
Task{id=1, taskName='task2', createdBy='taka2', createdDate=Sat Jul 23 23:38:44 JST 2016, modifiedBy='taka2', modifiedDate=Sat Jul 23 23:38:45 JST 2016}

呼び側ではtaskNameしかセットしていませんが、createdBy/createdDate/modifiedBy/modifiedDateの各フィールドがセットされていますね。サンプルなので、ユーザは固定の文字列返してますが、webアプリケーションなら、ログインユーザを返すところでしょうね。