1

Is there any known approach to hibernate pagination with aspect-oriented annotations (or anything else that does the job) with pointcuts to identify a Criteria in any given method and apply to it the setFirstResults and setMaxResults API methods?

For instance, I would like to apply pagination as follows in my hibernate GenericDao implementation:

@Paginate
public List<T> filter(Set<Criterion> filter, Map<String, String> alias){
    Criteria criteria = session.createCriteria(type.class);
    fillCriteria(criteria, filter, alias);
    return criteria.list();
}

And the @Paginate annotation would apply setFirstResults and setMaxResults on a criteria.list() call. Is this possible?

Note: @Paginate is an example of what I would like to use.

nuno
  • 1,771
  • 1
  • 19
  • 48
  • Please update your question by adding executable sample code illustrating your problem and then describe the desired outcome. People here like to discuss concrete programming problems. You are more likely to get a good answer that way. – kriegaex Oct 21 '14 at 10:33
  • Thank you for suggestion; I revised my question. – nuno Oct 21 '14 at 10:50

1 Answers1

1

Yes, you can do this via AspectJ.

Here is a self-consistent, stand-alone code sample. You can just copy and paste the classes and the aspect and run it. I am not using original Hibernate classes, but some dummy replacements because I am not a Hibernate user and I also want to make this a general purpose answer:

Dummy, hibernate-like interfaces and classes:

package de.scrum_master.app;

public interface Criterion {}
package de.scrum_master.app;

public class MyCriterion implements Criterion {
    private String criterion;

    public MyCriterion(String criterion) {
        this.criterion = criterion;
    }

    @Override
    public String toString() {
        return "MyCriterion [criterion=" + criterion + "]";
    }
}
package de.scrum_master.app;

import java.util.List;

public interface Criteria {
    Criteria add(Criterion criterion);
    List list();
    Criteria setFirstResult(int firstResult);
    Criteria setMaxResults(int maxResults);
}
package de.scrum_master.app;

import java.util.LinkedList;
import java.util.List;

public class MyCriteria implements Criteria {
    private List<Criterion> criteria = new LinkedList<>();

    @Override
    public Criteria add(Criterion criterion) {
        criteria.add(criterion);
        return this;
    }

    @Override
    public List list() {
        return new LinkedList();
    }

    @Override
    public Criteria setFirstResult(int firstResult) {
        add(new MyCriterion("first result " + firstResult));
        return this;
    }

    @Override
    public Criteria setMaxResults(int maxResults) {
        add(new MyCriterion("max results " + maxResults));
        return this;
    }

    @Override
    public String toString() {
        return "MyCriteria [criteria=" + criteria + "]";
    }
}

As you can see, methods setFirstResult(..) and setMaxResults(..) add special criteria via add(..) to the internal list. I just did this in order to later illustrate the aspect's effect.

Pagination annotation:

package de.scrum_master.app;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Paginate {}

Driver application:

package de.scrum_master.app;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Application {
    @Paginate
    public List filter(Set<Criterion> filter, Map<String, String> alias) {
        Criteria criteria = new MyCriteria();
        for (Criterion criterion : filter)
            criteria.add(criterion);
        return criteria.list();
    }

    public static void main(String[] args) {
        Set<Criterion> filterCriteria = new HashSet<>();
        filterCriteria.add(new MyCriterion("where"));
        filterCriteria.add(new MyCriterion("group by"));
        new Application().filter(filterCriteria, null);
    }
}

The application contains a method annotated by @Paginate, doing similar things as your sample code. It does not call any pagination methods though. This will be done by the following aspect.

Pagination aspect:

package de.scrum_master.aspect;

import java.util.List;
import de.scrum_master.app.Paginate;
import de.scrum_master.app.Criteria;

public aspect PaginationAspect {
    Object around(Criteria criteria) : 
        call(public List Criteria+.list())  &&
        cflow(execution(@Paginate * *(..))) &&
        target(criteria)
    {
        System.out.println(thisJoinPoint);
        System.out.println("Original criteria: " + criteria);
        criteria.setFirstResult(5);
        criteria.setMaxResults(10);
        System.out.println("Modified criteria: " + criteria);
        return proceed(criteria);
    }
}

The pointcut intercepts calls to Criteria+.list() (the + includes subclasses), but only when issued in the control flow (cflow()) of any executing methods annotated by @Paginate. The call target is bound to a parameter Criteria criteria so as to use it in the advice method bound to that pointcut. We need a reference to the call target because we want to call the pagination methods upon it. This is what the advice does and it is clearly illustrated by the console output.

Console output:

call(List de.scrum_master.app.Criteria.list())
Original criteria: MyCriteria [criteria=[MyCriterion [criterion=group by], MyCriterion [criterion=where]]]
Modified criteria: MyCriteria [criteria=[MyCriterion [criterion=group by], MyCriterion [criterion=where], MyCriterion [criterion=first result 5], MyCriterion [criterion=max results 10]]]

Enjoy!

kriegaex
  • 63,017
  • 15
  • 111
  • 202