使用Hibernate Criteria完成灵活的组合查询

使用Hibernate Criteria完成灵活的组合查询
在Hibernate中,进行查询主要提供了两个核心API:Query以及Criteria,前者是针对HQL语句的,就是说是面向结构化语句的查询,而后者是面向对象(非常适合没有SQL基础的程序开发人员使用)。
怎样生成Criteria?
生成criteria需要借助Hibernate的Session#createCriteria(Class

select * from `t_user` where `username`='张三';

其中查询原子语句可以认为是username=’张三’,Criteria的出发也是这样子的,它主要为你提供生成查询原子语句的方法,以及语句之间的逻辑组合,还有就是最常用的排序。我们想要的,基本而简单的,Criteria都提供给我们了。
Criteria提供了#add方法为我们添加Criteria专用表达式Criterion。
现在需要了解下,怎样生成Criterion表达式。Hibernate3之后为我们提供的API是Restrictions实现类,该类为我们提供了静态方法来生成查询原子语句生成方法#eq、#ne、#gt、#lt、#ge、#le、#in、#like等等
方法接受的参数虽然说都不一样,但是也是极其相似的,就是String property(属性的名称) ,返回值都是Criterion。
比如上述的username=’张三’,可以使用Restrictions生成的Criterion查询表达式是Restrictions.eq(“username”, “张三”):Criterion。这里的“username”是你的领域模型中的属性名称,而不是数据库的列名称。
Criteria中的方法
Criteria#add(Criterion c)以及Criteria#addOrder(Order order)是Criteria中核心的两个方法。一个用于添加Criterion表达式,另外一个用于添加排序。
介绍完了Criteria、Criterion、Restrictions,下面讲讲项目开发中常见的组合查询实现问题。
组合查询作为一个应用程序的基本功能,具有灵活多变的需求,现在的三层开发模式下,可能会在Dao对应的DaoImpl(Dao实现中,一般Dao作为接口,定义实现需要的方法),手工写入具体的hql语句来实现,如果在类不多的前提下(一般来说,一个类对应一个DaoImpl),这样写还算比较简单, 虽说实现简单,却不见的灵活。比方说客户今天说,按照用户名查询用户、明天又改变需求了,需要添加部门来查询用户,如果你是手工写入具体hql的,那么,你就不得不去更改DaoImpl中的hql语句了,如果你设计得比较好的话,或许不需要改变入参(这是最理想的情况了),但是WEB层的参数更改是或许是需要的,这样算算,就改动了持久化层与WEB层。那么怎样不需要代码,就能满足任意的组合查询呢?(仔细想想,这样做,开发量是减少了,如果不约束查询串,那么会有安全性问题)。
在这里我提供一种思路:前台无论传递多少参数,都需要经过HttpServletRequest对象,所有,只要使用这个对象,你就可以取得任意的参数串。而Dao层,要保证Criteria能自由地组合参数(这句话肯定是很抽象的),你可以在Dao中,使用命令行设计模式,结合组合查询的方法combineQuery(Set commands)来进行实现,commands一般就包含了子查询和排序两种命令在里面,在方法内部通过add(Criterion)以及addOrder(Order)来将属性以及排序拼接。看看以下的代码:
命令行接口:

package com.test;

import org.hibernate.Criteria;

/**
 * @author daniel
 *
 */
public interface CriteriaCommand {

    public Criteria execute(Criteria c);
}

域命令行实现类:

/**
 * 
 */
package com.test;

import java.util.List;
import java.util.Set;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.hibernate.Criteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;

/**
 * @author daniel
 *
 */
public class FieldCommandImpl implements CriteriaCommand {

    public static enum OperationMode {
        EQ, NE, GT, LT, GE, LE, IN, LIKE 
    }

    //属性名称
    private String propertyName;

    //属性值
    private Object propertyValue;

    //操作
    private OperationMode operation;

    public FieldCommandImpl()
    {
        this(null, null, OperationMode.EQ);
    }

