@Embeddable class with val in Kotlin

Viewed 2802

I'm working on Gradle-Kotlin-Hibernate project. I would like to keep some of my classes immutable which in Kotlin is especially easy. This works just fine for @Entity

@Entity(name = "SOMETHING")
class MeetingKeychain(
        val immutableProp: String
) {

// ID and others

}

thanks to the usage of kotlin-jpa plugin. But the same plugin does not work for @Embeddable.

@Embeddable
class MeetingKeychain(
        val immutableProp: String
) {

// ID and others

}

The following exception is thrown:

Caused by: org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.component.PojoComponentTuplizer]
    at org.hibernate.tuple.component.ComponentTuplizerFactory.constructTuplizer(ComponentTuplizerFactory.java:98)
    at org.hibernate.tuple.component.ComponentTuplizerFactory.constructDefaultTuplizer(ComponentTuplizerFactory.java:119)
    at org.hibernate.tuple.component.ComponentMetamodel.<init>(ComponentMetamodel.java:64)
    at org.hibernate.mapping.Component.getType(Component.java:169)
    at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:398)
    at org.hibernate.mapping.Property.isValid(Property.java:225)
    at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:595)
    at org.hibernate.mapping.RootClass.validate(RootClass.java:265)
    at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:329)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:443)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879)
    ... 46 more
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.hibernate.tuple.component.ComponentTuplizerFactory.constructTuplizer(ComponentTuplizerFactory.java:95)
    ... 56 more
Caused by: org.hibernate.PropertyNotFoundException: Could not locate setter method for property [com.scherule.calendaring.domain.entities.ParticipantId#id]
    at org.hibernate.internal.util.ReflectHelper.findSetterMethod(ReflectHelper.java:540)
    at org.hibernate.property.access.internal.PropertyAccessBasicImpl.<init>(PropertyAccessBasicImpl.java:44)
    at org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl.buildPropertyAccess(PropertyAccessStrategyBasicImpl.java:27)
    at org.hibernate.mapping.Property.getGetter(Property.java:299)
    at org.hibernate.tuple.component.PojoComponentTuplizer.buildGetter(PojoComponentTuplizer.java:143)
    at org.hibernate.tuple.component.AbstractComponentTuplizer.<init>(AbstractComponentTuplizer.java:46)
    at org.hibernate.tuple.component.PojoComponentTuplizer.<init>(PojoComponentTuplizer.java:42)
    ... 61 more

My question is that is there any way around this? I would like my @Embeddable class to be immutable.

2 Answers

Actually this proves to be doable and not related to Kotlin. The only way it is related is that you need to generate default constructor which what that kotlin-jpa plugin is used for. You need to use

@Access(AccessType.FIELD)

while

@Access(AccessType.PROPERTY)

is the default. Then it looks for setters rather than using field injections.

I guess this is Hibernate restriction. It clearly says Could not locate setter method for property. So we should provide setters for him. And the only way is to change val to var. The no-arg plugin can't help here even if we add @Embeddable annotation to the plugin configuration: noArg { invokeInitializers = true annotation("javax.persistence.Embeddable") }

Related