Adrian Adamczyk
Adrian Adamczyk

Reputation: 3070

Eager fetch nested (recursive) objects - Hibernate

I want to populate my object hierarchy eagerly. The problem is that at some point Hibernate stops from fetching; it leaves relation fields (lists) unpopulated.

The hierarchy is (some code is ommited like id's, getters, this part seems to be fine, hopefully):

public class User {
    private Container container;

    @OneToOne(fetch = FetchType.EAGER)
    public Container getContainer() {
        return container;
    }
}


public class Container {
    private List<Backpack> backpacks;
    private List<Item> items;

    @OneToMany(fetch = FetchType.EAGER, mappedBy="container")
    @Fetch(FetchMode.SELECT)
    private List<Item> getItems() {
        return items;
    }


    @OneToMany(fetch = FetchType.EAGER, mappedBy="container")
    @Fetch(FetchMode.SELECT)
    private List<Backpack> getBackpacks() {
        return backpacks;
    }
}


public class Backpack {
    ...
    private Container container;            /* Unidirectional relationship - ID of container which contains this backpack */
    private Container backpackContainer;    /* Unidirectional relationship - ID of container which represents backpack's storge */
    ...

    @NotNull
    @ManyToOne(fetch = FetchType.EAGER)
    @Fetch(FetchMode.SELECT)
    public Container getContainer() {
        return container;
    }


    @OneToOne(fetch = FetchType.EAGER)
    @Fetch(FetchMode.SELECT)
    public Container getBackpackContainer() {
        return backpackContainer;
    }
}

And the result:

User(
    uid=2,
    name=admin,
    ....,

    container=Container(
        id=1,

        items=[
            Item(...),
            Item(...),
            Item(...),
            Item(...)
        ],

        backpacks=[
            Backpack(
                container=1,

                backpackContainer=Container(
                    id=3,
                    items=null, /* Problem! Shouldn't be empty array [] at least or array with items? */
                    backpacks=null /* Problem! Shouldn't be empty array [] at least or array with backpacks? */
                )
            )
        ]
    )
)

As I annotated the result, nested fields items and backpacks are null, which suggests a huge problem. Hibernate tends to use empty sets instead of nulls, also I don't want to add null checkers everywhere.

What can cause this problem?

Also, this hierarchy is not infinite-deep, user's container may have as many backpacks as it wants, but those backpacks may not contain another backpacks.

Upvotes: 0

Views: 1863

Answers (1)

Dragan Bozanovic
Dragan Bozanovic

Reputation: 23552

The only logical explanation is that you are reading the User graph in the same persistence context (session) in which you have saved a new (transient) Container instance containing nulls which is assigned to the backpackContainer reference when loading and assembling the User graph afterwards.

Hibernate does not populate the associations when saving instances, but it does it when loading instances from the db, so you created it with nulls and it remained such in the persistence context. When you read the User graph afterwards, Hibernate reuses everything that is currently present in the persistence context including the Container instance which it assigns to the backpackContainer field.

So, you have two choices:

  • Update both sides of bidirectional relation if you actually intend to use both sides in the same persistence context (cleaner approach);
  • Or simply flush and clear the persistence context before reading the User (if you want everything to happen in the same transaction):

    entityManager.flush();
    entityManager.clear();
    

Upvotes: 2

Related Questions