タイトルの通り、レコードの作成日時と更新日時を自動設定してみる。

article.setCreatedDatetime(new Date());
article.setUpdatedDatetime(new Date());

こういうのを都度書くのはバカバカしいのでJPAに自動的に設定されるようにします。

Table

適当なテーブルを作ります。

$ mysql --version
mysql Ver 15.1 Distrib 5.5.52-MariaDB, for Linux (x86_64) using readline 5.1

DROP TABLE article;
CREATE TABLE IF NOT EXISTS article (
id INT(11) NOT NULL AUTO_INCREMENT,
title VARCHAR(50) DEFAULT NULL,
created_datetime DATETIME NOT NULL,
updated_datetime DATETIME NOT NULL,
PRIMARY KEY(id)
);

Entity

EntityはAbstractEntityに日時を自動更新する仕組みを持って、継承して使うようにします。

まず全てのentityの親となるEntityを作ります。

作成前、更新前などの永続化処理にフックするためのアノテーションがあるのでそれを使います。

annotation 備考
@PrePersist 永続化前に実行
@PostPersist 永続化後に実行
@PreUpdate 更新処理前に実行
@PostUpdate 更新処理後に実行
@PreRemove 削除前に実行
@PostRemove 削除後に実行
@PreLoad 読み込み前に実行
@PostLoad 読み込み後に実行

永続化前と更新前に処理を実行したいので、 @PrePersist@PreUpdate を使ってみます。

@Getter
@Setter
@MappedSuperclass
public class AbstractEntity {
@Column(name = "created_datetime")
private Date createdDatetime;

@Column(name = "updated_datetime")
private Date updatedDatetime;

@PrePersist
public void onPrePersist() {
setCreatedDatetime(new Date());
setUpdatedDatetime(new Date());
}

@PreUpdate
public void onPreUpdate() {
setUpdatedDatetime(new Date());
}
}

AbstractEntityを継承した適当なクラスを作ります。

@Setter
@Getter
@Entity(name = "article")
public class Article {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name = "title")
private String title;
}

Repository

repositoryを追加します。

@Repository
public interface ArticleRepository extends JpaRepository<Article, Long> {
}

Test

@PrePersistの動作を確認するためCreatedDatetimeUpdatedDatetimeを指定せず書き込んでみます。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class ArticleRepositoryTest {
@Autowired
private ArticleRepository articleRepository;

@Test
public void testCreateArticle() {
Article article = new Article();
article.setTitle("title1");
articleRepository.saveAndFlush(article);
}
}

確認。

MariaDB [sample]> SELECT * FROM article;
+----+--------+---------------------+---------------------+
| id | title | created_datetime | updated_datetime |
+----+--------+---------------------+---------------------+
| 1 | title1 | 2017-09-08 01:28:51 | 2017-09-08 01:28:51 |
+----+--------+---------------------+---------------------+
1 row in set (0.00 sec)

超簡単ですね。

次は@PreUpdate

@Test
public void testCreateArticle() throws InterruptedException {
Article article = new Article();
article.setTitle("title2");
article = articleRepository.saveAndFlush(article);
Thread.sleep(2000);
articleRepository.saveAndFlush(article);
}

Updateだけ確認するために2000ms間Sleepして実行した後に確認すると。

MariaDB [sample]> SELECT * FROM article;
+----+--------+---------------------+---------------------+
| id | title | created_datetime | updated_datetime |
+----+--------+---------------------+---------------------+
| 1 | title1 | 2017-09-08 01:28:51 | 2017-09-08 01:28:51 |
| 3 | title2 | 2017-09-08 01:45:02 | 2017-09-08 01:45:04 |
+----+--------+---------------------+---------------------+

いい感じですね。

MariaDBの機能で実現

最新版のMariaDBにCURRENT_TIMESTAMPUPDATE CURRENT_TIMESTAMPがあるらしくアプリ側で設定しなくても自動的に書き込めるらしい。

MariaDB 5.5.52では使えなかった。5.6以上で使えるかもしれない。(調べていない。)

CREATE TABLE IF NOT EXISTS article (
id INT(11) NOT NULL AUTO_INCREMENT,
title VARCHAR(50) DEFAULT NULL,
created_datetime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_datetime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(id)
);

参考

  1. https://mariadb.com/kb/en/the-mariadb-library/timestamp/