Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Here is my generic Criteria filtering method.</p> <p>Properties according to bean conventions have following form <code>foo.bar.name</code>.<br> With Criteria API a tree can be build from a given filtering map and Restrictions can be added. One special case I observed during testing is that filtering on the identifier property does not needs a new subcriteria since this property is already fetched.</p> <pre><code>/** * 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&lt;?&gt; type, final String identifierPropertyName, final Map&lt;String, Criterion&gt; filters) { final DetachedCriteria criteria = DetachedCriteria.forClass(type); // add restrictions using tree final TreeNode&lt;Entry&lt;String, Criterion&gt;&gt; rootNode = HibernateUtils2.createTree(filters.entrySet(), identifierPropertyName); final Iterator&lt;TreeNode&lt;Entry&lt;String, Criterion&gt;&gt;&gt; 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 &lt;T&gt; * * @param filters * @param identifierPropertyName Property name which is merged with its * parent property. Example: &lt;b&gt;user.id&lt;/b&gt; is treated as single * property. * @return */ public static &lt;T extends Object&gt; TreeNode&lt;Entry&lt;String, T&gt;&gt; createTree(final Set&lt;Entry&lt;String, T&gt;&gt; filters, final String identifierPropertyName) { final Iterator&lt;Entry&lt;String, Object&gt;&gt; it = filters.iterator(); /* * create key property tree for Entity properties */ final TreeNode&lt;Entry&lt;String, Object&gt;&gt; rootNode = new TreeNode&lt;Entry&lt;String, Object&gt;&gt;( new SimpleEntry&lt;String, Object&gt;("root", null)); while (it.hasNext()) { final Entry&lt;String, Object&gt; 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 &gt; 1 &amp;&amp; props[0].equals("this")) { props[0] = "this." + props[1]; props = ArrayUtils.remove(props, 1); } } else props = new String[] { key }; TreeNode&lt;Entry&lt;String, Object&gt;&gt; currNode = rootNode; // create nested criteria for (int i = 0; i &lt; props.length; i++) { Object valueAdd; // only leaf needs value if (i != props.length - 1) valueAdd = null; else valueAdd = entry.getValue(); final TreeNode&lt;Entry&lt;String, Object&gt;&gt; childTempNode = new TreeNode&lt;Entry&lt;String, Object&gt;&gt;( new SimpleEntry&lt;String, Object&gt;(props[i], valueAdd)); // try to get the real node TreeNode&lt;Entry&lt;String, Object&gt;&gt; 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&lt;Entry&lt;String, Criterion&gt;&gt; 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&lt;TreeNode&lt;Entry&lt;String, Criterion&gt;&gt;&gt; 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&lt;T&gt; { private final T element; private final Set&lt;TreeNode&lt;T&gt;&gt; childrens; public TreeNode(final T element) { if (element == null) throw new IllegalArgumentException("Element cannot be null"); this.element = element; this.childrens = new HashSet&lt;TreeNode&lt;T&gt;&gt;(); } public void addChildren(final TreeNode&lt;T&gt; 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&lt;T&gt; getChildren(final TreeNode&lt;T&gt; children) { final Iterator&lt;TreeNode&lt;T&gt;&gt; it = this.childrens.iterator(); TreeNode&lt;T&gt; next = null; while (it.hasNext()) { next = it.next(); if (next.equals(children)) return next; } return null; } public T getElement() { return this.element; } public Set&lt;TreeNode&lt;T&gt;&gt; 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 &amp;&amp; obj instanceof TreeNode) { final TreeNode&lt;?&gt; treeNode = (TreeNode&lt;?&gt;) 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; } } </code></pre> <p>Hope this helps you. Or have a look at the generic dao project you mentioned. I knew about this project and checked it out but never downloaded it.</p> <p>Using this approach a query can be created very simple like following:</p> <pre><code>Map&lt;String, Object&gt; filters = new HashMap&lt;String, Object&gt;(); filters.put("foo.bar.name", Restrictions.like("name", "peter")); filters.put("foo.test.id", Restrictions.eq("id", 2)); List&lt;Class&gt; data = HibernateUtils.createDetachedCriteria(Class, "get identifier from sessionFactory", filters).getExecutableCriteria(session).list(); </code></pre> <p><em>This odd approach to add the property name as the key and into the Restrictions is related to the fact that Restrictions have no getter and setter for property names.</em></p> <p><strong>Custom filtering</strong></p> <p>My real application uses similar code which is not restricted to <code>Criterion</code> class only. For my web tier I created custom filters which are equivalent to Restrictions api but the client does not needs hibernate jar's anymore. </p> <p><strong>Edit</strong></p> <p>Generic filtering across non-entities such as component's and composite-id's is not supported. It can easily be extended with help of <code>ClassMetadata</code> to support them without need for reflection. If code is needed i can provide it.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload