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