LoginSignup
0

More than 1 year has passed since last update.

posted at

updated at

Organization

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

概要

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にしてから使う必要がなくなるので、ロジック側のコードがすっきりすると思います。ぜひ活用してみてください。

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
What you can do with signing up
0