1 package net
.aramzamzam
.commons
.hibernate
.utils
;
3 import java
.util
.ArrayList
;
4 import java
.util
.Arrays
;
5 import java
.util
.HashSet
;
9 import org
.hibernate
.Criteria
;
10 import org
.hibernate
.EntityMode
;
11 import org
.hibernate
.HibernateException
;
12 import org
.hibernate
.criterion
.CriteriaQuery
;
13 import org
.hibernate
.criterion
.Criterion
;
14 import org
.hibernate
.criterion
.MatchMode
;
15 import org
.hibernate
.criterion
.Restrictions
;
16 import org
.hibernate
.criterion
.SimpleExpression
;
17 import org
.hibernate
.engine
.TypedValue
;
18 import org
.hibernate
.persister
.entity
.EntityPersister
;
19 import org
.hibernate
.type
.AbstractComponentType
;
20 import org
.hibernate
.type
.Type
;
21 import org
.hibernate
.util
.StringHelper
;
24 * A copy of Hibernate's Example class, with modifications that allow you to
25 * include many-to-one and one-to-one associations in Query By Example (QBE)
28 * @author original code by Gavin King, modified by jkelly
29 * @see <a href="http://forum.hibernate.org/viewtopic.php?t=942872">Example and
30 * association class </a>
32 @SuppressWarnings("unchecked")
33 public class AssociationExample
implements Criterion
{
34 private static final long serialVersionUID
= 4909881342527136958L;
36 private final Object entity
;
37 private final Set excludedProperties
= new HashSet();
38 private PropertySelector selector
;
39 private boolean isLikeEnabled
;
40 private boolean isIgnoreCaseEnabled
;
41 private MatchMode matchMode
;
42 private boolean includeAssociations
= true;
45 * A strategy for choosing property values for inclusion in the query
49 public static interface PropertySelector
{
50 public boolean include(Object propertyValue
, String propertyName
,
54 private static final PropertySelector NOT_NULL
= new NotNullPropertySelector();
55 private static final PropertySelector ALL
= new AllPropertySelector();
56 private static final PropertySelector NOT_NULL_OR_ZERO
= new NotNullOrZeroPropertySelector();
58 static final class AllPropertySelector
implements PropertySelector
{
59 public boolean include(Object object
, String propertyName
, Type type
) {
64 static final class NotNullPropertySelector
implements PropertySelector
{
65 public boolean include(Object object
, String propertyName
, Type type
) {
66 return object
!= null;
70 static final class NotNullOrZeroPropertySelector
implements
72 public boolean include(Object object
, String propertyName
, Type type
) {
74 && (!(object
instanceof Number
) || ((Number
) object
)
80 * Set the property selector
82 public AssociationExample
setPropertySelector(PropertySelector selector
) {
83 this.selector
= selector
;
88 * Exclude zero-valued properties
90 public AssociationExample
excludeZeroes() {
91 setPropertySelector(NOT_NULL_OR_ZERO
);
96 * Don't exclude null or zero-valued properties
98 public AssociationExample
excludeNone() {
99 setPropertySelector(ALL
);
104 * Use the "like" operator for all string-valued properties
106 public AssociationExample
enableLike(MatchMode matchMode
) {
107 isLikeEnabled
= true;
108 this.matchMode
= matchMode
;
113 * Use the "like" operator for all string-valued properties
115 public AssociationExample
enableLike() {
116 return enableLike(MatchMode
.EXACT
);
120 * Ignore case for all string-valued properties
122 public AssociationExample
ignoreCase() {
123 isIgnoreCaseEnabled
= true;
128 * Exclude a particular named property
130 public AssociationExample
excludeProperty(String name
) {
131 excludedProperties
.add(name
);
136 * Create a new instance, which includes all non-null properties by default
139 * @return a new instance of <tt>Example</tt>
141 public static AssociationExample
create(Object entity
) {
143 throw new NullPointerException("null AssociationExample");
144 return new AssociationExample(entity
, NOT_NULL
);
147 protected AssociationExample(Object entity
, PropertySelector selector
) {
148 this.entity
= entity
;
149 this.selector
= selector
;
152 public String
toString() {
153 return "example (" + entity
+ ')';
156 private boolean isPropertyIncluded(Object value
, String name
, Type type
) {
157 return !excludedProperties
.contains(name
)
158 && selector
.include(value
, name
, type
)
159 && (!type
.isAssociationType() || (type
.isAssociationType()
160 && includeAssociations
&& !type
.isCollectionType()));
163 public String
toSqlString(Criteria criteria
, CriteriaQuery criteriaQuery
)
164 throws HibernateException
{
166 StringBuffer buf
= new StringBuffer().append('(');
167 EntityPersister meta
= criteriaQuery
.getFactory().getEntityPersister(
168 criteriaQuery
.getEntityName(criteria
));
169 List propertyNames
= new ArrayList();
170 propertyNames
.addAll(Arrays
.asList(meta
.getPropertyNames()));
172 List propertyTypes
= new ArrayList();
173 propertyTypes
.addAll(Arrays
.asList(meta
.getPropertyTypes()));
175 // TODO: get all properties, not just the fetched ones!
176 List propertyValues
= new ArrayList();
177 propertyValues
.addAll(Arrays
.asList(meta
.getPropertyValues(entity
,
178 getEntityMode(criteria
, criteriaQuery
))));
179 if (meta
.getIdentifierPropertyName() != null) {
180 propertyNames
.add(meta
.getIdentifierPropertyName());
181 propertyTypes
.add(meta
.getIdentifierType());
182 propertyValues
.add(meta
.getIdentifier(entity
, getEntityMode(
183 criteria
, criteriaQuery
)));
186 for (int i
= 0; i
< propertyNames
.size(); i
++) {
187 Object propertyValue
= propertyValues
.get(i
);
188 String propertyName
= (String
) propertyNames
.get(i
);
190 boolean isPropertyIncluded
= i
!= meta
.getVersionProperty()
191 && isPropertyIncluded(propertyValue
, propertyName
,
192 (Type
) propertyTypes
.get(i
));
193 if (isPropertyIncluded
) {
194 if (((Type
) propertyTypes
.get(i
)).isComponentType()) {
195 appendComponentCondition(propertyName
, propertyValue
,
196 (AbstractComponentType
) propertyTypes
.get(i
),
197 criteria
, criteriaQuery
, buf
);
199 appendPropertyCondition(propertyName
, propertyValue
,
200 criteria
, criteriaQuery
, buf
);
204 if (buf
.length() == 1)
205 buf
.append("1=1"); // yuck!
206 return buf
.append(')').toString();
209 private static final Object
[] TYPED_VALUES
= new TypedValue
[0];
211 public TypedValue
[] getTypedValues(Criteria criteria
,
212 CriteriaQuery criteriaQuery
) throws HibernateException
{
214 EntityPersister meta
= criteriaQuery
.getFactory().getEntityPersister(
215 criteriaQuery
.getEntityName(criteria
));
217 List propertyNames
= new ArrayList();
218 propertyNames
.addAll(Arrays
.asList(meta
.getPropertyNames()));
220 List propertyTypes
= new ArrayList();
221 propertyTypes
.addAll(Arrays
.asList(meta
.getPropertyTypes()));
223 // TODO: get all properties, not just the fetched ones!
224 List values
= new ArrayList();
225 values
.addAll(Arrays
.asList(meta
.getPropertyValues(entity
,
226 getEntityMode(criteria
, criteriaQuery
))));
227 if (meta
.getIdentifierPropertyName() != null) {
228 propertyNames
.add(meta
.getIdentifierPropertyName());
229 propertyTypes
.add(meta
.getIdentifierType());
230 values
.add(meta
.getIdentifier(entity
, getEntityMode(criteria
,
233 List list
= new ArrayList();
234 for (int i
= 0; i
< propertyNames
.size(); i
++) {
235 Object value
= values
.get(i
);
236 Type type
= (Type
) propertyTypes
.get(i
);
237 String name
= (String
) propertyNames
.get(i
);
239 boolean isPropertyIncluded
= i
!= meta
.getVersionProperty()
240 && isPropertyIncluded(value
, name
, type
);
242 if (isPropertyIncluded
) {
243 if (((Type
) propertyTypes
.get(i
)).isComponentType()) {
244 addComponentTypedValues(name
, value
,
245 (AbstractComponentType
) type
, list
, criteria
,
248 addPropertyTypedValue(value
, type
, list
);
252 return (TypedValue
[]) list
.toArray(TYPED_VALUES
);
255 private EntityMode
getEntityMode(Criteria criteria
,
256 CriteriaQuery criteriaQuery
) {
257 EntityPersister meta
= criteriaQuery
.getFactory().getEntityPersister(
258 criteriaQuery
.getEntityName(criteria
));
259 EntityMode result
= meta
.guessEntityMode(entity
);
260 if (result
== null) {
261 throw new ClassCastException(entity
.getClass().getName());
266 protected void addPropertyTypedValue(Object value
, Type type
, List list
) {
268 if (value
instanceof String
) {
269 String string
= (String
) value
;
270 if (isIgnoreCaseEnabled
)
271 string
= string
.toLowerCase();
273 string
= matchMode
.toMatchString(string
);
276 list
.add(new TypedValue(type
, value
, null));
280 protected void addComponentTypedValues(String path
, Object component
,
281 AbstractComponentType type
, List list
, Criteria criteria
,
282 CriteriaQuery criteriaQuery
) throws HibernateException
{
284 if (component
!= null) {
285 String
[] propertyNames
= type
.getPropertyNames();
286 Type
[] subtypes
= type
.getSubtypes();
287 Object
[] values
= type
.getPropertyValues(component
, getEntityMode(
288 criteria
, criteriaQuery
));
289 for (int i
= 0; i
< propertyNames
.length
; i
++) {
290 Object value
= values
[i
];
291 Type subtype
= subtypes
[i
];
292 String subpath
= StringHelper
.qualify(path
, propertyNames
[i
]);
293 if (isPropertyIncluded(value
, subpath
, subtype
)) {
294 if (subtype
.isComponentType()) {
295 addComponentTypedValues(subpath
, value
,
296 (AbstractComponentType
) subtype
, list
,
297 criteria
, criteriaQuery
);
299 addPropertyTypedValue(value
, subtype
, list
);
306 protected void appendPropertyCondition(String propertyName
,
307 Object propertyValue
, Criteria criteria
, CriteriaQuery cq
,
308 StringBuffer buf
) throws HibernateException
{
310 if (propertyValue
!= null) {
311 boolean isString
= propertyValue
instanceof String
;
312 SimpleExpression se
= (isLikeEnabled
&& isString
) ? Restrictions
313 .like(propertyName
, propertyValue
) : Restrictions
.eq(
314 propertyName
, propertyValue
);
315 crit
= (isIgnoreCaseEnabled
&& isString
) ? se
.ignoreCase() : se
;
317 crit
= Restrictions
.isNull(propertyName
);
319 String critCondition
= crit
.toSqlString(criteria
, cq
);
320 if (buf
.length() > 1 && critCondition
.trim().length() > 0)
322 buf
.append(critCondition
);
325 protected void appendComponentCondition(String path
, Object component
,
326 AbstractComponentType type
, Criteria criteria
,
327 CriteriaQuery criteriaQuery
, StringBuffer buf
)
328 throws HibernateException
{
330 if (component
!= null) {
331 String
[] propertyNames
= type
.getPropertyNames();
332 Object
[] values
= type
.getPropertyValues(component
, getEntityMode(
333 criteria
, criteriaQuery
));
334 Type
[] subtypes
= type
.getSubtypes();
335 for (int i
= 0; i
< propertyNames
.length
; i
++) {
336 String subpath
= StringHelper
.qualify(path
, propertyNames
[i
]);
337 Object value
= values
[i
];
338 if (isPropertyIncluded(value
, subpath
, subtypes
[i
])) {
339 Type subtype
= subtypes
[i
];
340 if (subtype
.isComponentType()) {
341 appendComponentCondition(subpath
, value
,
342 (AbstractComponentType
) subtype
, criteria
,
345 appendPropertyCondition(subpath
, value
, criteria
,
353 public boolean isIncludeAssociations() {
354 return includeAssociations
;
357 public void setIncludeAssociations(boolean includeAssociations
) {
358 this.includeAssociations
= includeAssociations
;