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

来源:互联网 发布:魔百和网络机顶盒 编辑:程序博客网 时间:2024/05/20 03:08

使用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
原创粉丝点击