如何实现通用分页

发布于 2024-10-25 19:29:35 字数 3537 浏览 2 评论 0原文

我不是在寻找 Hibernate/JPA/JDBC 实现,而是在寻找通用设计模式。

谷歌搜索“分页”给了我大量的信息,很多有趣的文章解释了如何在 UI 上实现分页以及或多或少做同样事情的各种实现。

由于我使用的是 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);
}

出于某种原因,这似乎也很不自然。然后我想到了 Java 中的 varargs

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

这似乎也有点脆弱,出于某种原因,我一直认为桥接模式可能会有所帮助,但仍然远远不合适。

知道你会如何处理这个问题吗?

I am not looking for a Hibernate/JPA/JDBC implementation, but for a general design pattern.

Googling "pagination" gives me loads of information, lot of interesting articles that explain how to implement pagination on the UI and various implementations which more or less do the same.

Since I am using Spring 3.0.5, and I stumbled this good reference article How to implement pagination in Spring MVC 3.

Simple bean:

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

A simple DAO interface:

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

And the hibernate implementation

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

Now, I am thinking if I have to include similar parameters across all the interface then there is something really wrong here. Either I can wrap the request in a request bean object and pass this bean to the methods, something like this

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

And subsequently

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

But this too seems unnatural, for some reason. Then I am thinking of varargs in 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());
         }

This too seems bit flimsy, for some reason I keep thinking bridge pattern could be helpful but still is distant unfit.

Any idea how you would deal with this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

驱逐舰岛风号 2024-11-01 19:29:35

如果我是你,我不会返回结果 (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());
    }
}

If I were you I would return not the result (Set) itself but something that encapsulates retrieval of the result. Some sort of ResultBuilder. Look:

public interface ResultBuilder<T> {

    ResultBuilder<T> withOffset(int offset);

    ResultBuilder<T> withLimit(int limit);

    ResultBuilder<T> orderedBy(String property);

    List<T> result();
}

and then change DAO method signature:

ResultBuilder<Person> findPersonsByName(String name);

This way you can factor out business-irrelevant arguments from find-family methods.
If you don't want to make client specify this params then don't make him.

Just to be clear:

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());
    }
}
苦行僧 2024-11-01 19:29:35

我会考虑在这里应用策略模式。

基本上,不要将开始和限制作为参数提供或将它们包装在可变参数中,而是创建一个真实的对象,将它们放在那里,并将根据条件设置分页的责任转移到该对象。

粗略地说(我没有编译......):

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< /code> 类,并共享更多代码。

I would consider applying the Strategy Pattern here.

Basically, instead of supplying the start and limit as parameters or wrapping them in a varargs, make a real object, put them there, and move the responsibility of setting the paging on the criteria to this object.

Roughly speaking (I'm not compiling...):

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

And then of course pass this into your finders and call it in the obvious places.

One advantage of this is that you can make a NullPagingSpecification implementation which does nothing, so that you can use the same code when you don't actually want paging.

Another is that you can move things like the next() and previous() methods you're likely to need (to allow actual paging) into the PagingSpecification classes as well, and share yet more code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文