Hibernate пагинация с AOP pointcuts

Существует ли какой-либо известный подход к нумерации страниц с помощью аспектно-ориентированных аннотаций (или чего-либо еще, что делает работу) с помощью pointcut для идентификации Criteria в любом данном методе и применять к нему setFirstResults а также setMaxResults Методы API?

Например, я хотел бы применить нумерацию страниц в моей спящей GenericDao реализация:

@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();
}

И @Paginate аннотация будет применяться setFirstResults а также setMaxResults на criteria.list() вызов. Это возможно?

Замечания: @Paginate это пример того, что я хотел бы использовать.

1 ответ

Да, вы можете сделать это через AspectJ.

Вот самосогласованный, автономный пример кода. Вы можете просто скопировать и вставить классы и аспект и запустить его. Я не использую оригинальные классы Hibernate, но некоторые фиктивные замены, потому что я не пользователь Hibernate, и я также хочу сделать это универсальным ответом:

Фальшивые, похожие на hibernate интерфейсы и классы:

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 + "]";
    }
}

Как видите, методы setFirstResult(..) а также setMaxResults(..) добавить специальные критерии через add(..) на внутренний список. Я просто сделал это, чтобы позже проиллюстрировать эффект аспекта.

Разметка страницы:

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 {}

Приложение для водителя:

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);
    }
}

Приложение содержит метод, аннотированный @Paginate, делая подобные вещи, как ваш пример кода. Это не вызывает никаких методов нумерации страниц все же. Это будет сделано по следующему аспекту.

Нумерация страниц:

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);
    }
}

Pointcut перехватывает вызовы Criteria+.list() (+ включает подклассы), но только когда выдается в потоке управления (cflow()) любых исполняемых методов, аннотированных @Paginate, Цель вызова связана с параметром Criteria criteria чтобы использовать его в методе совета, привязанном к этому pointcut. Нам нужна ссылка на цель вызова, потому что мы хотим вызвать методы нумерации страниц на ней. Это то, что делает совет, и это ясно иллюстрируется выводом консоли.

Консольный вывод:

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]]]

Наслаждайтесь!

Другие вопросы по тегам