Skip to content

No all-open config with Kotlin + Spring Data JPA #1130

@timmyyy

Description

@timmyyy

When generating Spring project with Kotlin and JPA options it creates a maven / gradle config with "jpa" and "spring" plugins:

...
    <build>
        <plugins>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <configuration>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                        <plugin>jpa</plugin>
...

But to enable Hibernate to generate proxies for Entity classes it must create "all-open" plugin config for them as well:

...
           <compilerPlugins>
                        <!-- no-arg constructor for jpa entities -->
                        <plugin>jpa</plugin>
                        <!-- open @Component, @Async, @Transactional, @Cacheable and @SpringBootTest classes -->
                        <plugin>spring</plugin>
                        <!-- open Entities to let them using Lazy with ManyToOne -->
                        <plugin>all-open</plugin>
                    </compilerPlugins>
                    <pluginOptions>
                        <!-- open Entities to let them using Lazy with ManyToOne -->
                        <option>all-open:annotation=javax.persistence.Entity</option>
                        <option>all-open:annotation=javax.persistence.Embeddable</option>
                        <option>all-open:annotation=javax.persistence.MappedSuperclass</option>
                    </pluginOptions>
...

We can see the same config in Spring Boot + Kotlin tutorial on the official website. Also there was a discussion about adding this behaviour in "jpa" plugin in KT-28594

Otherwise we easily get into N+1 problem when querying an entity collection with @ManyToOne mapping in it:

@Entity
@Table(name = "commits")
class Commit(
    @Id var id: Int = 0,
    var hash: String? = null,

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id")
    var author: Author
)

@Table(name = "authors")
@Entity
class Author (
    @Id var id: Int = 0,

    @OneToMany(mappedBy = "author")
    var commits: MutableList<Commit> = mutableListOf()
)

interface CommitRepository : JpaRepository<Commit, Int> 

Calling commitRepository.findAll() causes Hibernate to fetch firstly all commits and then, as it fails to create a proxy for the mapped fields, their authors, each in a separate query. Moreover, if commit has other @ManyToOne mappings - they'd go in a separate query too, and if author has it's own @ManyToOne mappings ...you know where it leads.

When I select Java to use in a generated project this lazy fetch behavior is supported out-of-the-box due to the fact that all classes in Java are "open" from Kotlin perspective.

So I am absolutely sure that this config should be added in standard Kotlin + Spring Data JPA project initializr as I constantly see how such applications go in production causing enormous overhead there😩

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions