Announcement Announcement Module
Collapse
No announcement yet.
Spring Data JPA - Eager Fetching Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring Data JPA - Eager Fetching

    I'm working on a web application that makes use of the Spring Data JPA library. The web application is designed using the typical Dao/Service/Web layered architecture. From the service layer, my goal is to be able to instruct the repositories to eagerly fetch associations on my domain model. I don't like to rely on the OSIV pattern because I believe that transactions should begin and end at the service layer. I also think I can get better performance when I instruct the Dao layer to eagerly fetch associations with LEFT OUTER joins. I am unable figure out how to do this cleanly using the repository methods that have the Specification<ModelObject> argument. For example:

    Code:
    myRepository.findAll(Specification<ModelClass>)
    I can accomplish what I need to do but i don't think it's very clean and violates the Specification idiom. Here's an implementation of my solution and how it would be used, but I don't like it.

    I have a Fetches class that holds JPA metamodel SingularAtributes and PluralAttributes, essentially these are the instructions sent to the repository on what to eagerly fetch. Try to look pass all of generics noise, I'm sure i can clean that up a bit.

    Code:
    public class Fetches<T extends AbstractBaseEntity<? extends Serializable>> {
        
        private final List<SingularAttribute<T, ? extends Serializable>> singularAttributes = new ArrayList<SingularAttribute<T, ? extends Serializable>>();
    
    
        private List<PluralAttribute<T,? extends Collection<? extends AbstractBaseEntity<? extends Serializable>>, ? extends AbstractBaseEntity<? extends Serializable>>> pluralAttributes 
                        = new ArrayList<PluralAttribute<T,? extends Collection<? extends AbstractBaseEntity<? extends Serializable>>, ? extends AbstractBaseEntity<? extends Serializable>>>();
    
    
        public Fetches<T> add(SingularAttribute<T, ? extends Serializable> singularAttribute){
            singularAttributes.add(singularAttribute);
            return this;
        }
        
        public Fetches<T> add(PluralAttribute<T,? extends Collection<? extends AbstractBaseEntity<? extends Serializable>>, ? extends AbstractBaseEntity<? extends Serializable>> pluralAttribute){
            pluralAttributes.add(pluralAttribute);
            return this;
        }
        
        public List<SingularAttribute<T, ? extends Serializable>> singularAttributes() {
            return Collections.unmodifiableList(singularAttributes);
        }
        
        public List<PluralAttribute<T,? extends Collection<? extends AbstractBaseEntity<? extends Serializable>>, ? extends AbstractBaseEntity<? extends Serializable>>> pluralAttributes() {
            return Collections.unmodifiableList(pluralAttributes);
        }
    }
    The following static Specification method (hasPostalCode) has an additional Fetches argument. The fetches argument is passed to a private bindFetchesAttributes method along with the criteria Root to specify the fetching.

    Code:
    public class PartnerSpecifications extends BaseSpecifications<Partner> {
    
    
        public static Specification<Partner> hasPostalCode(final String postalCode, final Fetches<?> fetches) {
            
            return new Specification<Partner>() {
    
    
                @Override
                public Predicate toPredicate(Root<Partner> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                    bindFetchAttributes(root, fetches);
                    return builder.equal(root.get(Partner_.postalCode), postalCode);
                }
            };
        }
    
    
        private static void bindFetchAttributes(final Root<? extends AbstractBaseEntity<? extends Serializable>> root, final Fetches<?> fetchMetamodel ) {
            
            if(fetchMetamodel != null) {
                
                // SingularAtrributes
                for(SingularAttribute attr : fetchMetamodel.singularAttributes() ){
                    root.fetch(attr, JoinType.LEFT);
                }
                
                // PluraAttributes
                for(PluralAttribute attr : fetchMetamodel.pluralAttributes()) {
                    root.fetch(attr);
                }
            }
        }
    }
    And this is an example of how the above code would be used:

    Code:
    Fetches<Partner> fetches = new Fetches<Partner>();
    fetches.add(Partner_.addresses);
            
    List<Partner> partners = partnerRepository.findAll(where(hasPostalCode(postalCode, fetches)));

    What i would really like to be able to do is this:


    Code:
    Fetches<Partner> fetches = new Fetches<Partner>();
    fetches.add(Partner_.addresses);
            
    List<Partner> partners = partnerRepository.findAll(where(hasPostalCode(postalCode)).fetch(fetches));
    
    // or this...
    
    List<Partner> partners = partnerRepository.findAll(where(hasPostalCode(postalCode)), fetches);

    I like the second alternative more since fetching isn't really a Specification from the DDD point of view.

    I was just wondering if someone else had any ideas on how I could accomplish what I would like with the current repository interfaces? Maybe i'm overlooking an obvious way to accomplish my goal.

    Thanks.

    -Russ

  • #2
    Hi,

    I am also looking for a general structure to apply the fetching mode in the DAOs (Repositories) with Spring Data JPA, and I found this post.
    I had a similar idea. Taking the example above:

    List<Partner> findAll ( PartnerFetches ... fetches);

    Where the fetches are specific to Partner. I did not use generics, instead I used varargs with Enums for each attribute to fetch from Partner.
    Using varargs with the Enums allows me to combine the attributes to fetch. All attributes are fetched with a left_outer_join.
    This is possibly not the best approach, but I was looking to use something like Hibernate Meta Model to generate automatically all the attribute relationships of a persistent class, instead of the Enums.

    I am still looking for a solution. With Enums works fine, but I have to create each Enum value for each class.
    Does it work with generics?
    What would the guys from Spring Data JPA recommend?

    Comment


    • #3
      I found this related thread, using openJPA fetch groups

      http://forum.springsource.org/showth...JPA...possible

      Comment

      Working...
      X