Search code examples
hibernatejpadefault-constructorkotlin

Kotlin with JPA: default constructor hell


As JPA requires, @Entity classes should have a default (non-arg) constructor to instantiate the objects when retrieving them from the database.

In Kotlin, properties are very convenient to declare within the primary constructor, as in the following example:

class Person(val name: String, val age: Int) { /* ... */ }

But when the non-arg constructor is declared as a secondary one it requires values for the primary constructor to be passed, so some valid values are needed for them, like here:

@Entity
class Person(val name: String, val age: Int) {
    private constructor(): this("", 0)
}

In case when the properties have some more complex type than just String and Int and they're non-nullable, it looks totally bad to provide the values for them, especially when there's much code in primary constructor and init blocks and when the parameters are actively used -- when they're to be reassigned through reflection most of the code is going to be executed again.

Moreover, val-properties cannot be reassigned after the constructor executes, so immutability is also lost.

So the question is: how can Kotlin code be adapted to work with JPA without code duplication, choosing "magic" initial values and loss of immutability?

P.S. Is it true that Hibernate aside of JPA can construct objects with no default constructor?


Solution

  • As of Kotlin 1.0.6, the kotlin-noarg compiler plugin generates synthetic default construtors for classes that have been annotated with selected annotations.

    If you use gradle, applying the kotlin-jpa plugin is enough to generate default constructors for classes annotated with @Entity:

    buildscript {
        dependencies {
            classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
        }
    }
    
    apply plugin: "kotlin-jpa"
    

    For Maven:

    <plugin>
        <artifactId>kotlin-maven-plugin</artifactId>
        <groupId>org.jetbrains.kotlin</groupId>
        <version>${kotlin.version}</version>
    
        <configuration>
            <compilerPlugins>
                <plugin>jpa</plugin>
            </compilerPlugins>
        </configuration>
    
        <dependencies>
            <dependency>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-noarg</artifactId>
                <version>${kotlin.version}</version>
            </dependency>
        </dependencies>
    </plugin>