1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.oness.common.model.dao.hibernate;
17
18 import java.io.Serializable;
19 import java.lang.reflect.InvocationTargetException;
20 import java.util.Collection;
21 import java.util.List;
22
23 import net.sf.hibernate.Criteria;
24 import net.sf.hibernate.Hibernate;
25 import net.sf.hibernate.HibernateException;
26 import net.sf.hibernate.MappingException;
27 import net.sf.hibernate.Session;
28 import net.sf.hibernate.expression.Expression;
29 import net.sf.hibernate.expression.MatchMode;
30 import net.sf.hibernate.metadata.ClassMetadata;
31 import net.sf.hibernate.metadata.CollectionMetadata;
32 import net.sf.hibernate.type.CompositeCustomType;
33 import net.sf.hibernate.type.PersistentCollectionType;
34 import net.sf.hibernate.type.Type;
35 import net.sf.oness.common.model.bo.AuditableBusinessObject;
36 import net.sf.oness.common.model.dao.Dao;
37 import net.sf.oness.common.model.temporal.DateRange;
38 import net.sf.oness.common.model.temporal.hibernate.DateRangeType;
39 import net.sf.oness.common.model.util.PaginatedList;
40
41 import org.apache.commons.beanutils.BeanUtils;
42 import org.springframework.orm.ObjectRetrievalFailureException;
43 import org.springframework.orm.hibernate.HibernateCallback;
44
45 /***
46 * Dao that uses Hibernate for persistence
47 *
48 * @author Carlos Sanchez
49 * @version $Revision: 1.18 $
50 */
51 public class HibernateDao extends HibernateDaoSupport implements Dao {
52
53 private static final int INITIALIZE_LAZY = 1, NULLIFY_LAZY = 2;
54
55 public static final String WHERE_NOT_DELETED = "where this.transactionTime.end is null";
56
57 private Class theClass;
58
59 /***
60 * Create a HibernateDao for the class specified
61 *
62 * @param className
63 * @throws HibernateException
64 */
65 public HibernateDao(Class theClass) {
66 this.theClass = theClass;
67 }
68
69 /***
70 * Create a HibernateDao for the class with name specified by className
71 *
72 * @param className
73 * @throws ClassNotFoundException
74 * @throws HibernateException
75 */
76 public HibernateDao(String className) throws ClassNotFoundException {
77 this.theClass = Class.forName(className);
78 }
79
80 /***
81 * Return the persistent instance with the given identifier, assuming that
82 * the instance exists.
83 *
84 * @param id
85 * a valid identifier of an existing persistent instance of the
86 * class
87 * @return
88 */
89 protected AuditableBusinessObject load(Serializable id) {
90 return (AuditableBusinessObject) getHibernateTemplate().load(theClass,
91 id);
92 }
93
94 /***
95 * @see net.sf.oness.common.model.dao.AuditableDao#findById(java.io.Serializable)
96 */
97 public AuditableBusinessObject findById(Serializable id) {
98 return (AuditableBusinessObject) super.loadAndClone(theClass, id);
99 }
100
101 /***
102 * @see net.sf.oness.common.model.dao.AuditableDao#findById(java.util.Collection)
103 */
104 public List findById(Collection ids) {
105 return (List) getHibernateTemplate().execute(getFindByIdCallback(ids));
106 }
107
108 /***
109 * @see net.sf.oness.common.model.dao.Dao#findWithDetails(java.io.Serializable)
110 */
111 public AuditableBusinessObject findWithDetails(Serializable id) {
112 AuditableBusinessObject o = load(id);
113 return (AuditableBusinessObject) getHibernateTemplate().execute(
114 getProcessCollectionsCallback(o, INITIALIZE_LAZY));
115 }
116
117 /***
118 * Get a new HibernateCallback to do some operation on lazy initialized
119 * collections (initialize or set to null).
120 *
121 * @param object
122 * the persistent object
123 * @param operation
124 * the operation to do with collection properties
125 * (INITIALIZE_LAZY or NULLIFY_LAZY)
126 * @return a new non persistent object with collections processed
127 */
128 private HibernateCallback getProcessCollectionsCallback(
129 final AuditableBusinessObject object, final int operation) {
130
131 return new HibernateCallback() {
132
133 public Object doInHibernate(Session session)
134 throws HibernateException {
135
136 ClassMetadata classMetadata = getSessionFactory()
137 .getClassMetadata(object.getClass());
138
139
140 Type[] propertyTypes = classMetadata.getPropertyTypes();
141 String[] propertyNames = classMetadata.getPropertyNames();
142
143
144 AuditableBusinessObject dest = findById(object.getId());
145
146
147 for (int i = 0; i < propertyTypes.length; i++) {
148
149 if (!propertyTypes[i].isPersistentCollectionType())
150 continue;
151
152 CollectionMetadata collectionMetadata = getSessionFactory()
153 .getCollectionMetadata(
154 ((PersistentCollectionType) propertyTypes[i])
155 .getRole());
156
157
158 if (collectionMetadata.isLazy()) {
159 switch (operation) {
160 case (INITIALIZE_LAZY):
161 Collection c = filterNotDeleted((Collection) classMetadata
162 .getPropertyValue(object, propertyNames[i]));
163 classMetadata.setPropertyValue(dest,
164 propertyNames[i], c);
165 break;
166 case (NULLIFY_LAZY):
167 classMetadata.setPropertyValue(dest,
168 propertyNames[i], null);
169 break;
170 default:
171 throw new IllegalArgumentException(
172 "Unknown operation");
173 }
174 }
175 }
176 return dest;
177 }
178 };
179 }
180
181 /***
182 * Get a new HibernateCallback for loading objects by a Collection of ids
183 *
184 * @param ids
185 * collection of ids
186 * @return
187 */
188 private HibernateCallback getFindByIdCallback(final Collection ids) {
189
190 return new HibernateCallback() {
191
192 public Object doInHibernate(Session session)
193 throws HibernateException {
194
195 Criteria criteria = session.createCriteria(theClass);
196
197 ClassMetadata classMetadata = getSessionFactory()
198 .getClassMetadata(theClass);
199
200 String idPropertyName = classMetadata
201 .getIdentifierPropertyName();
202 criteria.add(Expression.in(idPropertyName, ids));
203
204 return criteria.list();
205 }
206 };
207 }
208
209 /***
210 * @see net.sf.oness.common.model.dao.AuditableDao#create(net.sf.oness.common.model.bo.Auditable)
211 */
212 public AuditableBusinessObject create(final AuditableBusinessObject bo) {
213 bo.setId(null);
214 getHibernateTemplate().save(bo);
215 return findById(bo.getId());
216 }
217
218 /***
219 * @see net.sf.oness.common.model.dao.AuditableDao#update(net.sf.oness.common.model.bo.Auditable)
220 */
221 public AuditableBusinessObject update(AuditableBusinessObject bo) {
222 Long id = bo.getId();
223 Object o = getHibernateTemplate().load(theClass, id);
224 try {
225 BeanUtils.copyProperties(o, bo);
226 } catch (IllegalAccessException e) {
227 throw new ObjectRetrievalFailureException(theClass, id, e
228 .getMessage(), e);
229 } catch (InvocationTargetException e) {
230 throw new ObjectRetrievalFailureException(theClass, id, e
231 .getMessage(), e);
232 }
233 getHibernateTemplate().update(o);
234 return findById(bo.getId());
235 }
236
237 /***
238 * @see net.sf.oness.common.model.dao.FinderDao#find(net.sf.oness.common.model.bo.Auditable,
239 * int, int)
240 */
241 public PaginatedList find(AuditableBusinessObject bo, int firstElement,
242 int maxElements) {
243 return (PaginatedList) getHibernateTemplate().execute(
244 getFindByValueCallback(bo, firstElement, maxElements));
245 }
246
247 /***
248 * Get a new HibernateCallback for finding objects by a bean property
249 * values, paginating the results. Properties with null values and
250 * collections are ignored. If the property is mapped as String find a
251 * partial match, otherwise find by exact match.
252 *
253 * @todo Use Criteria.count() when available in next Hibernate versions
254 *
255 * @param bean
256 * bean with the values of the parameters
257 * @param firstElement
258 * the first result, numbered from 0
259 * @param count
260 * the maximum number of results
261 * @return
262 */
263 private HibernateCallback getFindByValueCallback(final Object bean,
264 final int firstElement, final int count) {
265
266 return new HibernateCallback() {
267
268 public Object doInHibernate(Session session)
269 throws HibernateException {
270 Criteria criteria = session.createCriteria(bean.getClass());
271
272 ClassMetadata classMetadata = getSessionFactory()
273 .getClassMetadata(bean.getClass());
274
275 if (classMetadata == null)
276 throw new MappingException("No persister for: "
277 + bean.getClass().getName());
278
279
280 Type[] propertyTypes = classMetadata.getPropertyTypes();
281 String[] propertyNames = classMetadata.getPropertyNames();
282
283
284 for (int i = 0; i < propertyNames.length; i++) {
285 String name = propertyNames[i];
286 Object value = classMetadata.getPropertyValue(bean, name);
287
288 if (value == null)
289 continue;
290
291
292 if (propertyTypes[i].isPersistentCollectionType())
293 continue;
294
295 Type type = classMetadata.getPropertyType(name);
296
297 if (type.equals(Hibernate.STRING))
298 criteria.add(Expression.ilike(name, value.toString(),
299 MatchMode.ANYWHERE));
300
301
302
303
304 else if (type.equals(Hibernate.custom(DateRangeType.class))) {
305 CompositeCustomType customType = (CompositeCustomType) type;
306 String[] names = customType.getPropertyNames();
307 DateRange dr = (DateRange) value;
308 Object values[] = customType.getPropertyValues(value);
309 if (dr.getStart() != null)
310 if (values[0] != null)
311 criteria.add(Expression.eq(name + "."
312 + names[0], values[0]));
313 else
314 criteria.add(Expression.isNull(name + "."
315 + names[0]));
316 if (dr.getEnd() != null)
317 if (values[1] != null)
318 criteria.add(Expression.eq(name + "."
319 + names[1], values[1]));
320 else
321 criteria.add(Expression.isNull(name + "."
322 + names[1]));
323 }
324
325 else
326 criteria.add(Expression.eq(name, value));
327 }
328
329
330
331
332
333 int size = criteria.list().size();
334
335 List list = criteria.setFirstResult(firstElement)
336 .setMaxResults(count).list();
337 return new PaginatedList(list, firstElement, count, size);
338 }
339 };
340 }
341
342 /***
343 * Not implemented
344 *
345 * @throws UnsupportedOperationException
346 * @see net.sf.oness.common.model.dao.AuditableDao#delete(java.io.Serializable)
347 */
348 public void delete(Serializable id) {
349 throw new UnsupportedOperationException();
350 }
351
352 /***
353 * Return all persistent instances of the given class
354 *
355 * @return List of objects
356 */
357 public List findAll() {
358 return getHibernateTemplate().loadAll(theClass);
359 }
360
361 /***
362 * Filter a persistent collection, getting not deleted values. Allow
363 * efficient access to very large lazy collections. (Executing the filter
364 * does not initialize the collection.)
365 *
366 * @param collection
367 * a persistent collection to filter
368 * @return Collection the resulting collection
369 *
370 * @see Session.filter()
371 */
372 public Collection filterNotDeleted(Collection collection) {
373 return filter(collection, WHERE_NOT_DELETED);
374 }
375
376 }