Spring Data Jpaでレコードの登録日時/更新日時を自動で設定する方法
Spring Data Jpaでレコードの登録日時/更新日時を自動で設定する方法をご紹介します。
upsert処理を実現します。
目次
条件
- Spring Tool Suite 4
- Spring Boot 2.5.0
- PostgreSQL 13.3
- Spring Data Jpa
- lombok
前提
DBテーブル
以下のようなテーブルを使用します。
CREATE TABLE post
(
id bigint NOT NULL primary key,
title character varying(255),
create_at timestamp NOT NULL,
update_at timestamp NOT NULL
)
CREATE TABLE post
(
id bigint NOT NULL primary key,
title character varying(255),
create_at timestamp NOT NULL,
update_at timestamp NOT NULL
)
CREATE TABLE post ( id bigint NOT NULL primary key, title character varying(255), create_at timestamp NOT NULL, update_at timestamp NOT NULL )
プロジェクトの構成
ここでは、以下のような構成とします。
ソース
model
lombokを使用して、getterやsetterなどのソースはアノテーション指定とします。
登録日時と更新日時を扱うクラスを作成します。
- @PrePersistを用いて、insert時に登録日時と更新日時に現在日時を設定します。
- @PreUpdateを用いて、update時に更新日時を設定します。
- 登録日時のカラムは、updatable = falseとすることで更新時に影響を受けないようにします。
TimeEntity.java
package com.example.model;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@MappedSuperclass
public class TimeEntity {
@Column(name = "create_at", updatable = false)
private Timestamp createAt;
@Column(name = "update_at")
private Timestamp updateAt;
@PrePersist
public void onPrePersist() {
setCreateAt(new Timestamp(System.currentTimeMillis()));
setUpdateAt(new Timestamp(System.currentTimeMillis()));
}
@PreUpdate
public void onPreUpdate() {
setUpdateAt(new Timestamp(System.currentTimeMillis()));
}
}
package com.example.model;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@MappedSuperclass
public class TimeEntity {
@Column(name = "create_at", updatable = false)
private Timestamp createAt;
@Column(name = "update_at")
private Timestamp updateAt;
@PrePersist
public void onPrePersist() {
setCreateAt(new Timestamp(System.currentTimeMillis()));
setUpdateAt(new Timestamp(System.currentTimeMillis()));
}
@PreUpdate
public void onPreUpdate() {
setUpdateAt(new Timestamp(System.currentTimeMillis()));
}
}
package com.example.model; import java.sql.Timestamp; import javax.persistence.Column; import javax.persistence.MappedSuperclass; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import lombok.Getter; import lombok.Setter; @Getter @Setter @MappedSuperclass public class TimeEntity { @Column(name = "create_at", updatable = false) private Timestamp createAt; @Column(name = "update_at") private Timestamp updateAt; @PrePersist public void onPrePersist() { setCreateAt(new Timestamp(System.currentTimeMillis())); setUpdateAt(new Timestamp(System.currentTimeMillis())); } @PreUpdate public void onPreUpdate() { setUpdateAt(new Timestamp(System.currentTimeMillis())); } }
登録日時と更新日時を扱うクラスを継承します。
Post.java
package com.example.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "post")
public class Post extends TimeEntity {
@Id
@Column(name = "id")
private Long id;
@Column(name = "title")
private String title;
}
package com.example.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "post")
public class Post extends TimeEntity {
@Id
@Column(name = "id")
private Long id;
@Column(name = "title")
private String title;
}
package com.example.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Setter @Getter @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "post") public class Post extends TimeEntity { @Id @Column(name = "id") private Long id; @Column(name = "title") private String title; }
repository
PostRepository.java
package com.example.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.model.Post;
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
package com.example.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.model.Post;
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
package com.example.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.example.model.Post; @Repository public interface PostRepository extends JpaRepository<Post, Long> { }
service
今回使用するメソッドは、以下のうちupsert()のみです。
PostService.java
package com.example.service;
import java.util.Optional;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.model.Post;
import com.example.repository.PostRepository;
@Service
@Transactional
public class PostService {
@Autowired
PostRepository postRepository;
/**
* IDを指定して対象レコードを取得する
*
* @param id
* @return
*/
public Optional<Post> findById(Long id) {
return postRepository.findById(id);
}
/**
* レコードを追加または更新する
*
* @param post
*/
public void upsert(Post post) {
postRepository.save(post);
}
/**
* IDを指定して対象レコードを削除する
*
* @param id
*/
public void delete(Long id) {
postRepository.deleteById(id);
}
}
package com.example.service;
import java.util.Optional;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.model.Post;
import com.example.repository.PostRepository;
@Service
@Transactional
public class PostService {
@Autowired
PostRepository postRepository;
/**
* IDを指定して対象レコードを取得する
*
* @param id
* @return
*/
public Optional<Post> findById(Long id) {
return postRepository.findById(id);
}
/**
* レコードを追加または更新する
*
* @param post
*/
public void upsert(Post post) {
postRepository.save(post);
}
/**
* IDを指定して対象レコードを削除する
*
* @param id
*/
public void delete(Long id) {
postRepository.deleteById(id);
}
}
package com.example.service; import java.util.Optional; import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.example.model.Post; import com.example.repository.PostRepository; @Service @Transactional public class PostService { @Autowired PostRepository postRepository; /** * IDを指定して対象レコードを取得する * * @param id * @return */ public Optional<Post> findById(Long id) { return postRepository.findById(id); } /** * レコードを追加または更新する * * @param post */ public void upsert(Post post) { postRepository.save(post); } /** * IDを指定して対象レコードを削除する * * @param id */ public void delete(Long id) { postRepository.deleteById(id); } }
controller
PostServiceを持いて、レコードのupsertを行います。
- 登録日時と更新日時以外のデータを指定します。
- 登録日時と更新日時はプログラム任せです。
package com.example.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.model.Post;
import com.example.service.PostService;
@RestController
public class TestController {
@Autowired
PostService postService;
@GetMapping(path = "/test")
public String getTest(HttpServletRequest request) {
postService.upsert(new Post(Long.valueOf(11), "test"));
return "Hello World!";
}
}
package com.example.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.model.Post;
import com.example.service.PostService;
@RestController
public class TestController {
@Autowired
PostService postService;
@GetMapping(path = "/test")
public String getTest(HttpServletRequest request) {
postService.upsert(new Post(Long.valueOf(11), "test"));
return "Hello World!";
}
}
package com.example.controller; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.example.model.Post; import com.example.service.PostService; @RestController public class TestController { @Autowired PostService postService; @GetMapping(path = "/test") public String getTest(HttpServletRequest request) { postService.upsert(new Post(Long.valueOf(11), "test")); return "Hello World!"; } }
実行結果
初回
登録日時と更新日時が自動で設定されました。
2回目(更新)
以下のようにして、レコードを更新します。
postService.upsert(new Post(Long.valueOf(11), "testUpdate!!!"));
postService.upsert(new Post(Long.valueOf(11), "testUpdate!!!"));
postService.upsert(new Post(Long.valueOf(11), "testUpdate!!!"));
レコードが更新され、登録日時はそのままで、更新日時が更新されていることがわかります。
参考
Qiita:JPAでEntityを親クラスと子クラスに分ける方法
https://qiita.com/zateon/items/924ad592c7c94dee4381