Подтвердить что ты не робот

Как реализовать общую разбивку на страницы

Я не ищу реализацию Hibernate/JPA/JDBC, но для общей схемы проектирования.

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

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

Простой bean:

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

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

Любая идея, как вы справитесь с этим?

4b9b3361

Ответ 1

Если бы я был вами, я бы вернул не результат (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-family. Если вы не хотите, чтобы клиент указывал эти параметры, не делайте его.

Просто, чтобы быть ясным:

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

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

В принципе, вместо того, чтобы предоставлять начальные и конечные значения в качестве параметров или обертывать их в 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, а также поделиться еще большим количеством кода.