Как реализовать общее разбиение на страницы - PullRequest
19 голосов
/ 24 марта 2011

Я не ищу реализацию Hibernate / JPA / JDBC, но для общего шаблона проектирования.

Поиск по страницам в Google дает мне массу информации, множество интересных статей, объясняющих, как реализовать разбиение на страницы в пользовательском интерфейсе, и различные реализации, которые более или менее делают то же самое.

Поскольку я используюSpring 3.0.5, и я наткнулся на эту хорошую справочную статью Как реализовать разбиение на страницы в Spring MVC 3 .

Простой бин:

public class Person{
     private String personName;
     private int age;
     // ...
}

Простой интерфейс DAO:

public interface PersonDAO{
   Set<Person> getAllPersons(int start, int limit,String orderBy);
   Set<Person> findPersonsByName(String name, int start, int limit,String orderBy);
}

И реализация Hibernate

   @Repository
   public class PersonDAOImpl implements PersonDAO {

        @Autowired(required = true)
    private SessionFactory sessionFactory;

        public Set<Person> getAllPersons(int start, int limit, String orderBy){
                Criteria crit = sessionFactory.getCurrentSession().createCriteria(Person.class);
                crit.setFirstResult(start);
                crit.setMaxResults(limit);
                crit.addOrder(Order.asc("personName"));
                return new LinkedHashSet<Person>(crit.list());
        }


        public Set<Person> findPersonsByName(String name, int start, int limit, String orderBy){
                Criteria crit = sessionFactory.getCurrentSession().createCriteria(Person.class);
                crit.add(Restrictions.eq("name", name));
                crit.setFirstResult(start);
                crit.setMaxResults(limit);
                crit.addOrder(Order.asc(orderBy));
                return new LinkedHashSet<Person>(crit.list());
         }

Теперь я думаюесли мне нужно включить похожие параметры во всем интерфейсе, то здесь что-то действительно не так.Либо я могу обернуть запрос в объект bean-объекта запроса и передать этот bean-компонент методам, что-то вроде этого

public class PersonRequest{
   private int start;
   private int limit;
   private String orderBy;
   private String name;
   // ...
}

И впоследствии

public interface PersonDAO{
   Set<Person> getAllPersons(PersonRequest request);
   Set<Person> findPersonsByName(PersonRequest request);
}

Но для некоторых это тоже кажется неестественнымпричина.Затем я думаю о varargs в Java

public interface PersonDAO{
   Set<Person> getAllPersons(Object... params);
   Set<Person> findPersonsByName(String name,Object... params);
}


   @Repository
   public class PersonDAOImpl implements PersonDAO {

        @Autowired(required = true)
    private SessionFactory sessionFactory;



        public Set<Person> getAllPersons(Object... params){
                Criteria crit = sessionFactory.getCurrentSession().createCriteria(Person.class);
                crit.setFirstResult((Integer)params[0]);
                crit.setMaxResults((Integer)params[1]);
                crit.addOrder(Order.asc("personName"));
                return new LinkedHashSet<Person>(crit.list());
        }


        public Set<Person> findPersonsByName(String name, Object... params){
                Criteria crit = sessionFactory.getCurrentSession().createCriteria(Person.class);
                crit.add(Restrictions.eq("name", name));
                crit.setFirstResult((Integer)params[0]);
                crit.setMaxResults((Integer)params[1]);
                crit.addOrder(Order.asc((String)params[2]));
                return new LinkedHashSet<Person>(crit.list());
         }

Это тоже кажется немного неубедительным, по какой-то причине я продолжаю думать, что паттерн «мост» может быть полезным, но все еще далек от непригодности.

Любая идея, какбудет иметь дело с этим?

Ответы [ 2 ]

24 голосов
/ 24 марта 2011

На вашем месте я бы вернул не сам результат (Set), а что-то, что инкапсулирует извлечение результата.Какой-то ResultBuilder.Посмотрите:

public interface ResultBuilder<T> {

    ResultBuilder<T> withOffset(int offset);

    ResultBuilder<T> withLimit(int limit);

    ResultBuilder<T> orderedBy(String property);

    List<T> result();
}

и затем измените сигнатуру метода DAO:

ResultBuilder<Person> findPersonsByName(String name);

Таким образом, вы можете исключить несущественные для бизнеса аргументы из методов семейства find.Если вы не хотите, чтобы клиент указывал эти параметры, не указывайте его.

Просто чтобы было ясно:

public final class HibernateGenericResultBuilder<T> implements ResultBuilder<T> {

    private final Criteria criteria;

    public HibernateGenericResultBuilder(Criteria criteria) {
        this.criteria = criteria;
    }

    @Override public ResultBuilder<T> withOffset(int offset) {
        criteria.setFirstResult(offset);
        return this;
    }

    @Override public ResultBuilder<T> withLimit(int limit) {
        criteria.setMaxResults(limit);
        return this;
    }

    @Override public ResultBuilder<T> orderedBy(String property) {
        criteria.addOrder(Order.asc(property));
        return this;
    }

    @Override public List<T> result() {
        return new LinkedHashSet<T>(criteria.list());
    }
}
2 голосов
/ 24 марта 2011

Я бы рассмотрел применение шаблона стратегии здесь.

По сути, вместо того, чтобы указывать старт и лимит в качестве параметров или оборачивать их в varargs, создать реальный объект, поместить их туда и переместить ответственностьустановка подкачки по критериям для этого объекта.

Грубо говоря (я не компилирую ...):

public interface PagingSpecification {
    void apply(Criteria criteria);
}

public class ConcretePagingSpecification implements PagingSpecification {
    private int start;
    private int limit;

    public ConcretePagingSpecification(int start, int limit) {
       this.start = start;
       this.limit = limit;
    }

    public void apply(Criteria crit) {
       crit.setFirstResult(start);
       crit.setMaxResults(limit);         
    }
}

А затем, конечно, передайте это в ваши искатели и вызовите егов очевидных местах.

Одним из преимуществ этого является то, что вы можете сделать реализацию NullPagingSpecification, которая ничего не делает, так что вы можете использовать тот же код, когда вы на самом деле не хотите подкачки страниц.

Другое - то, что вы можете перемещать такие вещи, как next() и previous() методы, которые вам, вероятно, понадобятся (чтобы разрешить фактическую подкачку страниц), также в классы PagingSpecification и обмениваться еще большим количеством кода.

...