Comment mettre en œuvre la pagination générale

Je ne cherche pas une implémentation Hibernate / JPA / JDBC, mais un modèle de conception général.

La “pagination” sur Google me fournit une mine d’informations, de nombreux articles intéressants qui expliquent comment implémenter la pagination sur l’interface utilisateur et diverses implémentations qui font plus ou moins la même chose.

Depuis que j’utilise Spring 3.0.5, je suis tombé sur ce bon article de référence Comment implémenter la pagination dans Spring MVC 3 .

Haricot simple:

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

Une simple interface DAO:

 public interface PersonDAO{ Set getAllPersons(int start, int limit,Ssortingng orderBy); Set findPersonsByName(Ssortingng name, int start, int limit,Ssortingng orderBy); } 

Et l’implémentation d’hibernation

  @Repository public class PersonDAOImpl implements PersonDAO { @Autowired(required = true) private SessionFactory sessionFactory; public Set getAllPersons(int start, int limit, Ssortingng orderBy){ Criteria crit = sessionFactory.getCurrentSession().createCriteria(Person.class); crit.setFirstResult(start); crit.setMaxResults(limit); crit.addOrder(Order.asc("personName")); return new LinkedHashSet(crit.list()); } public Set findPersonsByName(Ssortingng name, int start, int limit, Ssortingng orderBy){ Criteria crit = sessionFactory.getCurrentSession().createCriteria(Person.class); crit.add(Ressortingctions.eq("name", name)); crit.setFirstResult(start); crit.setMaxResults(limit); crit.addOrder(Order.asc(orderBy)); return new LinkedHashSet(crit.list()); } 

Maintenant, je pense que si je dois inclure des parameters similaires dans toute l’interface, il y a quelque chose qui ne va vraiment pas ici. Soit je peux envelopper la requête dans un object bean request et transmettre ce bean aux méthodes, quelque chose comme ceci

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

Et par la suite

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

Mais cela aussi semble anormal, pour une raison quelconque. Alors je pense à varargs en Java

 public interface PersonDAO{ Set getAllPersons(Object... params); Set findPersonsByName(Ssortingng name,Object... params); } @Repository public class PersonDAOImpl implements PersonDAO { @Autowired(required = true) private SessionFactory sessionFactory; public Set 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(crit.list()); } public Set findPersonsByName(Ssortingng name, Object... params){ Criteria crit = sessionFactory.getCurrentSession().createCriteria(Person.class); crit.add(Ressortingctions.eq("name", name)); crit.setFirstResult((Integer)params[0]); crit.setMaxResults((Integer)params[1]); crit.addOrder(Order.asc((Ssortingng)params[2])); return new LinkedHashSet(crit.list()); } 

Cela aussi semble un peu fragile, pour une raison quelconque, je continue de penser que le motif de pont peut être utile, mais rest inutilisable à distance.

Avez-vous une idée de la façon dont vous traiteriez cela?

Si j’étais vous, je ne retournerais pas le résultat ( Set ) lui-même mais quelque chose qui résume la récupération du résultat. Une sorte de ResultBuilder. Regardez:

 public interface ResultBuilder { ResultBuilder withOffset(int offset); ResultBuilder withLimit(int limit); ResultBuilder orderedBy(Ssortingng property); List result(); } 

puis changez la signature de méthode DAO:

 ResultBuilder findPersonsByName(Ssortingng name); 

De cette façon, vous pouvez factoriser les arguments non pertinents pour l’entreprise à partir des méthodes find-family. Si vous ne voulez pas que le client spécifie ce paramètre, alors ne le faites pas.

Juste pour être clair:

 public final class HibernateGenericResultBuilder implements ResultBuilder { private final Criteria criteria; public HibernateGenericResultBuilder(Criteria criteria) { this.criteria = criteria; } @Override public ResultBuilder withOffset(int offset) { criteria.setFirstResult(offset); return this; } @Override public ResultBuilder withLimit(int limit) { criteria.setMaxResults(limit); return this; } @Override public ResultBuilder orderedBy(Ssortingng property) { criteria.addOrder(Order.asc(property)); return this; } @Override public List result() { return new LinkedHashSet(criteria.list()); } } 

J’envisagerais d’appliquer le modèle de stratégie ici.

Fondamentalement, au lieu de fournir les parameters start et limit sous la forme de parameters ou de les envelopper dans un varargs, créez un object réel, mettez-les là et transférez la responsabilité de définir la pagination sur les critères à cet object.

Grosso modo (je ne comstack pas …):

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

Et bien sûr, transmettez ceci à vos trouveurs et appelez-le dans les endroits évidents.

Un avantage de cela est que vous pouvez effectuer une implémentation NullPagingSpecification qui ne fait rien, de sorte que vous puissiez utiliser le même code lorsque vous ne voulez pas réellement faire de pagination.

Une autre possibilité consiste à déplacer des éléments tels que les méthodes next() et previous() vous aurez probablement besoin (pour permettre la pagination proprement PagingSpecification ) dans les classes PagingSpecification , et à partager davantage de code.