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 public class AssociationExample
implements Criterion
{
33 private static final long serialVersionUID
= 4909881342527136958L;
35 private final Object entity
;
36 private final Set excludedProperties
= new HashSet();
37 private PropertySelector selector
;
38 private boolean isLikeEnabled
;
39 private boolean isIgnoreCaseEnabled
;
40 private MatchMode matchMode
;
41 private boolean includeAssociations
= true;
44 * A strategy for choosing property values for inclusion in the query
48 public static interface PropertySelector
{
49 public boolean include(Object propertyValue
, String propertyName
,
53 private static final PropertySelector NOT_NULL
= new NotNullPropertySelector();
54 private static final PropertySelector ALL
= new AllPropertySelector();
55 private static final PropertySelector NOT_NULL_OR_ZERO
= new NotNullOrZeroPropertySelector();
57 static final class AllPropertySelector
implements PropertySelector
{
58 public boolean include(Object object
, String propertyName
, Type type
) {
63 static final class NotNullPropertySelector
implements PropertySelector
{
64 public boolean include(Object object
, String propertyName
, Type type
) {
65 return object
!= null;
69 static final class NotNullOrZeroPropertySelector
implements
71 public boolean include(Object object
, String propertyName
, Type type
) {
73 && (!(object
instanceof Number
) || ((Number
) object
)
79 * Set the property selector
81 public AssociationExample
setPropertySelector(PropertySelector selector
) {
82 this.selector
= selector
;
87 * Exclude zero-valued properties
89 public AssociationExample
excludeZeroes() {
90 setPropertySelector(NOT_NULL_OR_ZERO
);
95 * Don't exclude null or zero-valued properties
97 public AssociationExample
excludeNone() {
98 setPropertySelector(ALL
);
103 * Use the "like" operator for all string-valued properties
105 public AssociationExample
enableLike(MatchMode matchMode
) {
106 isLikeEnabled
= true;
107 this.matchMode
= matchMode
;
112 * Use the "like" operator for all string-valued properties
114 public AssociationExample
enableLike() {
115 return enableLike(MatchMode
.EXACT
);
119 * Ignore case for all string-valued properties
121 public AssociationExample
ignoreCase() {
122 isIgnoreCaseEnabled
= true;
127 * Exclude a particular named property
129 public AssociationExample
excludeProperty(String name
) {
130 excludedProperties
.add(name
);
135 * Create a new instance, which includes all non-null properties by default
138 * @return a new instance of <tt>Example</tt>
140 public static AssociationExample
create(Object entity
) {
142 throw new NullPointerException("null AssociationExample");
143 return new AssociationExample(entity
, NOT_NULL
);
146 protected AssociationExample(Object entity
, PropertySelector selector
) {
147 this.entity
= entity
;
148 this.selector
= selector
;
151 public String
toString() {
152 return "example (" + entity
+ ')';
155 private boolean isPropertyIncluded(Object value
, String name
, Type type
) {
156 return !excludedProperties
.contains(name
)
157 && selector
.include(value
, name
, type
)
158 && (!type
.isAssociationType() || (type
.isAssociationType()
159 && includeAssociations
&& !type
.isCollectionType()));
162 public String
toSqlString(Criteria criteria
, CriteriaQuery criteriaQuery
)
163 throws HibernateException
{
165 StringBuffer buf
= new StringBuffer().append('(');
166 EntityPersister meta
= criteriaQuery
.getFactory().getEntityPersister(
167 criteriaQuery
.getEntityName(criteria
));
168 List propertyNames
= new ArrayList();
169 propertyNames
.addAll(Arrays
.asList(meta
.getPropertyNames()));
171 List propertyTypes
= new ArrayList();
172 propertyTypes
.addAll(Arrays
.asList(meta
.getPropertyTypes()));
174 // TODO: get all properties, not just the fetched ones!
175 List propertyValues
= new ArrayList();
176 propertyValues
.addAll(Arrays
.asList(meta
.getPropertyValues(entity
,
177 getEntityMode(criteria
, criteriaQuery
))));
178 if (meta
.getIdentifierPropertyName() != null) {
179 propertyNames
.add(meta
.getIdentifierPropertyName());
180 propertyTypes
.add(meta
.getIdentifierType());
181 propertyValues
.add(meta
.getIdentifier(entity
, getEntityMode(
182 criteria
, criteriaQuery
)));
185 for (int i
= 0; i
< propertyNames
.size(); i
++) {
186 Object propertyValue
= propertyValues
.get(i
);
187 String propertyName
= (String
) propertyNames
.get(i
);
189 boolean isPropertyIncluded
= i
!= meta
.getVersionProperty()
190 && isPropertyIncluded(propertyValue
, propertyName
,
191 (Type
) propertyTypes
.get(i
));
192 if (isPropertyIncluded
) {
193 if (((Type
) propertyTypes
.get(i
)).isComponentType()) {
194 appendComponentCondition(propertyName
, propertyValue
,
195 (AbstractComponentType
) propertyTypes
.get(i
),
196 criteria
, criteriaQuery
, buf
);
198 appendPropertyCondition(propertyName
, propertyValue
,
199 criteria
, criteriaQuery
, buf
);
203 if (buf
.length() == 1)
204 buf
.append("1=1"); // yuck!
205 return buf
.append(')').toString();
208 private static final Object
[] TYPED_VALUES
= new TypedValue
[0];
210 public TypedValue
[] getTypedValues(Criteria criteria
,
211 CriteriaQuery criteriaQuery
) throws HibernateException
{
213 EntityPersister meta
= criteriaQuery
.getFactory().getEntityPersister(
214 criteriaQuery
.getEntityName(criteria
));
216 List propertyNames
= new ArrayList();
217 propertyNames
.addAll(Arrays
.asList(meta
.getPropertyNames()));
219 List propertyTypes
= new ArrayList();
220 propertyTypes
.addAll(Arrays
.asList(meta
.getPropertyTypes()));
222 // TODO: get all properties, not just the fetched ones!
223 List values
= new ArrayList();
224 values
.addAll(Arrays
.asList(meta
.getPropertyValues(entity
,
225 getEntityMode(criteria
, criteriaQuery
))));
226 if (meta
.getIdentifierPropertyName() != null) {
227 propertyNames
.add(meta
.getIdentifierPropertyName());
228 propertyTypes
.add(meta
.getIdentifierType());
229 values
.add(meta
.getIdentifier(entity
, getEntityMode(criteria
,
232 List list
= new ArrayList();
233 for (int i
= 0; i
< propertyNames
.size(); i
++) {
234 Object value
= values
.get(i
);
235 Type type
= (Type
) propertyTypes
.get(i
);
236 String name
= (String
) propertyNames
.get(i
);
238 boolean isPropertyIncluded
= i
!= meta
.getVersionProperty()
239 && isPropertyIncluded(value
, name
, type
);
241 if (isPropertyIncluded
) {
242 if (((Type
) propertyTypes
.get(i
)).isComponentType()) {
243 addComponentTypedValues(name
, value
,
244 (AbstractComponentType
) type
, list
, criteria
,
247 addPropertyTypedValue(value
, type
, list
);
251 return (TypedValue
[]) list
.toArray(TYPED_VALUES
);
254 private EntityMode
getEntityMode(Criteria criteria
,
255 CriteriaQuery criteriaQuery
) {
256 EntityPersister meta
= criteriaQuery
.getFactory().getEntityPersister(
257 criteriaQuery
.getEntityName(criteria
));
258 EntityMode result
= meta
.guessEntityMode(entity
);
259 if (result
== null) {
260 throw new ClassCastException(entity
.getClass().getName());
265 protected void addPropertyTypedValue(Object value
, Type type
, List list
) {
267 if (value
instanceof String
) {
268 String string
= (String
) value
;
269 if (isIgnoreCaseEnabled
)
270 string
= string
.toLowerCase();
272 string
= matchMode
.toMatchString(string
);
275 list
.add(new TypedValue(type
, value
, null));
279 protected void addComponentTypedValues(String path
, Object component
,
280 AbstractComponentType type
, List list
, Criteria criteria
,
281 CriteriaQuery criteriaQuery
) throws HibernateException
{
283 if (component
!= null) {
284 String
[] propertyNames
= type
.getPropertyNames();
285 Type
[] subtypes
= type
.getSubtypes();
286 Object
[] values
= type
.getPropertyValues(component
, getEntityMode(
287 criteria
, criteriaQuery
));
288 for (int i
= 0; i
< propertyNames
.length
; i
++) {
289 Object value
= values
[i
];
290 Type subtype
= subtypes
[i
];
291 String subpath
= StringHelper
.qualify(path
, propertyNames
[i
]);
292 if (isPropertyIncluded(value
, subpath
, subtype
)) {
293 if (subtype
.isComponentType()) {
294 addComponentTypedValues(subpath
, value
,
295 (AbstractComponentType
) subtype
, list
,
296 criteria
, criteriaQuery
);
298 addPropertyTypedValue(value
, subtype
, list
);
305 protected void appendPropertyCondition(String propertyName
,
306 Object propertyValue
, Criteria criteria
, CriteriaQuery cq
,
307 StringBuffer buf
) throws HibernateException
{
309 if (propertyValue
!= null) {
310 boolean isString
= propertyValue
instanceof String
;
311 SimpleExpression se
= (isLikeEnabled
&& isString
) ? Restrictions
312 .like(propertyName
, propertyValue
) : Restrictions
.eq(
313 propertyName
, propertyValue
);
314 crit
= (isIgnoreCaseEnabled
&& isString
) ? se
.ignoreCase() : se
;
316 crit
= Restrictions
.isNull(propertyName
);
318 String critCondition
= crit
.toSqlString(criteria
, cq
);
319 if (buf
.length() > 1 && critCondition
.trim().length() > 0)
321 buf
.append(critCondition
);
324 protected void appendComponentCondition(String path
, Object component
,
325 AbstractComponentType type
, Criteria criteria
,
326 CriteriaQuery criteriaQuery
, StringBuffer buf
)
327 throws HibernateException
{
329 if (component
!= null) {
330 String
[] propertyNames
= type
.getPropertyNames();
331 Object
[] values
= type
.getPropertyValues(component
, getEntityMode(
332 criteria
, criteriaQuery
));
333 Type
[] subtypes
= type
.getSubtypes();
334 for (int i
= 0; i
< propertyNames
.length
; i
++) {
335 String subpath
= StringHelper
.qualify(path
, propertyNames
[i
]);
336 Object value
= values
[i
];
337 if (isPropertyIncluded(value
, subpath
, subtypes
[i
])) {
338 Type subtype
= subtypes
[i
];
339 if (subtype
.isComponentType()) {
340 appendComponentCondition(subpath
, value
,
341 (AbstractComponentType
) subtype
, criteria
,
344 appendPropertyCondition(subpath
, value
, criteria
,
352 public boolean isIncludeAssociations() {
353 return includeAssociations
;
356 public void setIncludeAssociations(boolean includeAssociations
) {
357 this.includeAssociations
= includeAssociations
;