こんにちは。こまつです。
概要
Java8からJavaにもnullセーフが入りました。optionalを使えばヌルポがなくなります。
とか言いながら、Java+SpringDataJPAではoptionalをマッピングできません。カスですね。
Kotlinならどうなるんでしょうか。
前提
nullセーフとはヌルポになりうるコードをコンパイルエラーにしてくれるやつです。
Entityは特にnullセーフにする重要度が高いです。
nulセーフサンプル.java
String randomStr = new Random().nextBoolean() ? "わーい" : null; // nullかも知れない文字列
Integer length1 = randomStr.length(); // 1/2でヌルポ
Optional<String> str = Optional.ofNullable(randomStr); // null知 文字列型定義
Integer length2 = str.length(); // コンパイルエラー
Integer length3 = str.map(s -> s.length()).orElseGet(() -> 0); // コンパイルOK(nullであればlength=0)
Java+JPAのOptionalはこうしたい。けどできない
肝心のOptional部分でJPAのエラーとなります。
javaでjpa.java
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
public class Post {
@Id
private Long id;
@Column(length = 100, nullable = false)
private String title;
@Column(length = 1000, nullable = true) // nullを許容
private Optional<String> body; <----こうしたいがエラーになる
}
のでJava+JPAではこうするしかない(たぶん)
AllArgsConstructorを諦めてコンストラクタ、アクセサを自前で実装しています。
sono1.java
@Entity
@NoArgsConstructor
@Setter
public class Post {
@Id
private Long id;
@Column(length = 100, nullable = false)
private String title;
@Column(length = 1000, nullable = true) // nullを許容
private String body;
public Post(Optional<Long> id, String title, Optional<String> body) {
this.id = id.orElseGet(() -> null);
this.title = title;
this.body = body.orElseGet(() -> null);
}
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public Optional<String> getBody() {
return Optional.ofNullable(body);
}
}
Kotlin+JPAなら
普通に実装すればnullセーフのプロパティを持てます。
Entity.kt
@Entity
data class Post(
@Id
val id: Long,
@Column(length = 100, nullable = false)
val title: String,
@Column(length = 1000, nullable = true) // nullを許容
val body: String?, <---- JavaのOptional<String>と等価
): Serializable
実際に動くか登録してみます。
実際に登録してみる.kt
val post = Post(1, "nullじゃない", "内容!")
postRepository.save(post)
val post2 = Post(2, "nullなんです", null)
postRepository.save(post2)
想定通りに登録されているようです。取得も問題ありませんでした。
まとめ
この問題はJavaに対する大きな不満点でしたが、Kotlinでは解消されているようです。