Search code examples
springperformancespring-dataimmutabilityspring-data-jdbc

How to model immutable entities with static factory method


Hello there i have a question concerning the right way of modelling immutable entities:

Consider this entity (edited as of the suggestion by Jens Schauder):

@Getter
@RequiredArgsConstructor(staticName = "of", access = AccessLevel.PACKAGE)
public final class Student {

    private final @Id @Wither
    long studentId;

    @NotNull
    @Size(min = 4, max = 20)
    private final String userId;

    @NotNull
    @Min(0)
    private final int matriculationNumber;

    @NotNull
    @Email
    private final String eMail;
}

So this entity should be immutable and offers a static of creation method. Also the RequiredArgsConstructor builds a private constructor although it should create a package visible one for all final/non null fields per definition. In short i did an AllArgsConstructor so to speak.

This document over here https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#mapping.fundamentals in detail the section about "Object creation internals" states 4 aspects for improved handling - "the constructor to be used by Spring Data must not be private" amongst others which are fulfilled in my opinion.

So my question: Is this pictured entity done right in both ways concerning immutabillity and spring data jdbc internals optimum mapping?

EDIT:

There seems to be a bug with lombok plugin in intellij, hindering the access = AccessLevel.PACKAGE doing the right stuff. See here: https://github.com/mplushnikov/lombok-intellij-plugin/issues/584

Although the issue is already closed a new version of the plugin is not available ...


Solution

  • This depends on your definition of "optimum mapping".

    It should work, so this is already something.

    But the optimization described in the docs cannot be applied because your constructor is private. Therefore you lose the 10% performance boost of that which it probably does not make it "optimal".

    But the 10% boost is about the object instantiation. It is not about the roundtrip to the database which involves:

    • extraction of data from your entities
    • construction (or lookup) of SQL to use
    • sending both to the database
    • performing the query in the database
    • returning the result

    This makes it very likely that the gain from that optimization is well below 10% and in most cases nothing to worry about.

    Of course, you will never really know until you made your own benchmarks with real data. For this, you would need to create an all args constructor which has at least package scope.