简体   繁体   中英

Hibernate not caching my OneToOne relationship on the inverse side

I have code like:

@Entity
@Table(name = "A")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class A
{
  @OneToOne(cascade={CascadeType.ALL}, fetch=FetchType.EAGER, mappedBy="a")
  public B getB() {};
}

@Entity
@Table(name = "B")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class B
{
  @OneToOne(cascade={}, fetch=FetchType.LAZY)
  @JoinColumn(name="A_ID")
  public A getA() {};
}

each time when A is loaded there is query for B . Why is A.getB() not cached after A is loaded and is it possible to cache it?

Workaround that work for me is create additional method with @OneToMany

@OneToMany(cascade={}, fetch=FetchType.EAGER, mappedBy="a")
public Set<B> getBSet() {};

@Transient
public B getB() { return b.iterator().next(); }

I'm not very happy with this solutions, but it works and I can't find other way.

Try putting @Cache annotation on getB() getter as well. My observations are that if you cache the object, it's associations may not be considered cached.

I feel that the original answer does not cover entirly why this is happening.

Why OneToOne is not cached ?

It is not cached because class A is not the owner of the relationship and does not contain the @JoinColumn inside its table. Therefore there is no way for class A to tell what is the ID of class B . This is why when trying to retrieve class A it needs to send a query for class B to figure out what the ID of class B is, but when it sends the query the class B is already loaded so there is no need for it to actualy retrieve it from the cache.

When OneToOne will be cached ?

Now if you navigate the opposite way from class B to class A then you will hit the cache straight away :)

Why is @OneToMany(cascade={}, fetch=FetchType.EAGER, mappedBy="a") working ?

In hibernate collections are cached in their dedicated region known as collection cache. Hibernate caches the primary keys of the entities that make up the collection. Not the entities themselves; ie there is no Set stored somewhere in the second level cache.

Once the primary key for is retrieved from the collection cache region it falls back to the regular entity cache to retrieve the actual object. Therefore the @OneToMany hack works for you.

It may be a little more work, but you could try making the fetchType Lazy, and do the fetching of B explicitly. That way you could check whether the instance of B has already been loaded or not?

On a side note, have you seen this post? I think the problem is similar:

https://forum.hibernate.org/viewtopic.php?p=2378461

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM