-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: Not-So-Lazy Fetching of Transitive Associations
PostPosted: Tue Oct 07, 2014 11:03 am 
Newbie

Joined: Mon Oct 06, 2014 1:25 pm
Posts: 2
I've been experiencing a problem with excessive select statements bogging down our database, and I'm curious if anybody can shed some light on the situation. The code I've provided below shows the basic structure of the relationships that seem to be giving us the problem.

We use a @MappedSuperclass to provide @Id and @Version for all of our classes. Class A and class B have a many-to-many relationship managed through OccurrenceOfB, but there is also extra information (not shown) associated with each OccurrenceOfB instance (hence why we are not merely using a JoinTable).

The problem becomes manifest when we attempt to create a string representation of A. In this case, I'm actually constructing JSON, but I've excluded the bulk of that here. The pertinent code, included below, involves iterating over all OccurrenceOfBs in the A and getting the ID of each. However, when we do this (and specifically, when the iteration begins), Hibernate is retrieving all of the OccurrenceOfB objects. This is just as I would expect. However, at the same time, it is also retrieving all of the corresponding B objects, and the C objects associated with each B. In other words, when it lazily retrieves the OccurrenceOfBs, it is then eagerly fetching the rest of the entity graph and not honoring the FetchType.LAZY annotations (which are admittedly, according to the documentation, merely a hint). This is not what I would expect--and is causing some significant performance hits when the size of A.occurrences is in the thousands. (Our actual objects are slightly more complex than this, but serializing one A with 1500 OccurrenceOfBs results in 10502 select statements in our system.)

Curiously, the opposite does not hold true. If I have a B that occurs in 1500 As, converting the B to a string (which likewise iterates over the occurrences collection and retrieves the ID of each) does NOT perform all of the excessive select statements.

And now, at long last to the actual question: How can I improve performance in my database in this scenario? Setting the default batch fetch size provides some relief, but it seems to be a band-aid on the underlying problem. Ideally, how can I convince Hibernate to actually honor lazy fetching for transitive associations?

In advance, thanks for your help!

Code:
@MappedSuperclass
public class PersistentObject
{
    @Id
    @GeneratedValue
    @Column
    protected Long id;

    @Version
    @Column
    protected Long version;
}


@Entity
public class A extends PersistentObject
{
    @OneToMany(mappedBy = "a",
               fetch = FetchType.LAZY,
               orphanRemoval = true,
               cascade = { CascadeType.ALL })
    private final Set<OccurrenceOfB> occurrences = new HashSet<>();
   
    // Other columns here
   
   @Override
    public String toString()
    {
        // ...
        List<Long> ids = new LinkedList<>();
        for ( OccurrenceOfB occurrence : occurrences )
        {
            ids.add(occurrence.getId());
        }
        // ...
    }
}

@Entity
public class OccurrenceOfB extends PersistentObject
{
    @ManyToOne(fetch = FetchType.LAZY,
               cascade = { CascadeType.PERSIST,
                           CascadeType.MERGE },
               optional = false)
    private A a;


    @ManyToOne(fetch = FetchType.LAZY,
               cascade = { CascadeType.PERSIST,
                           CascadeType.MERGE },
               optional = false)
    private B b;

    // Other columns here
}

@Entity
public class B extends PersistentObject
{
    @OneToMany(mappedBy = "b",
               fetch = FetchType.LAZY,
               orphanRemoval = true,
               cascade = { CascadeType.ALL })
    private final Set<OccurrenceOfB> occurrences = new HashSet<>();

    @OneToOne(fetch = FetchType.LAZY,
              cascade = { CascadeType.ALL },
              optional = false)
    @JoinColumn(name = "c",
                nullable = false)
    private C c;
   
    // Other columns here
}

@Entity
public class C extends PersistentObject
{
    @OneToOne(mappedBy = "c",
              optional = false)
    @JoinColumn(name = "b",
                nullable = false)
    private B b;
   
    // Other columns here
}


Top
 Profile  
 
 Post subject: Re: Not-So-Lazy Fetching of Transitive Associations
PostPosted: Wed Oct 08, 2014 4:09 pm 
Newbie

Joined: Mon Oct 06, 2014 1:25 pm
Posts: 2
Follow-Up:
So, with some more investigation I think I've identified the problem--and it wasn't in any of the code I had mirrored here. It was actually a faulty implementation of equals/hashCode. Because OccurrenceOfB is pretty much defined by the A and B to which it references, both values were being referenced in equals/hashCode. As a result, the mere act of iterating over the collection was triggering a fetch. Because B had a similar implementation (that referenced C), it was performing yet another transitive fetch.

I've removed the implementations entirely and saw that my 10502 select statements was reduced to 3--and that's an improvement I can get behind. Obviously a proper implementation of equals/hashCode will be required (and will be fun given the caveats indicated in https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html), but that is beyond the scope of this post.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.