2

Using Kotlin, Spring Data JPA with Hibernate. Consider following JPA entity:

@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
    }
}

From the language level perspective, the Application.k8s property is never null. So accessing the k8s property without any null-checks should be safe.

Using, for example, findAll() function from a classic CrudRepository:

@Repository
interface ApplicationRepository : CrudRepository<Application, Long>

I will get a list of Application instances, where the non-null k8s property is null (if k8s.image is null).

This will cause a NullPointerException at runtime when you access the property k8s even if it shouldn't (thanks to Kotlin's null-safety mechanism). So it is very confusing and can lead to unexpected bugs. It may be caused by some Hibernate interoperability issues I guess? Hibernate finds out that all fields in the embeddable class are null, so it sets it to null using reflection?

Solution 1:

Declare k8s property (and other embedded entities) always as nullable, which will force additional null checks.

Solution 2:

@Embedded
var k8s: K8s = K8s()
   get() = if (field == null) K8s() else field

This is also confusing since there is a null check to a non-nullable type. So it generates a compiler warning: Condition 'field == null' is always 'false'

Solution 3: Declare a helper class with a dummy property that ensures that there is always something that is never null.

@Embeddable
@MappedSuperclass
open class NonNullEmbeddable {
    @Formula("0")
    private var internalNonNullProperty: Int = 0
}

Usage:

class K8s : NonNullEmbeddable() {
    @Column(name = "k8s_image")
    var image: String? = null
}

Any thoughts? Is it a bug or expected behavior?

I would understand if the Application entity comes from a Java code (https://kotlinlang.org/docs/java-interop.html#null-safety-and-platform-types). But since the Application is declared as Kotlin class, it feels just wrong.

jantobola
  • 688
  • 2
  • 10
  • 28

1 Answers1

0

This is according to JPA specs, when all fields on an @Embeddable are null the @Embeddable itself is set to null.

If this is right or wrong is a topic for a different discussion, but that's how JPA defines embeddables. You are trying to move away from the spec which is not a good direction in general.

Maybe you want something different from the concept of embeddable?

Pedro Borges
  • 1,568
  • 1
  • 14
  • 25