    public FieldCommandImpl(String propertyName, Object propertyValue, OperationMode mode)
    {
        this.propertyName = propertyName;
        this.propertyValue = propertyValue;
        this.operation = mode;
    }

    /**
     * @return the propertyName
     */
    public Object getPropertyName() {
        return propertyName;
    }

    /**
     * @param propertyName the propertyName to set
     */
    public void setPropertyName(String propertyName) {
        this.propertyName = propertyName;
    }

    /**
     * @return the propertyValue
     */
    public Object getPropertyValue() {
        return propertyValue;
    }

    /**
     * @param propertyValue the propertyValue to set
     */
    public void setPropertyValue(Object propertyValue) {
        this.propertyValue = propertyValue;
    }

    /**
     * @return the operation
     */
    public OperationMode getOperation() {
        return operation;
    }

    /**
     * @param operation the operation to set
     */
    public void setOperation(OperationMode operation) {
        this.operation = operation;
    }

    @SuppressWarnings("unchecked")
    public Criteria execute(Criteria c) {
        // TODO Auto-generated method stub
        if(c == null)
        {
            throw new NullPointerException();
        }
        else
        {
            if(OperationMode.EQ.equals(operation))//等于
            {
                c.add(Restrictions.eq(propertyName, propertyValue));
            }
            else if(OperationMode.NE.equals(operation))//不等于
            {
                c.add(Restrictions.ne(propertyName, propertyValue));
            }
            else if(OperationMode.GT.equals(operation))//大于
            {
                c.add(Restrictions.gt(propertyName, propertyValue));
            }
            else if(OperationMode.GE.equals(operation))//大于等于
            {
                c.add(Restrictions.ge(propertyName, propertyValue));
            }
            else if(OperationMode.LT.equals(operation))//小于
            {
                c.add(Restrictions.lt(propertyName, propertyValue));
            }
            else if(OperationMode.LE.equals(operation))//小于等于
            {
                c.add(Restrictions.le(propertyName, propertyValue));
            }
            else if(OperationMode.IN.equals(operation))//in操作
            {
                if(propertyValue instanceof List)
                {
                    List<Object> in_params = (List<Object>) propertyValue;
                    c.add(Restrictions.in(propertyName, in_params));
                }
                else if(propertyValue instanceof Set)
                {
                    Set<Object> in_params = (Set<Object>) propertyValue;
                    c.add(Restrictions.in(propertyName, in_params));
                }
                else if(propertyValue instanceof Object[])
                {
                    Object[] in_params = (Object[]) propertyValue;
                    c.add(Restrictions.in(propertyName, in_params));
                }
                else
                {
                    throw new IllegalArgumentException();//其他,非法参数
                }
            }
            else if(OperationMode.LIKE.equals(operation))
            {
                c.add(Restrictions.like(propertyName, (String) propertyValue, MatchMode.ANYWHERE));//%a%的匹配模式
            }
        }
        return c;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        HashCodeBuilder builder = new HashCodeBuilder();
        builder.append(propertyName);
        builder.append(propertyValue);
        builder.append(operation);
        return builder.toHashCode();
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        if(obj == null)
        {
            return false;
        }
        if(obj == this)
        {
            return true;
        }
        if(!obj.getClass().equals(this.getClass()))
        {
            return false;
        }
        FieldCommandImpl command = (FieldCommandImpl) obj;
        EqualsBuilder builder = new EqualsBuilder();
        builder.append(propertyName, command.getPropertyName());
        builder.append(propertyValue, command.getPropertyValue());
        builder.append(operation, command.getOperation());
        return builder.isEquals();
    }


}

排序命令行实现类:

/**
 * 
 */
package com.test;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.hibernate.Criteria;
import org.hibernate.criterion.Order;

/**
 * @author daniel
 *
 */
public class SortCommandImpl implements CriteriaCommand {

    public static enum SortMode {
        ASC, DESC
    }

