1

I am learning how to fully control Hibernate behaviour and have hit a roadblock: if I annotate my @ManyToOne entity properties one way they aren't lazy "enough" but if I annotate them differently then entity graphs don't have a desired effect. Specifically:

Common to both Hibernate 5.4 is used with bytecode enhancement. Entity class is annotated as follows:

@javax.persistence.Entity
@Table(name = "test")
@org.hibernate.annotations.DynamicInsert(false)
@org.hibernate.annotations.DynamicUpdate(false)
@org.hibernate.annotations.SelectBeforeUpdate(false)
@org.hibernate.annotations.Proxy(lazy = false)
@org.hibernate.annotations.OptimisticLocking(type = org.hibernate.annotations.OptimisticLockType.VERSION)
@org.hibernate.annotations.Polymorphism(type = org.hibernate.annotations.PolymorphismType.IMPLICIT)
@javax.persistence.Access(javax.persistence.AccessType.FIELD)
public class TestEntity {

All @ManyToOne properties look like:

    @ManyToOne // or @ManyToOne(fetch = FetchType.LAZY) - no difference
    @JoinColumn(name = "test_link_id", nullable = true)
    @OptimisticLock(excluded = false)
    @NotFound(action = NotFoundAction.EXCEPTION)
    private TestEntity testLink;

Way #1:

HAVE the following annotation:

    @LazyToOne(LazyToOneOption.NO_PROXY)

but do NOT have the following together with it (if I do it becomes "Way 2"):

    @Fetch(FetchMode.JOIN) // Other fetch modes are OK

GOOD: If I do not specify an entity graph at all Hibernate does not load ANYTHING about these ManyToOne properties (i.e. SQL query does not select them) - not even ids that are in the same table/rows as other basic properties of the entity/ies I am getting. That is an overkill. Attempt to access will cause an extra query to get the ids first, then individual queries to lazily get the rest...

BAD: If I do specify the entity graph to load these ManyToOne properties, the SQL query that Hibernate executes does get them but Hibernate reports the properties as uninitialized. On access Hibernate lazily executes a second query which just gets the ids of those ONLY (again?) ... It seems to have the other properties already ... so why loading the ids again?

Way #2:

Either do NOT have the following annotation:

    @LazyToOne(LazyToOneOption.NO_PROXY)

OR DO HAVE the following together with it (otherwise it becomes "Way 1"):

    @Fetch(FetchMode.JOIN) // other fetch modes are NOT OK

BAD: If I don't specify an entity graph at all Hibernate issues TWO queries before I get the chance to do anything. The first query just gets the ids. Then the second query then gets the properties of the ManyToOne linked entities even though I didn't ask/want that.

GOOD: If I do specify the entity graph it behaves as expected - the single executed SQL query joins in all the data needed and no subsequent queries are issued on access.

Test code

    final CriteriaBuilder = session.getCriteriaBuilder()
    final CriteriaQuery<TestEntity> query = builder().createQuery(TestEntity.class);
    final Root<TestEntity> root = query.from(TestEntity.class);
    final Path<Long> idPath = root.get("id");
    query.where(builder.equal(idPath, someChosenId));
    final TypedQuery<TestEntity> tq = session().createQuery(query);
    tq.setHint("javax.persistence.fetchgraph", entityGraph); // as/only when wanted
    final TestEntity testEntity = tq.getSingleResult();
    System.out.println(Hibernate.isPropertyInitialized(testEntity, "testLink"));
    // access testEntity's testLink property, watch Hibernate-generated/executed SQL

Question

How can I get the "good" both without and with entity graphs specified? I.e. no eager/extra loading without the graph (not even the more complex query) and still have the ability to say that I want more/eagerly using the graph without IT then causing additional queries for id? Note: I tried using .fetch(...) methods instead of graphs with exactly the same effect.

Learner
  • 1,215
  • 1
  • 11
  • 26
  • (Almost) the entire test code can be found at https://pastebin.com/1iFiPTL4 - start it with testLoading() method. You do have to supply your own Hibernate Session. – Learner Mar 05 '21 at 04:02
  • This may have something to do with https://hibernate.atlassian.net/browse/HHH-13134 mentioned in the comment here: https://vladmihalcea.com/the-best-way-to-lazy-load-entity-attributes-using-jpa-and-hibernate/ – Learner Mar 05 '21 at 18:51
  • Also related to this: https://discourse.hibernate.org/t/how-to-make-lazy-loading-truly-lazy-v5-4/1994/15 – Learner Mar 05 '21 at 18:58
  • Let's continue with the discussion on discourse: https://discourse.hibernate.org/t/how-to-make-lazy-loading-truly-lazy-v5-4/1994/21 – Christian Beikov Mar 08 '21 at 14:00
  • @ChristianBeikov: I cannot respond there any more - I have a limit of three posts in the entire thread. – Learner Mar 09 '21 at 15:11

0 Answers0