OneToOne level 2 caching limitation


When OneToOne associations are marked with @Cache, the caching mechanism is not able to fully support them.

When two entities A and B have a OneToOne, bi-directional association where A has an @JoinColumn reference to the @Id of B and where both A and B have been cached and the association navigated from both directions (A.getB() and B.getA() methods were called), calling either A.getB() or B.getA() results in database queries to initialize the association (in the new session) from B to A because the level 2 cache relies on primary key lookup and has no support for lookup of an entity whose foreign key references a given primary key value.




Gail Badner
March 28, 2018, 7:02 AM

Caching by natural ID is already supported (see

Unfortunately there are some restrictions on natural IDs, so it is an incomplete solution for the general problem:

  • an entity can only have 1 natural ID (which can include 1 or more attributes);

  • the natural ID must be defined at the root of an entity hierarchy.

A general solution would need to support:

  • caching entities by multiple foreign keys (one for each one-to-one association);

  • allow caching entities by foreign keys that are defined in the root of the entity hierarchy, as well as in subclasses.

Something to keep in mind is that caching by IDs performs well because an entity's ID is immutable. One-to-one associations based on a foreign key is mutable by default, and there will be a cost in performance to deal with associations that change. There should be a way to avoid checking for changes when the application "knows" there are no changes, similar to what is described for natural IDs. See SimpleNaturalIdLoadAccess#setSynchronizationEnabled.

I have tentatively set the fix version for to 6.0.Beta1. We will need to investigate further if this is really feasible.

Gail Badner
March 29, 2018, 4:53 AM

[5:44 PM] Gail Badner: @SteveEbersole , btw, I'm looking at NaturalIdXrefDelegate to see if I can extract/extend some functionality for FK used by logical one-to-one associations
[5:45 PM] Steve Ebersole: its a lot if the association is mutable
[5:47 PM] Gail Badner: yeah; one thing I was thinking about was allowing the application to set setSynchronizationEnabled( false ) somehow when calling Query#iterate or Query#scroll
[5:49 PM] Gail Badner: also, if the association has updatable="false", it is effectively immutable
[5:49 PM] Gail Badner: ^^^ @SteveEbersole
[5:51 PM] Steve Ebersole: yes, i understand that.. but i am talking about the (default) case where updatable="true"
[5:51 PM] Steve Ebersole: I'm really not a fan of this Query#iterate or Query#scroll thing
[5:53 PM] Steve Ebersole: one option is to support this just for immutable associations
[5:53 PM] Steve Ebersole: for the first cut
[5:53 PM] Gail Badner: an alternative is to have Session#setOneToOneFKSynchronizationEnabled
[5:54 PM] Gail Badner: and be able to set it before/after calling Query#getResultList
[5:55 PM] Steve Ebersole: why doesn't this just fall under normal flush-mode handling?
[5:55 PM] Gail Badner: it looks like a lot of the logic for mutable natural IDs applies
[5:55 PM] Steve Ebersole: i really don't like any of these options
[5:55 PM] Steve Ebersole: yes, but load-by-id is always a single load
[5:56 PM] Steve Ebersole: yes, i agree.... most of NaturalIdXrefDelegate can be reused here
[5:56 PM] Steve Ebersole: probably even reused if we make it sufficiently abstract
[5:57 PM] Gail Badner: the problem is when the association on the mappedBy side is initialized; that can happen while an application iterates the List returned by Query#getResultList
[5:58 PM] Gail Badner: yeah, I think I can extract an superclass
[5:59 PM] Gail Badner: part of is to cache entities by the one-to-one FK; I'm not crazy about doing that
[5:59 PM] Gail Badner: but caching in the Session help a lot
[6:01 PM] Steve Ebersole: I'm quite against shared caching
[6:01 PM] Steve Ebersole: here
[6:01 PM] Steve Ebersole: so we agree there
[6:02 PM] Gail Badner: I don't think it's worth it for mutable associations for sure
[6:02 PM] Steve Ebersole: for immutable we really dont even need to involve caching
[6:03 PM] Gail Badner: 2lc?
[6:03 PM] Steve Ebersole: a simple SF-scope ConcurrentHashMap is more than enough
[6:03 PM] Gail Badner: oh, interesting
[6:03 PM] Steve Ebersole: its read-only, no need for locking
[6:03 PM] Steve Ebersole: simplest thing that works
[6:04 PM] Steve Ebersole: and doesn't change SPIs
[6:04 PM] Gail Badner: is that done with immutable natural IDs?
[6:04 PM] Steve Ebersole: we do cache those in the second level cache
[6:04 PM] Steve Ebersole: but thats different
[6:05 PM] Steve Ebersole: tbh, I'm not even sure the ConcurrentHashMap thing is even needed
[6:05 PM] Steve Ebersole: just saying, if we do end up "shared caching" them, no need to get all full blown and use l2c
[6:06 PM] Steve Ebersole: i'd not even bother for mutable fks
[6:06 PM] Gail Badner: really all we need to cache is the FK -> PK; then load from cache by PK
[6:06 PM] Gail Badner: oh, that was kinda ambiguous
[6:07 PM] Gail Badner: really all we need to cache is the FK -> PK (in SF); then load from 2lc cache by PK
[6:07 PM] Steve Ebersole: i understand what you need to do
[6:07 PM] Steve Ebersole: i'm questioning the usefulness
[6:08 PM] Gail Badner: ok, I'm making sure I understand what I need to do
[6:08 PM] Steve Ebersole: cache in Session... meh
[6:08 PM] Steve Ebersole: that has a huge potential to increase the size of the session with potentially no benefit
[6:08 PM] Gail Badner: how would it be read-only in SF?
[6:08 PM] Steve Ebersole: ?
[6:09 PM] Gail Badner: you said something above...
[6:09 PM] Steve Ebersole: how would immutable fks be read-only?
[6:09 PM] Steve Ebersole: that seems self-evident
[6:09 PM] Gail Badner: "its read-only, no need for locking"
[6:09 PM] Steve Ebersole: yep, we were talking about immutable fks
[6:10 PM] Steve Ebersole: 1->2 is always 1->2
[6:10 PM] Gail Badner: oh, ok; I thought that was about the Map in SF
[6:10 PM] Steve Ebersole: thats the meaning of immutable
[6:10 PM] Steve Ebersole: that is what I mmeant
[6:10 PM] Gail Badner: wimi, entries have to be added to the Map
[6:10 PM] Gail Badner: so the Map isn't read-oly
[6:11 PM] Steve Ebersole: of course it is
[6:11 PM] Steve Ebersole: you never over-write
[6:11 PM] Steve Ebersole: just like @Immutable does not stop insertions
[6:11 PM] Gail Badner: well, it's possible that it could be added by 2 sessions
[6:11 PM] Steve Ebersole: and thats bad how?
[6:11 PM] Gail Badner: they'd be adding the same thing
[6:11 PM] Steve Ebersole: exactly
[6:12 PM] Steve Ebersole: so locking would accomplish what?
[6:12 PM] Gail Badner: ok, I see
[6:12 PM] Steve Ebersole: just let them each suceed
[6:12 PM] Steve Ebersole: nothing to see, move along
[6:13 PM] Steve Ebersole: but again... this has the potential to grow (and grow)
[6:14 PM] Gail Badner: well I suppose it could be some other kind of map that gets rid of last used entries
[6:14 PM] Steve Ebersole: not sure how we would measure the benefit of this cache versus not caching
[6:14 PM] Steve Ebersole: sure which also has ovverhead
[6:15 PM] Gail Badner: the DB can end up getting hit a lot more
[6:15 PM] Gail Badner: w/o caching FK -> PK
[6:15 PM] Steve Ebersole: sure and generally there is ann index for resolution by that FK
[6:15 PM] Steve Ebersole: dont be so sure its always faster to cache these
[6:16 PM] Steve Ebersole: its like anything we cache... there is an overhead involved in the cachign itself
[6:16 PM] Gail Badner: is this something that should be enabled?
[6:16 PM] Steve Ebersole: but in general, i agree that there are a lot of similarities with natural-id x-refing
[6:17 PM] Steve Ebersole: good question
[6:17 PM] Steve Ebersole: not sure
[6:17 PM] Steve Ebersole: probably

Galder Zamarreno
July 12, 2018, 1:47 PM

This doesn't seem related to Infinispan cache provider itself, so removing that component.




Stephen Fikes

Fix versions





Suitable for new contributors


Requires Release Note


Pull Request




Affects versions