Delegated property vs JPA field annotation


#1

I’m experimenting with property delegates + JPA.

I implemented a very simple property delegate:

class MutableNonSynchronizedLazy<T>(private val init: () -> T) : ReadWriteProperty<Any?, T> {

    private var value: T? = null

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        if (value == null) {
            value = init()
        }
        return value!!
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}

fun <T> mutableNonSynchronizedLazy(init: () -> T) = MutableNonSynchronizedLazy(init)

My problem is that I don’t find a way to add an annotation to a field delegated by MutableNonSynchronizedLazy:

@Entity
class SomeEntity {
    @field:ElementCollection(fetch = FetchType.EAGER)
    var map: Map<String, String?> by mutableNonSynchronizedLazy { HashMap<String, String?>() }
}

I get the error:

@field:’ annotations could be applied only to properties with backing fields

Maybe is there a way to use the “original” backing field of the property in the delegate’s getValue() and setValue() methods?

(I know that it is possible to define JPA annotations on getters instead of fields - but I prefer the latter.)


#2

By “original” backing field I expect you mean the backing field used by the delegate class? It’s not possible.

var value by mutableNonSynchronizedLazy { ... }

basically get’s translated to the following code

invisible val delegateObject = mutableNonSynchronizedLazy{...}
var value
    get() = delegateObject.getValue()
    set(v) { delegateObject.setValue(v) }

So what you are asking for is that the annotation you apply to a value gets somehow applied to a value in a different class, which might be from a library or some external source. Also what would happen if you apply a different annotation somewhere else? Do all variables using this delegate type share the annotations?
I think you see why you can’t apply annotations to the field of delegated variables.


#3

Thanks for the quick reply, it sounds logical :slight_smile:
I think I will use simply this version:

@Entity
class SomeEntity {
    @ElementCollection(fetch = FetchType.EAGER)
    private var map: Map<String, String>? = null

    val mapAccessor: Map<String, String>
        get() {
            if (map == null) {
                map = HashMap()
            }
            return map ?: throw IllegalStateException()
        }
}