= Circular fetching [IMPORTANT] ==== We need to make sure that any association is only join-fetched once. E.g. ``` from LineItem l join fetch l.order o join fetch l.order o2 ``` This should be illegal. It is possible to handle it specially, but that would be very complicated. `FromClauseIndex#findJoinFetch(NavigablePath parentPath, String fetchableName)` - if this does not return null we know that the association is already join fetched and we should throw and exception ==== == one-to-one ``` @Entity class Root { ... @OneToOne(mappedBy="root1") Leaf leaf1; @OneToOne(mappedBy="root2") Leaf leaf2; } @Entity class Leaf { ... @OneToOne @JoinColumn(name="root_id_1") Root root1; @OneToOne @JoinColumn(name="root_id_2") Root root2; } ``` Given this model, we have the following mappings from modelPart to "identifying columns": * `Root#leaf1` -> `leaf.root_id_1` * `Root#leaf2` -> `leaf.root_id_2` * `Leaf#root1` -> `leaf.root_id_1` * `Leaf#root2` -> `leaf.root_id_2` So given a query like: ``` from Root r join fetch r.leaf1 l join fetch l.root1 join fetch l.root2 ``` `l.root1` is circular whereas `l.root2` is not. We'd know this by looking at the "identifying columns". Specifically, `l.root1` is considered circular **not** because it refers back to `Root(r)` but because it maps to the same column(s) as its parent: `leaf.root_id_1` // we need to be able to ultimately be able to resolve the "identifying columns" for a given path. E.g. ``` interface DomainResultCreationState { ... Fetchable resolveFetchable(NavigablePath navigablePath) { // the path passed in here would be `pathToParent` (i.e. `Root(r).leaf1(l)`) // we'd used that to determine the lhs's identifying columns via // `Fetchable#getIdentifyingColumnExpressions` and check them against the // identifying columns for the Fetchable we are processing } } ``` == many-to-one ``` @Entity @Table(name="orders") class Order { ... @OneToMany(mappedBy="order") List lineItems; } @Entity @Table(name="lines") class LineItem { ... @ManyToOne @JoinColumn(name="order_id") Order order; } ``` Given this model, we have the following mappings from modelPart to "identifying columns": * `LineItem#order` -> `lines.order_id` * `Order#lineItems#{element}` -> `lines.order_id` Once we find a circularity we should build the `BiDirectionalFetch` reference pointing to the Initializer for the "parent parent path". See `RowProcessingState#.resolveInitializer` Hibernate needs to handle circularity in a fetch-graph. E.g.: ``` select o from Order o join fetch o.lineItems l join fetch l.order o2 join fetch o2.lineItems ``` Here, the join fetch of `l.order` is circular, meaning we do not want to render a join in the SQL for it because it is already part of the from-clause via `Order o`. Recognizing circularity needs to happen in a number of mapping scenarios and I believe the conditions vary depending on the type of mapping involved (one-to-one, many-to-one, many-to-many). Ideally we can find commonality and handle these conditions uniformly. == with embeddables ``` @Entity @Table(name="root") class RootEntity { ... @Embedded IntermediateComponent intermediateComponent; } @Embeddable class IntermediateComponent { ... @OneToMany( mappedBy = "rootEntity" ) Set leaves } @Entity @Table(name="leaf") class LeafEntity { ... @ManyToOne @JoinColumn(name="root_id) RootEntity rootEntity; } ``` Given this model, we have the following mappings from modelPart to "identifying columns": * `RootEntity#intermediateComponent#leaves -> `leaf.root_id` * `RootEntity#intermediateComponent#leaves -> `leaf.root_id` * * `RootEntity#intermediateComponent#leaves#{element} * `Order#lineItems#{element}` -> `lines.order_id` class Order { @OneToMany(mappedBy="order") List lineItems; } ``` ------------ "orders" ------------ id INTEGER name VARCHAR ------------ "order_items" ------------ orders_id items_id ------------ "items" ------------ id qty ```