【发布时间】:2021-09-11 19:08:44
【问题描述】:
使用 Kotlin、Spring Data JPA 和 Hibernate。 考虑以下 JPA 实体:
@Entity
@Table(name = "applications")
class Application(
@Column(name = "name", nullable = false)
var name: String,
@Column(name = "version", nullable = false)
var version: String,
) {
@Id
@SequenceGenerator(name = "seq_gen_application_id", sequenceName = "seq_application_id")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_gen_application_id")
@Column(name = "id", unique = true, nullable = false)
var id: Long? = null
@Embedded
var/val k8s: K8s = K8s()
@Embeddable
class K8s {
@Column(name = "k8s_image")
var image: String? = null
}
}
从语言级别的角度来看,Application.k8s 属性永远不会为空。所以访问 k8s 属性而不进行任何空检查应该是安全的。
例如,使用经典 CrudRepository 中的 findAll() 函数:
@Repository
interface ApplicationRepository : CrudRepository<Application, Long>
我将获得Application 实例的列表,其中非空k8s 属性是 null(如果k8s.image 为空)。
当您访问属性k8s 时,这将在运行时导致NullPointerException,即使它不应该(感谢Kotlin 的空安全机制)。所以它非常令人困惑,并可能导致意想不到的错误。我猜这可能是由一些 Hibernate 互操作性问题引起的? Hibernate发现embeddable类中的所有字段都是null,所以它使用反射将其设置为null?
解决方案 1:
将k8s 属性(和其他嵌入式实体)始终声明为可为空,这将强制进行额外的空检查。
解决方案 2:
@Embedded
var k8s: K8s = K8s()
get() = if (field == null) K8s() else field
这也令人困惑,因为对不可为空的类型进行了空检查。所以它会生成一个编译器警告:
Condition 'field == null' is always 'false'
解决方案 3: 声明一个具有虚拟属性的辅助类,以确保总有一些东西永远不会为空。
@Embeddable
@MappedSuperclass
open class NonNullEmbeddable {
@Formula("0")
private var internalNonNullProperty: Int = 0
}
用法:
class K8s : NonNullEmbeddable() {
@Column(name = "k8s_image")
var image: String? = null
}
有什么想法吗?这是错误还是预期行为?
如果Application 实体来自 Java 代码 (https://kotlinlang.org/docs/java-interop.html#null-safety-and-platform-types),我会理解。但是由于Application被声明为Kotlin类,感觉就错了。
【问题讨论】:
标签: hibernate kotlin spring-data-jpa nullpointerexception null