这是我的通用标准过滤方法。
根据 bean 约定的属性具有以下形式foo.bar.name
。
使用 Criteria API,可以从给定的过滤映射构建树,并且可以添加限制。我在测试期间观察到的一个特殊情况是,对标识符属性进行过滤不需要新的子标准,因为该属性已经被获取。
/**
* Creates a detached criteria from the given Type and given map of filters.
*
* @param type Target type the Criteria is build for.
* @param identifierPropertyName If provided (not null) the identifier
* property name can be identified for the given type to simplify
* the queries if the identifier property is the only property
* used on the parent no subqueries are needed.
* @param filters
*
* @see #createTree(Set, String)
* @see #addRestrictions(DetachedCriteria, TreeNode)
*
* @return
*/
public static DetachedCriteria createDetachedCriteria(final Class<?> type, final String identifierPropertyName,
final Map<String, Criterion> filters)
{
final DetachedCriteria criteria = DetachedCriteria.forClass(type);
// add restrictions using tree
final TreeNode<Entry<String, Criterion>> rootNode = HibernateUtils2.createTree(filters.entrySet(),
identifierPropertyName);
final Iterator<TreeNode<Entry<String, Criterion>>> it = rootNode.getChildren().iterator();
while (it.hasNext())
HibernateUtils.addRestrictions(criteria, it.next());
return criteria;
}
/**
* Creates a Tree from the given Set using a fictional root TreeNode.
*
* @param <T>
*
* @param filters
* @param identifierPropertyName Property name which is merged with its
* parent property. Example: <b>user.id</b> is treated as single
* property.
* @return
*/
public static <T extends Object> TreeNode<Entry<String, T>> createTree(final Set<Entry<String, T>> filters,
final String identifierPropertyName)
{
final Iterator<Entry<String, Object>> it = filters.iterator();
/*
* create key property tree for Entity properties
*/
final TreeNode<Entry<String, Object>> rootNode = new TreeNode<Entry<String, Object>>(
new SimpleEntry<String, Object>("root", null));
while (it.hasNext())
{
final Entry<String, Object> entry = it.next();
// foo.bar.name
final String key = entry.getKey();
String[] props;
/*
* check if we have a nested hierarchy
*/
if (key.contains("."))
{
props = key.split("\\.");
// check for identifier since identifier property name does not
// need new subcriteria
if (!StringUtils.isBlank(identifierPropertyName))
{
int propsTempLength = props.length - 1;
if (props[propsTempLength].equals(identifierPropertyName))
{
props = Arrays.copyOf(props, propsTempLength);
propsTempLength--;
props[propsTempLength] = props[propsTempLength] + "." + identifierPropertyName;
}
}
// check for "this" identifier of beginning, which needs to be
// added for projections because of hibernate not recognizing it
if (props.length > 1 && props[0].equals("this"))
{
props[0] = "this." + props[1];
props = ArrayUtils.remove(props, 1);
}
}
else
props = new String[]
{
key
};
TreeNode<Entry<String, Object>> currNode = rootNode;
// create nested criteria
for (int i = 0; i < props.length; i++)
{
Object valueAdd;
// only leaf needs value
if (i != props.length - 1)
valueAdd = null;
else
valueAdd = entry.getValue();
final TreeNode<Entry<String, Object>> childTempNode = new TreeNode<Entry<String, Object>>(
new SimpleEntry<String, Object>(props[i], valueAdd));
// try to get the real node
TreeNode<Entry<String, Object>> childNode = currNode.getChild(childTempNode.getElement());
// check if we already have a unique node
if (childNode == null)
{
childNode = childTempNode;
// add new child to set if its a new node
currNode.addChild(childNode);
}
currNode = childNode;
}
}
return rootNode;
}
/**
* Recursively adds the given Restriction's wrapped in the given TreeNode to
* the Criteria.
*
* @param criteria
* @param treeNode
*/
public static void addRestrictions(final DetachedCriteria criteria,
final TreeNode<Entry<String, Criterion>> treeNode)
{
// if we have a leaf simply add restriction
if (treeNode.getChildren().size() == 0)
criteria.add(treeNode.getElement().getValue());
else
{
// create new sub Criteria and iterate children's
final DetachedCriteria subCriteria = criteria.createCriteria(treeNode.getElement().getKey());
final Iterator<TreeNode<Entry<String, Criterion>>> it = treeNode.getChildren().iterator();
while (it.hasNext())
HibernateUtils.addRestrictions(subCriteria, it.next());
}
}
/*
* Utility classes
*/
/**
* Generic TreeNode implementation with a Set to hold its children to only allow
* unique children's.
*/
public class TreeNode<T>
{
private final T element;
private final Set<TreeNode<T>> childrens;
public TreeNode(final T element)
{
if (element == null)
throw new IllegalArgumentException("Element cannot be null");
this.element = element;
this.childrens = new HashSet<TreeNode<T>>();
}
public void addChildren(final TreeNode<T> children)
{
this.childrens.add(children);
}
/**
* Retrieves the children which equals the given one.
*
* @param children
* @return If no children equals the given one returns null.
*/
public TreeNode<T> getChildren(final TreeNode<T> children)
{
final Iterator<TreeNode<T>> it = this.childrens.iterator();
TreeNode<T> next = null;
while (it.hasNext())
{
next = it.next();
if (next.equals(children))
return next;
}
return null;
}
public T getElement()
{
return this.element;
}
public Set<TreeNode<T>> getChildrens()
{
return this.childrens;
}
/**
* Checks if the element of this instance equals the one of the given
* Object.
*/
@Override
public boolean equals(final Object obj)
{
if (this == obj)
return true;
if (obj != null && obj instanceof TreeNode)
{
final TreeNode<?> treeNode = (TreeNode<?>) obj;
return this.element.equals(treeNode.element);
}
else
return false;
}
@Override
public int hashCode()
{
int hash = 1;
hash = hash * 17 + this.element.hashCode();
return hash;
}
}
希望这对您有所帮助。或者看看你提到的通用 dao 项目。我知道这个项目并检查了它,但从未下载过。
使用这种方法,可以非常简单地创建查询,如下所示:
Map<String, Object> filters = new HashMap<String, Object>();
filters.put("foo.bar.name", Restrictions.like("name", "peter"));
filters.put("foo.test.id", Restrictions.eq("id", 2));
List<Class> data = HibernateUtils.createDetachedCriteria(Class, "get identifier from sessionFactory", filters).getExecutableCriteria(session).list();
这种将属性名称作为键添加到 Restrictions 中的奇怪方法与 Restrictions 没有属性名称的 getter 和 setter 的事实有关。
自定义过滤
我的真实应用程序使用类似的代码,不仅限于Criterion
类。对于我的 Web 层,我创建了与 Restrictions api 等效的自定义过滤器,但客户端不再需要休眠 jar。
编辑
不支持跨非实体(如组件和复合 ID)的通用过滤。ClassMetadata
它可以在无需反思的情况下轻松扩展以支持它们。如果需要代码,我可以提供。