javahibernatejpaspring-data-jpajava-record

Why is JPA still working with record class as entity?


Based on all the documentation I have read the following code should not work;Java records are immuatble and would not work with JPA. However the following code works. I can succesfully retrieve data from the database. Is it that JPA can support Java records in read-only scenarios ?

@Embeddable
public record TaxableLimitAmountId(@Column(name = "TAX_YR") String taxYr,
        @Column(name = "TAXL_LMT_UID") int taxableLimitUniqueId,
        @Column(name = "EFF_STDT", columnDefinition = "DATE") Date effectiveStartDate)
        implements Serializable {

    private static final long serialVersionUID = 1L;

}

@Entity
@Table(name = "TableName", schema = "schema")
public record TaxableLimitAmountEntity(
        @EmbeddedId TaxableLimitAmountId taxableLimitAmountId,
        @Column(name = "TAXL_LMT_AMT", columnDefinition = "DECIMAL") BigDecimal taxableLimitAmount,
        @Column(name = "EFF_ENDT", columnDefinition = "DATE") Date effectiveEndDate,
        @Column(name = "INSRT_TS") Timestamp insertTimeStamp,
        @Column(name = "LU_TS") Timestamp updateTimeStamp) {

}

public record FicaMaxData(int taxYr, BigDecimal taxableLimitAmount) {

}

@Repository
public interface TaxableLimitAmountRepo
        extends JpaRepository<TaxableLimitAmountEntity, Integer> {

    @Query("SELECT NEW gov.ssa.irsrptg.repo.FicaMaxData(CAST(tla.taxableLimitAmountId.taxYr as INTEGER),tla.taxableLimitAmount) FROM TaxableLimitAmountEntity tla "
            + "WHERE tla.taxableLimitAmountId.taxableLimitUniqueId = 1")
    List<FicaMaxData> getFicaMaxAllYears();

    @Query("SELECT tla.taxableLimitAmount FROM TaxableLimitAmountEntity tla "
            + "WHERE tla.taxableLimitAmountId.taxableLimitUniqueId = 1 and tla.taxableLimitAmountId.taxYr = :taxYr")
    BigDecimal getFicaMaxSpecificYear(@Param("taxYr") String taxYr);
}


Hibernate gives the following warning but it doesn't throw any errors

o.h.m.i.EntityInstantiatorPojoStandard   : HHH000182: No default (no-argument) constructor for class: gov.ssa.irsrptg.repo.TaxableLimitAmountEntity (class must be instantiated by Interceptor)

I'm using Java 17, Spring Boot 3.2.6, Spring Data Jpa 3.2.6, and Hibernate 6.4.8.Final


Solution

  • The statement "JPA does not work with records for entities" is a short form for: "There are things that JPA requires from an entity that aren't possible with records".

    The hard limit here is the immutability, which prevents JPA to work as intended by tracking changes to the entity.

    Of course this doesn't mean that a JPA implementation has to immediately blow up in your face if it encounters a record. Some things might work just fine, because those features aren't necessary for the use case. For example you don't need a default constructor, when you use a constructor expression. Or your JPA implementation might work around some limitations of JPA, as Hibernate seems to do for the lack of a default constructor, a hinted at by

    No default (no-argument) constructor for class: gov.ssa.irsrptg.repo.TaxableLimitAmountEntity (class must be instantiated by Interceptor)

    So yes: some stuff might work.

    So far I haven't seen an official statement by JPA or Hibernate what kind of stuff works and I would be reluctant to rely on features that just happen to work, since they might stop working with the next release.