最近又遇到了关于“动态查询”的需求, 之前在使用JDBC查询数据库时我就被这个问题所困扰过, 最终我是采取拼接字符串的方式。 后来知道了那样子很容易遭受SQL注入, 于是我就改成传参数的形式, 但是思想还是拼接SQL语句。 如下我的源代码:
/**
* 捐款情况查询
*/
public List queryDonateInfo( final ContributeList contribute) {
final DateConventer conventer = new DateConventer();
List list = (List)this.getHibernateTemplate().execute(new HibernateCallback(){
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
List lstInfo;
Date date;
StringBuffer buffer =new StringBuffer("select f, d.name from Donated d inner join d.donateinfos f " +
"where d.id="+contribute.getDonatedid());
if(!"".equals(contribute.getDonatername().trim())){
buffer.append(" and f.donatename=:donateName ");
}
if(!"".equals(contribute.getStartTime().trim())){
date = conventer.transformDate(contribute.getStartTime().trim());
buffer.append(" and f.donatetime>=:startTime ");
}
if(!"".equals(contribute.getEndTime().trim())){
date = conventer.transformDate(contribute.getEndTime().trim());
buffer.append(" and f.donatetime<=:endTime ");
}
Query query = session.createQuery(buffer.toString());
if(!"".equals(contribute.getDonatername().trim())){
query.setParameter("donateName", contribute.getDonatername());
}
if(!"".equals(contribute.getStartTime().trim())){
date = conventer.transformDate(contribute.getStartTime().trim());
query.setParameter("startTime", date);
}
if(!"".equals(contribute.getEndTime().trim())){
date = conventer.transformDate(contribute.getEndTime().trim());
query.setParameter("endTime", date);
}
lstInfo = query.list();
Object[] obj = (Object[])lstInfo.get(0);
String str =(String) obj[1];
Donateinfo info =(Donateinfo) obj[0];
System.out.println("捐款金额: "+info.getMoney());
System.out.println("受捐者名字: "+str);
return (List)lstInfo;
}
}
);
return list;
}
虽然问题是解决了, 但是觉得这种硬编码很麻烦而且不利于推广。
————改进1: 传入一个参数数组
Query query = session.createQuery(hql);
for (int i = 0; i < para.length; i++) {
if (!para[i].equals(""))
query.setString(i, para[i]);
}
但是此种改进其实并不可行, 因为sql语句是动态变化的。
——————改进2: 使用QBC
Java codeCriteria criteria = session.createCriteria(Person.class).setFirstResult(from).setMaxResults(number);
if(null !=condition){
if(condition.getPersonCodeQuery() !=null &&!condition.getPersonCodeQuery().trim().equals("")){
criteria.add(Restrictions.like("personCode", condition.getPersonCodeQuery()+"%"));
}if(condition.getPersonIdBeginQuery() !=null &&!condition.getPersonIdBeginQuery().trim().equals("")){
criteria.add(Restrictions.ge("personID", new Integer(condition.getPersonIdBeginQuery())));
}if(condition.getPersonIdEndQuery() !=null &&!condition.getPersonIdEndQuery().trim().equals("")){
criteria.add(Restrictions.le("personID", new Integer(condition.getPersonIdEndQuery())));
}if(condition.getPersonSexQuery() != null &&!condition.getPersonSexQuery().trim().equals("")){
criteria.add(Restrictions.eq("personSex", condition.getPersonSexQuery()));
}
}
criteria1.list();
—————— 还是不好 (Criteria by Example的方式)。
最好要有某种框架就好了?
————改进3: 使用ibatis框架。 ibatis框架没有用过, 打算有时间再学习一下..... ....
—————改进4: 构筑自己的动态查询框架。 ^_^ ^_^
关于动态SQL创建问题。一些复杂的查询比较灵活,功能强大。但是一般做法都hardcode,即在代码中根据前台用户输入的条件在程序代码中构建sql语句。感觉非常繁琐,而且代码质量不高。
考虑:是否可以使用XML文件的形式来完成这项工作。
1)不管查询组合情况如何,查询组合项是确定的。
2)查询情况不外乎一下情况:
比较运算符 =(等于)>(大于) <(小于) >=(大于或等于) <=(小于或等于) <>(不等于)
逻辑运算符 ALL AND ANY BETWEEN EXISTS IN LIKE NOT OR SOME
在项目中设置这样一个资源文件
<组合项 动作名称=“actionName”>
<属性名称=“actionFormProperty” 列名=“colName” 运算符=“”>
</组合项>
就像struts配置文件一样映射成config对象一样,将这些配置文件映射成对象。当请求某个action时,取得对应的actionForm的名称。然后根据这个actionForm来取得对应的查询组合对象。
这样只要在上面映射对象的toSQL()方法就可以了。
——————————该觉实现这个太困难了, 不过还是有可能的吧。 加油!
——————————————-网上新发现: Hibernate3的DetachedCriteria支持————————————————————
动态查询就是查询条件不确定的查询,或者说查询条件是根据用户传入的某些参数来生成的,在查询之前就已经生成了查询条件,hibernate中的查询条件一般都需要在取得session后才构造,而hibernate动态查询在取得session之前就已经构造好了查询条件,只是将查询条件作为参数传入到查询方法中,因此也叫离线查询,动态查询的条件可以在web层中就已经构造好了,例如servlet中,构造查询条件的方式如下:
DetachedCriteria dc=DetachedCriteria.forClass(User.class);
String name=request.getParameter("name");//接收参数
dc.add(Restrictions.eq("name",name));//此为约束条件,等于是用eq,还可以根据需要设置成大于小于等等
List users=dc(dc);//调用实现方法
实现查询方法如下:
public List dc(DetachedCriteria dc){
Session s=HibernateUtil.getSession();//取得session
Criteria c=dc.getExecutableCriteria(s);
List list=c.list();
s.close();
return list;
}
上面的方法就可以完成动态条件查询
个人点评: ^_^ ^_^ 这种做法的好处在于:
查询语句的构造完全被搬离到web层实现,而业务层则只负责完成持久化和查询的封装即可,与查询条件构造完全解耦。
但是在WEB曾的条件构造还是回到了我上述论述的老问题。 ^_^ ^_^
——————————————————————————————————————————————————————
哪位朋友要是有好的方法, 要不吝赐教呀! ^_^ ^_^