1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Java / Kotlin】Entityでよく使うカラムを共通化する

Last updated at Posted at 2022-08-28

はじめに

Entityでよく使うカラムの共通化の仕方について、
考えたことをまとめておきます。

Javaで共通化

各テーブルに共通して存在するカラム(たとえば created_atとか)は抽象クラスに切り出し。

  • 抽象クラス
CommonEntity.java
/**
 * 共通Entity(各テーブルで保持する項目)
 */
@MappedSuperclass
@Data
public abstract class CommonEntity {

  @Column(updatable = false)
  private String createdBy;
  @Column(updatable = false)
  private LocalDateTime createdAt;
  private String updatedBy;
  private LocalDateTime updatedAt;

  // 登録前に共通的に実行されるメソッド(呼び出し不要)
  @PrePersist
  public void preInsert() {
    LocalDateTime localDateTime = LocalDateTime.now();
    setCreatedAt(localDateTime);
    setUpdatedAt(localDateTime);
  }

  // 更新前に共通的に実行されるメソッド(呼び出し不要)
  @PreUpdate
  public void preUpdate() {
    setUpdatedAt(LocalDateTime.now());
  }

  // 登録前に共通的に実行されるメソッド
  public void initUserId(CustomRequestHeaderForm header) {
    this.setCreatedBy(header.getLoginUserId());
    this.setUpdatedBy(header.getLoginUserId());
  }

  // 更新前に共通的に実行されるメソッド
  public void updateUserId(CustomRequestHeaderForm header) {
    this.setUpdatedBy(header.getLoginUserId());
  }
}
  • エンティティ
MemoEntity.java
@Entity
@Getter
@Setter
@Builder
@Table(name = "memo")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class MemoEntity extends CommonEntity implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String title;

  private String content;

  @Builder.Default
  private Boolean isDeleted = ApplicationConstant.IS_NOT_DELETED;

  public void delete() {
    isDeleted = ApplicationConstant.IS_DELETED;
  }
}

なにがうれしいか

  • 登録前や更新前に実行したいお決まりの処理を切り出すことができる
    • エンティティを定義するたびに、同じことを書く必要がなくなる
  • エンティティがスッキリする
  • ドメインの実装に集中できる

Kotlinで共通化

上記と同じことを実装しようとすると、Kotlinでは以下のようになりました。

  • 抽象クラス

ちなみに、data classはスーパークラスになれないみたいです。

CommonEntity.kt
@MappedSuperclass
abstract class CommonEntity {
    @Column(updatable = false)
    lateinit var createdAt: LocalDateTime
    lateinit var updatedAt: LocalDateTime

    @Column(updatable = false)
    lateinit var createdBy: String
    lateinit var updatedBy: String

    fun initUserId(header: CustomRequestHeaderForm) {
        createdBy = header.loginUserId
        updatedBy = header.loginUserId
    }

    fun updateUserId(header: CustomRequestHeaderForm) {
        updatedBy = header.loginUserId
    }

    @PrePersist
    fun preInsert() {
        val now = LocalDateTime.now()
        createdAt = now
        updatedAt = now
    }

    @PreUpdate
    fun preUpdate() {
        updatedAt = LocalDateTime.now()
    }
}
  • エンティティ
MemoEntity.kt
@Entity
@Table(name = "memo")
data class MemoEntity(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long?,
    val title: String,
    val content: String,
    var isDeleted: Boolean,
): CommonEntity() {
  fun delete() {
    isDeleted = ApplicationConstant.IS_DELETED
  }
}

微妙だなと思ったこと

コンストラクタでまとめて初期化できない

抽象クラスのフィールドはコンストラクタ内で初期化できないのでスッキリしません。。
(Javaも同じではありますが、Kotlinではコンストラクタで完結できるとスッキリ書けるので・・)

  • NG

本当はこんな感じに書きたい。けどエラる。

MemoRequestForm.kt
data class MemoRequestForm (
    val title: String,
    val content: String,
) {
    fun toEntity(header: CustomRequestHeaderForm, id: Long? = null) =
        MemoEntity(
            id,
            title,
            content,
            header.loginUserId // ←抽象クラスのフィールドなのでエラる!!
        )
    }
}
  • OK
    以下だとエラーにならない。
MemoRequestForm.kt
data class MemoRequestForm (
    val title: String,
    val content: String,
) {
    fun toEntity(header: CustomRequestHeaderForm, id: Long? = null): MemoEntity {
      val entity = MemoEntity(
          id,
          title,
          content,
      )
      entity.createdBy = header.loginUserId // 別で代入してあげる必要がある。
      return entity
    }
  }
}

抽象クラスでvarを使ってしまっている

上記の通り、コンストラクタ内ですべてのフィールドを初期化できないがために、
以下のように抽象クラスを使わなければ全てvalで書けるのに、varで書く必要が出てしまいます。

  • good
    抽象クラスで切り出さない場合は全てvalで書ける。
MemoEntity.kt
@Entity
@Table(name = "memo")
data class MemoEntity(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long?,
    val title: String,
    val content: String,
    val isDeleted: Boolean,
    @Column(updatable = false)
    val createdAt: LocalDateTime,
    val updatedAt: LocalDateTime,
    @Column(updatable = false)
    val createdBy: String,
    val updatedBy: String,
)

まとめ

以上、JavaとKotlinでのEntityの共通カラムの切り出し方についてまとめてみました。
Kotlinで抽象クラスを使うのは、もしかすると微妙かもしれません・・(好みの問題)

私はどっちがよいのか2、3日考えましたが、答えは出せず。。

皆さんのJava / Kotlinでの共通化Tips、あればぜひ教えてください!!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?