blaze-persistence icon indicating copy to clipboard operation
blaze-persistence copied to clipboard

EntityFieldAttributeAccessor.getValue should not unproxy the argument when the underlying field is a primary key

Open david-kubecka opened this issue 2 years ago • 10 comments

Unproxying on a PK leads to an unnecessary population of the proxied entity.

The offending code is here. Other methods in that class should probably be changed as well.

david-kubecka avatar Dec 19 '22 20:12 david-kubecka

The offending code is pointing to the EntityFieldAttributeAccessor. I am pretty sure that field access on a proxy is not valid.

Probably you should change the mapping to method based access to benefit from the id access without initialisation.

jwgmeligmeyling avatar Dec 19 '22 20:12 jwgmeligmeyling

@jwgmeligmeyling I'm confused about your response :-) Do you claim that my request is not feasible in principle or that the code should (and could) be changed to use a different kind of reflection access (method instead of attribute)?

david-kubecka avatar Dec 20 '22 16:12 david-kubecka

Yes, I don't think it can't be done. I think you should move the JPA mapping annotations from private Long id to private Long getId() if you want to benefit from this optimisation.

jwgmeligmeyling avatar Dec 21 '22 08:12 jwgmeligmeyling

I guess we could do something special here for the id like e.g. call PersistenceUnitUtil.getIdentifier which would allow accessing the identifier on a proxy without initializing. The workaround is to place the annotations on the getter instead.

beikov avatar Dec 21 '22 11:12 beikov

Which annotations are you exactly talking about? Because I already have the BP @IdMapping on the getter and the JPA annotations should IMO be on the property. At least that's what everybody does and trying to put @Id on the getter leads to strange (Hibernate) errors.

david-kubecka avatar Dec 22 '22 08:12 david-kubecka

The annotations on the entity. You should put all annotations on the getters instead of the fields.

beikov avatar Dec 22 '22 11:12 beikov

I really cannot make your suggestion work. I'm using Kotlin and currently I have this in my BaseEntity (from which all other non-embeddable entities inherit):

    @Id
    @GeneratedValue
    var id: Long? = null

So putting the annotations to the getter in Kotlin means changing that to:

    @get:Id
    @get:GeneratedValue
    var id: Long? = null

Once I do that I run into really weird Hibernate errors, e.g.Could not locate setter method for property [com.vacuumlabs.robo.backend.entities.Money#amount]. Well, Money#amount has indeed just a getter but how is that related to moving the @Id annotation to the getter? Okay, so I add setter (also to a couple of other places) and I get another error Unable to create unique key constraint (client_id, iban) on table account: database column 'client_id' not found. But of course the client_id column is there and if I switch the @Id back to the field then my app starts and runs ok.

Do you have any idea what might be causing this (perhaps I will post to SO)?

Also, why annotation placement should make a difference to my original issue in the first place?

david-kubecka avatar Jan 17 '23 12:01 david-kubecka

You need both the getter and the setter. The annotations only need to be the getter. Perhaps annotating the method as opposed to the field leads to different column names being generated/expected by Hibernate. You can explicitly specify the column name using @get:Column(name = "client_id").

jwgmeligmeyling avatar Jan 17 '23 14:01 jwgmeligmeyling

I forgot to stress that apart from the above-mentioned initial changes (putting Id and GeneratedValue on getters, while still keeping the setters as is) I did not do anything else and still started getting those weird errors.

leads to different column names being generated/expected by Hibernate

I don't think so as the message Unable to create unique key constraint (client_id, iban) on table account: database column 'client_id' not found states it quite clearly. Hibernate correctly expects an account.client_id column but for some reason can't find it...

david-kubecka avatar Jan 17 '23 14:01 david-kubecka

If you change to property access, embeddables will also use property access, unless you annotate them with @Access(FIELD). If you have an association or basic value that defines a column through some @Column annotation, and you refer to that column name in e.g. a @JoinColumn or something like that, then you could run into such a situation when you forget to use @get: on some other annotations.

Essentially, every JPA annotation should be prefixed with @get: to make this work.

beikov avatar Jan 23 '23 18:01 beikov