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.