    private String propertyName;

    private SortMode sortMode;

    public SortCommandImpl()
    {
        this(null, SortMode.ASC);//默认增序排序
    }

    public SortCommandImpl(String propertyName, SortMode sortMode)
    {
        this.propertyName = propertyName;
        this.sortMode = sortMode;
    }

    /**
     * @return the propertyName
     */
    public String getPropertyName() {
        return propertyName;
    }

    /**
     * @param propertyName the propertyName to set
     */
    public void setPropertyName(String propertyName) {
        this.propertyName = propertyName;
    }

    /**
     * @return the sortMode
     */
    public SortMode getSortMode() {
        return sortMode;
    }

    /**
     * @param sortMode the sortMode to set
     */
    public void setSortMode(SortMode sortMode) {
        this.sortMode = sortMode;
    }

    public Criteria execute(Criteria c) {
        // TODO Auto-generated method stub
        if(c == null)
        {
            throw new NullPointerException();
        }
        else
        {
            if(SortMode.ASC.equals(sortMode))//升序
            {
                c.addOrder(Order.asc(propertyName));
            }
            else if(SortMode.DESC.equals(sortMode))//降序
            {
                c.addOrder(Order.desc(propertyName));
            }
            else
            {
                throw new IllegalArgumentException();
            }
            return c;
        }
    }

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        HashCodeBuilder builder = new HashCodeBuilder();
        builder.append(propertyName);
        builder.append(sortMode);
        return builder.toHashCode();
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        if(obj == null)
        {
            return false;
        }
        if(obj == this)
        {
            return true;
        }
        if(!obj.getClass().equals(this.getClass()))
        {
            return false;
        }
        SortCommandImpl command = (SortCommandImpl) obj;
        EqualsBuilder builder = new EqualsBuilder();
        builder.append(propertyName, command.getPropertyName());
        builder.append(sortMode, command.getSortMode());
        return builder.isEquals();
    }


}

为什么会有命令行接口?其实就是为了方法参数更加优雅。
为什么要区分域命令行以及排序命令行?
其实是针对add(Criterion)以及addOrder(Order order)两个方法的调用。
在生成子查询时,Criterion需要的信息主要是属性名称、属性值以及操作类型,这样才能决定生成什么样的Criterion。比方说,传递进来的FieldCommandImpl中propertyName=username、而propertyValue=张三,mode=OperationMode.EQ,就可以决定生成一个Restrictions.eq(propertyName,propertyValue)的Criterion表达式。
在生成排序时,需要调用生成对应的Order对项,而Order#asc(String propertyName):Order以及Order.desc#desc(String propertyName):Order。毕竟排序时需要的信息比生成原子查询Criterion表达式i信息少,只需要知道属性名称和排序方式就够了。
Dao层的实现方法:

    /**
     * 适用于组合查询
     */
    public List<E> zuheQuery(final Class<?> entityClass, final Set<CriteriaCommand> commands)
    {
        return template.execute(new HibernateCallback<List<E>>() {

            public List<E> doInHibernate(Session arg0)
                    throws HibernateException, SQLException {
                // TODO Auto-generated method stub
                Criteria criteria = arg0.createCriteria(entityClass);
                //使用两个循环完成 了查询子句和排序子句的拼接
                if(commands != null && !commands.isEmpty())
                {
                    for(CriteriaCommand command : commands)
                    {
                        command.execute(criteria);
                    }
                }
                return criteria.list();
            }

        });

只要Service层对Web层暴露API,就可以在Web层使用了。上面说过,如果保证Web传递的参数无差异化(也就是说传递过来多少个参数,都没有关系,能够自动将参数解析,并且保证一个不少地传递给Service层),那有一个核心的对象就是request了,可以使用它作为参数,当然,request获取到的参数太原始,需要我们进行处理,所以,可以创建一个QueryFilter类,来封装request并且对request参数进行处理。时间有限,今天就先写到这里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值