はじめに
SpringBootのエンティティは便利だが、可変(ミュータブル)な実装になりがちなので、
不変(イミュータブル)にできないか検討する。
ありがちな実装
- エンティティ
@Entity
@Table(name = "company")
@Getter
@Setter
public class Company {
@Id
private String companyId;
private String companyName;
}
- 呼び出し元
Company company = new Company();
晴れて、生焼けオブジェクトのできあがり!!
変数company
がどのような状態かよく分からない。。
この状態を避けるために、Company
エンティティを不変にしてみる。
エンティティの制約
エンティティには、引数を持たないコンストラクタが必要。
引数を持たないコンストラクタが存在しないと、
リポジトリのfindByIdとか実行すると、下記のように例外が発生する。
org.hibernate.InstantiationException: No default constructor for entity: : ~~~
引数を持たなければアクセス修飾子は何でもよく、privateでも実行できる。
試しにprivateな引数なしのコンストラクタにブレークポイントを設定したところ、
下記のようにリフレクションで呼ばれていることが確認できる。
リフレクションでsetAccessible(true)
をされたらどうしようも無いが、
こればかりは諦める。
実装例
@Setter
を削除し、privateな引数なしコンストラクタと、引数ありコンストラクタを用意。
フィールドが多くなると引数渡しは呼び出し元がツライため、ビルダーにした。
- エンティティ
@Entity
@Table(name = "company")
@Getter
public class Company {
@Id
private String companyId;
private String companyName;
private Company() {}
private Company(final String companyId, final String companyName) {
this.companyId = companyId;
this.companyName = companyName;
}
public static class CompanyBuilder {
private String companyId;
private String companyName;
public CompanyBuilder companyId(final String companyId) {
this.companyId = companyId;
return this;
}
public CompanyBuilder companyName(final String companyName) {
this.companyName = companyName;
return this;
}
public Company build() {
return new Company(companyId, companyName);
}
}
}
- 呼び出し元
Company company = new CompanyBuilder()
.companyId("12345")
.companyName("株式会社ほげ")
.build();
最後に
もっといい実装方法があれば教えてください。