hibernatecachingeager-loading

Hibernate 6.2+ with 2nd level cache ignores eager loading


I have project with hibernate 6.2.13.Final and jcache/ehcache for 2nd level cache:

<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.2.13.Final</version>
</dependency>

<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-envers</artifactId>
    <version>6.2.13.Final</version>
</dependency>

<!-- API for 2nd Level Cache -->
<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-jcache</artifactId>
    <version>6.2.13.Final</version>
</dependency>
<!-- Implementation for 2nd Level Cache -->
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <classifier>jakarta</classifier>
    <version>3.10.8</version>
</dependency>

And I have entities with @ManyToOne(fetch = FetchType.EAGER) and caching. I know that Eager Loading is an anti-pattern, but the software is complex and it's not possible to change that at the moment.

@Entity
@Cacheable
@Audited
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class FooType {

    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid2")
    @Size(max = 36)
    @Column(length = 36)
    @UserInterface(showInForm = false, showInList = false)
    @Comment("Primary key")
    private String id;

    @Version
    @NotNull
    @UserInterface(showInForm = false, showInList = false)
    @Comment("Version column for optimistic locking")
    private int version;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }
}
@Entity
@Cacheable
@Audited
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Foo {

    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid2")
    @Size(max = 36)
    @Column(length = 36)
    @UserInterface(showInForm = false, showInList = false)
    @Comment("Primary key")
    private String id;

    @Version
    @NotNull
    @UserInterface(showInForm = false, showInList = false)
    @Comment("Version column for optimistic locking")
    private int version;
    
    @ManyToOne(fetch = FetchType.EAGER)
    private FooType;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }
    
    public FooType getFooType() {
        return fooType;
    }
    
    public void setFooType(FooType fooType) {
        this.fooType = fooType;
    }
}

When I query an entry, everything is fine.

entityManager.find(Foo.class, id);

However, when I query multiple entries, the FooType is lazy loaded in the results of Foo and is a hibernate proxy object.

final TypedQuery<Foo> fooQuery = entityManager.createQuery("FROM Foo f", Foo.class);
final List<Foo> fooList = fooQuery.getResultList();

Hibernate 6.2 and disabled 2nd level cache

When I deactivate the 2nd level cache, the result is eagerly loaded as expected.

<property name="hibernate.cache.use_second_level_cache" value="false" />

Hibernate 6.1 and enabled 2nd level cache

When I change the Hibernate version to 6.1.7.Final and the 2nd level cache is enabled, the result loads eagerly as expected.

Hibernate 6.4.0.CR1

Same issue like 6.2

I'm not sure if this is a bug or an expected change in caching? However, I did not find anything about this in the hibernate 6.2 migration guide. How can I handle this issue, I need the result eager loaded with 2nd level cache enabled.


Solution

  • I got the answer in the Hibernate forum:

    Just because you see a proxy object being set for the association doesn’t mean it is lazy loaded. Sometimes Hibernate ORM needs to work with a proxy for other reasons, but will ensure the object becomes initialized as part of a load operation.

    Thread in Hibernate forum with hints for Jackson.