{"id":669971,"date":"2026-01-20T18:25:41","date_gmt":"2026-01-20T17:25:41","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=idea&#038;p=669971"},"modified":"2026-01-30T10:34:38","modified_gmt":"2026-01-30T09:34:38","slug":"how-to-avoid-common-pitfalls-with-jpa-and-kotlin","status":"publish","type":"idea","link":"https:\/\/blog.jetbrains.com\/pt-br\/idea\/2026\/01\/how-to-avoid-common-pitfalls-with-jpa-and-kotlin","title":{"rendered":"How to Avoid Common Pitfalls With JPA and Kotlin"},"content":{"rendered":"\n<p><em>This post was written together with <a href=\"https:\/\/thorben-janssen.com\/\" data-type=\"link\" data-id=\"https:\/\/thorben-janssen.com\/\" target=\"_blank\" rel=\"noopener\">Thorben Janssen<\/a>, who has more than 20 years of experience with JPA and Hibernate and is the author of &#8220;Hibernate Tips: More than 70 Solutions to Common Hibernate Problems&#8221; and the JPA newsletter.<\/em><\/p>\n\n\n\n<p>Kotlin and Jakarta Persistence (also known as JPA) are a popular combination for server-side development. Kotlin offers concise syntax and modern language features, while Jakarta Persistence provides a proven persistence framework for enterprise applications.<\/p>\n\n\n\n<p>However, Jakarta Persistence was originally designed for Java. Some of Kotlin\u2019s popular features and concepts, like null safety and data classes, help you tremendously when implementing your business logic, but they don\u2019t align well with the specification.<\/p>\n\n\n\n<p>This article outlines a set of best practices to help you avoid problems and build reliable persistence layers with Kotlin and Jakarta Persistence. And to share some good news before diving in, IntelliJ IDEA 2026.1 will automatically detect many of these issues, highlight them with warnings, and provide support through various inspections.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"entity-class-design\"><strong>Entity class design<\/strong><\/h2>\n\n\n\n<p>Jakarta Persistence defines several <a href=\"https:\/\/jakarta.ee\/specifications\/persistence\/3.2\/jakarta-persistence-spec-3.2#a18\" target=\"_blank\" rel=\"noopener\">requirements<\/a> for entity classes that form the foundation for how persistence providers manage entity objects.<\/p>\n\n\n\n<p>An entity class must:<\/p>\n\n\n\n<ul>\n<li><strong>Provide a no-argument constructor<\/strong><strong><br><\/strong>The persistence provider uses reflection to call the no-argument constructor to create entity instances when loading data from the database.<\/li>\n\n\n\n<li><strong>Have non-final attributes<\/strong><strong><br><\/strong>When fetching an entity object from the database, the persistence provider sets all attribute values after it calls the no-argument constructor to instantiate the entity object. This process is called hydration.<br>After that is done, the persistence provider keeps a reference to the entity object to perform automatic dirty checks, during which it detects changes and updates the corresponding database records automatically.<\/li>\n\n\n\n<li><strong>Be non-final<\/strong><strong><br><\/strong>The persistence provider often creates proxy subclasses to implement features such as <a href=\"https:\/\/www.baeldung.com\/hibernate-lazy-eager-loading\" target=\"_blank\" rel=\"noopener\">lazy loading<\/a> for @ManyToOne and @OneToOne relationships. For this to work, the entity class can\u2019t be final.<br><\/li>\n<\/ul>\n\n\n\n<p>In addition to these specification requirements, it is a widely accepted best practice to:<\/p>\n\n\n\n<ul>\n<li><strong>Implement <code>equals<\/code>, <code>hashCode<\/code>, and <code>toString<\/code> carefully<br><\/strong>These methods should rely only on the entity\u2019s identifier and type to avoid unexpected behavior in persistence contexts. You can find approaches for better implementing those <a href=\"https:\/\/thorben-janssen.com\/ultimate-guide-to-implementing-equals-and-hashcode-with-hibernate\/\" target=\"_blank\" rel=\"noopener\">here<\/a>.<br><\/li>\n<\/ul>\n\n\n\n<p>These rules are easy to follow in Java but conflict with some of Kotlin\u2019s defaults, such as final classes, immutable properties, and constructor-based initialization.<br><br>The following sections show how to adapt your Kotlin classes to meet these requirements while still using Kotlin\u2019s language features effectively.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Data classes vs. entities<\/h3>\n\n\n\n<p><a href=\"https:\/\/kotlinlang.org\/docs\/data-classes.html\" target=\"_blank\" rel=\"noopener\">Kotlin\u2019s data classes<\/a> are designed to hold data. They are final and provide several utility methods, including getters and setters for all fields, as well as <code>equals<\/code>, <code>hashCode<\/code>, and <code>toString<\/code>.<\/p>\n\n\n\n<p>This makes data classes a great fit for DTOs, which represent query results and are not managed by your persistence provider.<\/p>\n\n\n\n<p>Below is a typical usage of a data class to fetch data:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">data class EmployeeWithCompany(val employeeName: String, val companyName: String)\n\nval query = entityManager.createQuery(\"\"\"\n\u00a0\u00a0\u00a0SELECT new com.company.kotlin.model.EmployeeWithCompany(p.name, c.name)\n\u00a0\u00a0\u00a0\u00a0FROM Employee e\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0JOIN e.company c\n\u00a0\u00a0\u00a0\u00a0WHERE p.id = :id\"\"\")\n\nval employeeWithCompany = query.setParameter(\"id\", 1L).singleResult;<\/pre>\n\n\n\n<p>However, entities differ because they are managed objects. And that causes problems when you model them as a data class.<\/p>\n\n\n\n<p>For entities, the persistence provider automatically detects changes and uses lazy loading for relationships. To support this, it expects entity classes to follow the requirements defined in the Jakarta Persistence specification, which we discussed at the beginning of this chapter.&nbsp;<\/p>\n\n\n\n<p>As you can see in the following table, that makes Kotlin\u2019s data classes a bad fit for entity classes.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><\/td><td><strong>Kotlin Data Class<\/strong><\/td><td><strong>Jakarta Persistence Entity<\/strong><\/td><\/tr><tr><td><strong>Class Type<\/strong><\/td><td>Final<\/td><td>Must be open (non-final) so the provider can create proxy subclasses<\/td><\/tr><tr><td><strong>Constructors<\/strong><\/td><td>Primary constructor with required parameters<\/td><td>Must provide a no-argument constructor, used by the persistence provider<\/td><\/tr><tr><td><strong>Mutability<\/strong><\/td><td>Immutable by default (val properties)<\/td><td>Must have mutable, non-final attributes so the provider can perform lazy loading as well as detect and persist changes<\/td><\/tr><tr><td><strong>equals<\/strong><strong> and <\/strong><strong>hashCode<\/strong><\/td><td>Use all properties<\/td><td>Should rely only on type and primary key<\/td><\/tr><tr><td><strong>toString<\/strong><\/td><td>Includes all properties<\/td><td>Should only reference eagerly loaded attributes to avoid additional queries<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>The recommended approach is to use regular open classes to model your entities. They are mutable and proxy-friendly, and they don\u2019t cause any issues with Jakarta Persistence.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Entity\nopen class Person {\n\u00a0\u00a0\u00a0@Id\n\u00a0\u00a0\u00a0@GeneratedValue\n\u00a0\u00a0\u00a0var id: Long? = null\n\n\u00a0\u00a0\u00a0var name: String? = null\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Non-final classes and no-argument constructors<\/h3>\n\n\n\n<p><a href=\"#entity-class-design\">As discussed earlier<\/a>, Jakarta Persistence requires entity classes to be non-final and provide a no-argument constructor.&nbsp;<\/p>\n\n\n\n<p>Kotlin\u2019s classes are final by default and don\u2019t have to offer a no-argument constructor.<\/p>\n\n\n\n<p>But don\u2019t worry, it\u2019s easy to fulfill the requirements without changing your code or implementing your entity classes in a specific way. Just add the no-arg and all-open plugins and add <a href=\"https:\/\/kotlinlang.org\/docs\/reflection.html\" target=\"_blank\" rel=\"noopener\">kotlin-reflect<\/a> to your dependencies. This adds the required constructor and marks annotated classes as open at build time.<\/p>\n\n\n\n<p>Currently, you need the compiler plugins <code>plugin.spring<\/code> and <code>plugin.jpa<\/code>, which will automatically add the no-arg and all-open plugins. When creating a new Spring project using the <em>New Project<\/em> wizard in IntelliJ IDEA or via <a href=\"http:\/\/start.spring.io\" target=\"_blank\" rel=\"noopener\">start.spring.io<\/a>, both plugins are automatically configured for you. And starting with IntelliJ IDEA 2026.1, this will also be the case when you add a Kotlin file to an existing Java project.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">plugins {\n\u00a0\u00a0\u00a0kotlin(\"plugin.spring\") version \"2.2.20\"\n\u00a0\u00a0\u00a0kotlin(\"plugin.jpa\") version \"2.2.20\"\n}\n\nallOpen {\n\u00a0\u00a0\u00a0annotation(\"jakarta.persistence.Entity\")\n\u00a0\u00a0\u00a0annotation(\"jakarta.persistence.MappedSuperclass\")\n\u00a0\u00a0\u00a0annotation(\"jakarta.persistence.Embeddable\")\n}<\/pre>\n\n\n\n<p>When configuring this manually, pay close attention to both parts of this setup. <code>plugin.jpa<\/code> appears to provide the required configuration, but it only configures the no-arg plugin, not the all-open one. This will be improved with the upcoming JPA plugin update. You will then no longer have to add the allOpen section. See: <a href=\"https:\/\/youtrack.jetbrains.com\/issue\/KT-79389\/Add-allopen-plugin-JPA-preset-to-kotlin.plugin.jpa\" target=\"_blank\" rel=\"noopener\">KT-79389<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Mutability<\/h2>\n\n\n\n<p>As a Kotlin developer, you\u2019re used to analyzing whether information is mutable or immutable and modelling your classes accordingly. And when defining your entities, you might want to do the same. But that creates potential issues.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>var<\/code> vs. <code>val<\/code><\/h3>\n\n\n\n<p>In Kotlin, you use val to define an immutable field or property and var for mutable ones. Under the hood, val is compiled in Java to a final field. But as discussed earlier, the Jakarta Persistence specification requires all fields to be non-final.<\/p>\n\n\n\n<p>So, in theory, you can\u2019t use val when modelling your entities. However, if you look at various projects, you can find several entities that use val without causing any bugs.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Entity\nclass Person(name: String) {\n\u00a0\u00a0\u00a0@Id\n\u00a0\u00a0\u00a0@GeneratedValue\n\u00a0\u00a0\u00a0var id: Long? = null\n\n\u00a0\u00a0\u00a0val name: String = name\n}<\/pre>\n\n\n\n<p>That\u2019s because your Jakarta Persistence implementation, the persistence provider, populates entity fields through reflection if you use field-based access, which is usually the case when implementing Jakarta Persistence entities in Kotlin. <code>final<\/code> fields can also be <a href=\"https:\/\/docs.oracle.com\/javase\/specs\/jls\/se25\/html\/jls-17.html#jls-17.5.3\" target=\"_blank\" rel=\"noopener\">modified using reflection<\/a>. As a result, your persistence provider can modify val fields, but this contradicts Kotlin\u2019s immutability guarantees.<\/p>\n\n\n\n<p>So, practically, you can use <code>val<\/code> to model immutable fields of your entity class. Still, it\u2019s not in line with the Jakarta Persistence specification, and your fields are not as immutable as you might expect. To make it even worse, <a href=\"https:\/\/openjdk.org\/jeps\/500\" target=\"_blank\" rel=\"noopener\">JEP 500: Prepare to Make Final Mean Final<\/a> discusses introducing a warning and future changes to restrict final field modifications via reflection. This would prevent you from using <code>val<\/code> on your entity fields and break many persistence layers using Jakarta Persistence and Kotlin.<\/p>\n\n\n\n<p>Be careful when using <code>val<\/code> for your entity fields and make sure everyone on your team understands the implications.<\/p>\n\n\n\n<p>Starting with version 2026.1, IntelliJ IDEA will display a weak warning indicating that a val field will be modified when the persistence provider, such as Hibernate or EclipseLink, instantiates the entity object.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Access types<\/h2>\n\n\n\n<p>The Jakarta Persistence specification defines two access types that determine if your persistence provider uses getter and setter methods to access your entity\u2019s fields or reflection.<\/p>\n\n\n\n<p>You can define the access type explicitly by annotating your entity class with the <code>@Access<\/code> annotation. Or, as almost all development teams do, define it implicitly by where you place your mapping annotations:<\/p>\n\n\n\n<ul>\n<li>Annotations on entity fields \u2192 field access = direct access using reflection<\/li>\n\n\n\n<li>Annotations on getter methods \u2192 property access = access via getter or setter methods<\/li>\n<\/ul>\n\n\n\n<p>Most Kotlin developers put their annotations on properties, which Hibernate treats as field access by default.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Entity\nclass Company {\n\u00a0\u00a0\u00a0@Id\n\u00a0\u00a0\u00a0@GeneratedValue\n\u00a0\u00a0\u00a0var id: Long? = null\n\n\u00a0\u00a0\u00a0var name: String? = null\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0get() {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0println(\"Getter called\")\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return field\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0set(value) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0println(\"Setter called\")\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0field = value\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n}<\/pre>\n\n\n\n<p>In this example, it might look like the getter and setter methods will be called to access the name property. But that\u2019s only the case for your business logic. Because we annotated the fields, the persistence provider will use reflection to access them directly, bypassing the getter and setter methods.<\/p>\n\n\n\n<p>As a general best practice, it\u2019s recommended to stick to field access. It\u2019s easier to read and lets your persistence provider access the entity\u2019s fields directly. You can then provide getter and setter methods that help your business code without affecting your database mapping.<\/p>\n\n\n\n<p>If you want to use property access, you can either annotate your entity class with <code>@Access(AccessType.PROPERTY)<\/code> or annotate the accessors explicitly:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Entity\nclass Company {\n   @get:Id\n   @get:GeneratedValue\n   var id: Long? = null\n\n   var name: String? = null\n       get() {\n           println(\"Getter called\")\n           return field\n       }\n       set(value) {\n           println(\"Setter called\")\n           field = value\n       }\n}<\/pre>\n\n\n\n<p>However, when you do this, you must ensure that all fields are defined as <code>var<\/code>. Kotlin doesn\u2019t provide setter methods for fields defined as <code>val<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Entity\nclass Company {\n   @get:Id\n   @get:GeneratedValue\n   var id: Long? = null\n\n   val name: String? = null \n}<\/pre>\n\n\n\n<p>You can see this when checking Kotlin\u2019s decompiled bytecode of a snippet above.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import jakarta.persistence.Entity;\nimport jakarta.persistence.GeneratedValue;\nimport jakarta.persistence.Id;\nimport kotlin.Metadata;\nimport org.jetbrains.annotations.Nullable;\n\n\n@Entity\n\u2026\npublic final class Company {\n  @Nullable\n  private Long id;\n  @Nullable\n  private final String name;\n\n  @Id\n  @GeneratedValue\n  @Nullable\n  public final Long getId() {\n     return this.id;\n  }\n\n  public final void setId(@Nullable Long var1) {\n     this.id = var1;\n  }\n\n  @Nullable\n  public final String getName() {\n     return this.name;\n  }\n}\n<\/pre>\n\n\n\n<p>Your persistence provider will check that each field has a getter and a setter method. As long as you use var to define your entity fields, property access works with Kotlin.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Null safety and default values<\/h2>\n\n\n\n<p>Null safety and default values are two popular features in Kotlin that don\u2019t exist in that form in Java. It\u2019s no surprise that you have to pay special attention if you want to use them in your Jakarta Persistence entities.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Nullability considerations (including primary key fields)<\/h3>\n\n\n\n<p>Kotlin allows you to define whether a field or property supports <code>null <\/code>values. Unfortunately, reflection can bypass Kotlin&#8217;s null prevention, and as explained earlier, the persistence provider uses reflection to initialize your entity objects.<\/p>\n\n\n\n<p>Even if you define an entity attribute as non-nullable, your persistence provider will set it to <code>null<\/code> if the database contains a <code>null<\/code> value. In your business code, this can lead to runtime exceptions similar to those seen in Java.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Entity\n@Table(name = \"user\")\nclass User(\n   @Id\n   var id: Long? = null\n\n   var name: String\n)\n\nfun testLogic(){\n   \/\/ Suppose the row with id = 1 has name = NULL in the database\n   val user = userRepository.findById(1).get()\n   println(\"Firstname: ${user.name}\") \/\/ null, because Hibernate saves null via reflection\n}<\/pre>\n\n\n\n<p>And unfortunately, solving this problem is not as easy as it seems.<\/p>\n\n\n\n<p>You could argue that all non-nullable entity fields should map to a database column with a not-null constraint. So, your database can\u2019t contain any <code>null<\/code> values.<\/p>\n\n\n\n<p>In general, this is a great approach. But it does not eliminate the risk completely. Constraints can get out of sync between different environments or during migrations. Therefore, using not-null constraints on your database is highly recommended, but it doesn\u2019t provide an unbreakable guarantee that you will never fetch a <code>null<\/code> value from the database.<\/p>\n\n\n\n<p>To make it even worse, all Jakarta Persistence implementations call the no-argument constructor of your entity class to instantiate an object and then use reflection to initialize each field. This means that technically, all your entity fields must be nullable.<\/p>\n\n\n\n<p>What does that mean for your entities? Should you use <code>val<\/code> or <code>var<\/code> to model your fields?<\/p>\n\n\n\n<p>That decision is ultimately up to you. Both of them work, but we recommend sticking to the Kotlin way: Use <code>val<\/code> if an entity field is not supposed to be changed by your business logic, and <code>var<\/code> otherwise. However, due to the issues discussed earlier, it is also essential to ensure that everyone on your team is aware that your Jakarta Persistence implementation may set those fields to <code>null<\/code> if your database lacks a not-null constraint.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">@Id and generated value<\/h3>\n\n\n\n<p>The previous paragraphs already discussed why all entity fields should be nullable. However, many developers consider primary key attributes to be distinct because the database requires a primary key value, and the Jakarta Persistence specification defines it as immutable. Primary keys are mandatory and immutable as soon as you persist the entity object in your database. But let\u2019s quickly discuss why this doesn\u2019t mean that primary key values should be not-nullable, especially if you\u2019re using database-generated primary key values.<\/p>\n\n\n\n<p>When you want to store a new record in your database, you create a new entity object without a primary key and persist it.&nbsp;<\/p>\n\n\n\n<p>Unfortunately, the Jakarta Persistence specification doesn\u2019t clearly define how to implement the persist operation. But it requires generating a primary key value if none is provided. The handling of provided primary key values differs across implementations, but that\u2019s a topic for a different article.&nbsp;<\/p>\n\n\n\n<p>The important thing here is that all persistence providers treat null as a not-provided primary key value. They then use a database sequence or an auto-incremented column to generate a primary key value and set it on the entity object. Due to this mechanism, the primary key value is <code>null <\/code>before the entity gets persisted, and changes during the persist operation.<\/p>\n\n\n\n<p>An interesting side note is that Hibernate handles the primary key value 0 differently when calling the persist or the merge method. The persist method throws an exception because it expects the object to be an already-persisted entity. In contrast, Hibernate\u2019s merge method generates a new primary key value and inserts a new record into the database. That\u2019s why you can model a primary key with the default value 0 and save the new entity object using Spring Data JPA. The default repository implementation recognizes the already set primary key value and calls the merge method instead of the persist method.<\/p>\n\n\n\n<p>Now, returning to the initialization of primary key fields.<\/p>\n\n\n\n<p>When you fetch an entity object from the database, your persistence provider uses the parameterless constructor to instantiate a new object. It then uses reflection to set the primary key value before it returns the entity object to your business code.<\/p>\n\n\n\n<p>All of this clearly shows that the Jakarta Persistence specification expects the primary key field to be mutable, even though the primary key value is not allowed to change after it was assigned. To avoid any portability issues across different Jakarta Persistence implementations, use <code>null <\/code>to represent an undefined primary key value.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Entity\nclass Company {\n   @Id\n   @GeneratedValue\n   var id: Long? = null\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Declaring default values<\/h3>\n\n\n\n<p>Kotlin\u2019s support for default values can simplify your business code and prevent <code>null<\/code> values.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@Entity\nclass Company(\n   @Id @GeneratedValue \n   var id: Long? = null,\n\n   @NotNull\n   var name: String = \"John Doe\",\n\n   @Email\n   var email: String = \"default@email.com\"\n)<\/pre>\n\n\n\n<p>However, please be aware that these default values will have no effect when your persistence provider fetches an entity object from the database.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val companyFromDb = companyRepository.findById(1).get()\nprintln(companyFromDb.email) \/\/ &lt;- If email in DB is empty, it will not set to \"default@email.com\"<\/pre>\n\n\n\n<p>The Jakarta Persistence specification requires a parameterless constructor that the implementations call when fetching an entity object from the database. After that, they use reflection to map all values retrieved from the database to the corresponding entity fields. As a result, the default values defined in your constructor will not be used, and some fields of your entity object might not be set even though you expect your constructor to assign default values. This may not cause any issues in your application, but it is something you and your team should be aware of.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Annotation placement<\/h2>\n\n\n\n<p>In Java, annotations are typically applied directly to the field, method, or class you annotate. In Kotlin, by contrast, annotations can target different elements, such as constructor parameters, properties, or fields.<\/p>\n\n\n\n<p>Before Kotlin 2.2, this often caused problems because annotations applied to properties were applied only to the constructor parameter by default. This often caused problems for Jakarta Persistence and validation frameworks. Annotations like <code>@NotNull<\/code>, <code>@Email<\/code>, or even <code>@Id<\/code> didn&#8217;t end up where the framework expected them to be. This led to missed validations or mapping issues.<\/p>\n\n\n\n<p>The good news is that this has been improved in Kotlin 2.2. With the new compiler option, which IntelliJ IDEA will suggest enabling, annotations will be applied to the constructor parameter and the property or field by default. So, your code now works as expected without requiring any changes.<\/p>\n\n\n\n<p>To learn more, check out the <a href=\"https:\/\/blog.jetbrains.com\/idea\/2025\/09\/improved-annotation-handling-in-kotlin-2-2-less-boilerplate-fewer-surprises\/\">blog post<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">IntelliJ IDEA to the rescue!<\/h2>\n\n\n\n<p>In the upcoming 2026.1 release, IntelliJ IDEA will provide inspections and quick-fixes to address many of the problems mentioned in this article, thereby improving your overall experience. Be sure to update when the release becomes available. Here are a few examples of what you\u2019ll get with the new release:<\/p>\n\n\n\n<ul>\n<li>Highlighting missing no-arg constructors or final entity classes and suggestions to enable the correct Kotlin plugins.<\/li>\n\n\n\n<li>Autoconfiguration of all essential setup when configuring Kotlin in the project.<\/li>\n\n\n\n<li>Detection and quick fix for data classes and val fields on JPA-managed properties.<\/li>\n<\/ul>\n\n\n\n<p>And other JPA-related updates!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>About the author<\/strong><\/h2>\n\n\n    <div class=\"about-author \">\n        <div class=\"about-author__box\">\n            <div class=\"row\">\n                                                            <div class=\"about-author__box-img\">\n                            <img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/12\/Thorben-Janssen-400x400-1.jpg\" alt=\"\" loading=\"lazy\">\n                        <\/div>\n                                        <div class=\"about-author__box-text\">\n                                                    <h4>Thorben Janssen<\/h4>\n                                                <div class=\"p-client_container\">\n<div class=\"p-ia4_client_container\">\n<div class=\"p-ia4_client p-ia4_client--sidebar-wide p-ia4_client--with-split-view-feature\">\n<div class=\"p-client_workspace_wrapper\" role=\"tabpanel\" aria-label=\"JetBrains\">\n<div class=\"p-client_workspace\">\n<div class=\"p-client_workspace__layout\">\n<div class=\"p-client_workspace__tabpanel\" role=\"tabpanel\" aria-label=\"DMs\">\n<div class=\"enabled-managed-focus-container\" role=\"none\">\n<div>\n<div class=\"p-view_contents p-view_contents--primary\" tabindex=\"-1\" role=\"dialog\" aria-label=\"Conversation with Teodor Irkhin\">\n<div class=\"tabbed_channel__Abx5r\">\n<div class=\"tabbed_channel__Abx5r\">\n<div class=\"channel_tab_panel__zJ5Bt c-tabs__tab_panel c-tabs__tab_panel--active c-tabs__tab_panel--full_height\" role=\"none\" data-qa=\"tabs_content_container\">\n<div class=\"p-file_drag_drop__container\">\n<div class=\"p-workspace__primary_view_body\">\n<div class=\"p-message_pane p-message_pane--classic-nav p-message_pane--scrollbar-float-adjustment p-message_pane--with-bookmarks-bar\" data-qa=\"message_pane\">\n<div>\n<div id=\"message-list\" role=\"presentation\">\n<div id=\"message-list\" class=\"c-virtual_list c-virtual_list--scrollbar c-message_list c-scrollbar c-scrollbar--fade\" role=\"presentation\">\n<div class=\"c-scrollbar__hider\" role=\"presentation\" data-qa=\"slack_kit_scrollbar\">\n<div class=\"c-scrollbar__child\" role=\"presentation\">\n<div class=\"c-virtual_list__scroll_container\" role=\"presentation\" data-qa=\"slack_kit_list\">\n<div id=\"message-list_1766072525.327379\" class=\"c-virtual_list__item\" tabindex=\"0\" role=\"listitem\" aria-setsize=\"-1\" data-qa=\"virtual-list-item\" data-item-key=\"1766072525.327379\">\n<div class=\"c-message_kit__background c-message_kit__background--hovered p-message_pane_message__message c-message_kit__message\" role=\"presentation\" data-qa=\"message_container\" data-qa-unprocessed=\"false\" data-qa-placeholder=\"false\" data-msg-ts=\"1766072525.327379\" data-msg-channel-id=\"D072Y30UX54\">\n<div class=\"c-message_kit__hover c-message_kit__hover--hovered\" role=\"document\" aria-roledescription=\"message\" data-qa-hover=\"true\">\n<div class=\"c-message_kit__actions c-message_kit__actions--above\">\n<div class=\"c-message_kit__gutter\">\n<div class=\"c-message_kit__gutter__right\" role=\"presentation\" data-qa=\"message_content\">\n<div class=\"c-message_kit__blocks c-message_kit__blocks--rich_text\">\n<div class=\"c-message__message_blocks c-message__message_blocks--rich_text\" data-qa=\"message-text\">\n<div class=\"p-block_kit_renderer\" data-qa=\"block-kit-renderer\">\n<div class=\"p-block_kit_renderer__block_wrapper p-block_kit_renderer__block_wrapper--first\">\n<div class=\"p-rich_text_block\" dir=\"auto\">\n<p>Thorben Janssen is a consultant and trainer who helps teams build better persistence layers with JPA and Hibernate. An international speaker with more than 20 years of experience in JPA and Hibernate, Thorben is the author of the best-selling book\u00a0<i data-stringify-type=\"italic\">Hibernate Tips: More than 70 solutions to common Hibernate problems<\/i>.<\/p>\n<p>He also writes on\u00a0thorben-janssen.com\u00a0about various persistence topics, and to help developers improve their skills, he founded the Persistence Hub.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n                    <\/div>\n                            <\/div>\n        <\/div>\n    <\/div>\n","protected":false},"author":1423,"featured_media":669975,"comment_status":"closed","ping_status":"closed","template":"","categories":[4113,2347],"tags":[3007,21],"cross-post-tag":[6355],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/idea\/669971"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/idea"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/types\/idea"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/users\/1423"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/comments?post=669971"}],"version-history":[{"count":11,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/idea\/669971\/revisions"}],"predecessor-version":[{"id":699979,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/idea\/669971\/revisions\/699979"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/media\/669975"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/media?parent=669971"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/categories?post=669971"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/tags?post=669971"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/cross-post-tag?post=669971"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}