12
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 3 years have passed since last update.

[Java] 再帰的ジェネリクスでEntityから不要なデータを省きつつCloneする

Last updated at Posted at 2020-12-14

概要

DBからデータを読み込み、ある一部分を変更したあと新しいデータとして保存する時に、clone()メソッドを使うと思います。
本記事は、再帰的ジェネリクスを活用してイイ感じにcloneしてみようというものです。

再帰的ジェネリクスとは

型パラメータをもつクラスを継承するときに、その型パラメータに対して自身のクラス名を指定するアレです。
コードでいうとこういうものです。

public class Entity<T extends Entity<T>> {
}

とするやつですね。
こうすることで何が嬉しいのかというと、戻り値が親クラスである継承元のメソッドをオーバーライドしたときに、その戻り値を自分のクラスにすることができます。
今回のcloneメソッドを例にすると、

public class Entity<T extends Entity<T>> implements Cloneable {

    @Override 
    public T clone() {
        return (T) super.clone();
    }
}

このクラスを定義する時点でキャストする必要が一回ありますが、以降継承するときには意識する必要がなくなります。

Spring用のBaseEntityをつくる

複合キーを想定しない作りにはなるのですが、新しくEntityをコピーするときにIDだけ省いたEntityをクローンすると言ったことが可能です。
単純に、先程のclone()内でIDnullにしてあげればいいだけです。

@Data
@MappedSuperclass
public class BaseEntity<T extends BaseEntity<T>> implements Cloneable {

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

    @Override
    @SuppressWarnings("unchecked")
    public T clone() {
        try {
            T clonedEntity = (T) super.clone();
            clonedEntity.setId(null);

            return clonedEntity;
        } catch (CloneNotSupportedException e) {
            // Cloneableを実装しているためスローされない。握り潰し方をJavaAPIにあわせる。
            throw new InternalError(e);
        }
    }
}

これを継承したクラスでは、常にIDnullになっているEntityをクローンすることができるようになります。
先程のBaseEntityを継承してもう一枚クラスを重ねることもできます。
フィールドの意味合いをまとめたいときに有効だと思います。
下記コードは監査証跡を自動で残すクラスで、クローン時は先のクラスに渡さないというものです。


@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class AuditingEntity<T extends AuditingEntity<T>> extends BaseEntity<T> {

    @CreatedBy
    protected String createdBy;
    @CreatedDate
    protected LocalDateTime createdAt;
    @LastModifiedBy
    protected String updatedBy;
    @LastModifiedDate
    protected LocalDateTime updatedAt;

    @Override
    public T clone() {
        T clonedEntity = super.clone();
        clonedEntity.setCreatedBy(null);
        clonedEntity.setCreatedAt(null);
        clonedEntity.setUpdatedBy(null);
        clonedEntity.setUpdatedAt(null);

        return clonedEntity;
    }
}

おわりに

Entityをクローンした時にいちいち不要なフィールドをnullにしてから使う必要がなくなるので、ロジック側のコードがすっきりすると思います。ぜひ活用してみてください。

12
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
12
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?