1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.geekologue.md4j.dao.hibernate;
18
19 import java.io.Serializable;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import javax.naming.InitialContext;
27 import org.apache.log4j.Logger;
28 import org.hibernate.Criteria;
29 import org.hibernate.Session;
30 import org.hibernate.SessionFactory;
31 import org.hibernate.criterion.Example;
32 import org.hibernate.criterion.Projections;
33 import org.hibernate.criterion.Property;
34 import org.hibernate.criterion.Restrictions;
35 import com.geekologue.md4j.config.Configuration;
36 import com.geekologue.md4j.dao.AbstractDAO;
37 import com.geekologue.md4j.dao.DataAccessException;
38 import com.geekologue.md4j.dao.Order;
39 import com.geekologue.md4j.dao.Page;
40 import com.geekologue.md4j.util.Md4jException;
41
42 /***
43 * Hibernate based DAO
44 *
45 * @author manos
46 *
47 */
48 public abstract class AbstractHbmDAO extends AbstractDAO {
49 private static Logger log = Logger.getLogger(AbstractHbmDAO.class);
50
51 protected SessionFactory sf;
52
53 /***
54 * @param clazz
55 */
56 public AbstractHbmDAO(Class clazz, String idName) {
57 super(clazz, idName);
58 try {
59 InitialContext inctx = new InitialContext();
60 String sfName = (String) (Configuration.getInstance())
61 .getProperty("session_factory_name");
62 if (sfName == null) {
63 sfName = (String) (Configuration.getInstance())
64 .getProperty("hibernate.session_factory_name");
65 }
66 this.sf = (SessionFactory) inctx.lookup(sfName);
67 log.debug("Obtained Session Factory:" + this.sf);
68 } catch (Exception e) {
69 throw new DataAccessException(
70 "Failed to obtain a Hibernate Session Factory", e);
71 }
72 }
73
74 /***
75 * Retreive the object matching the class handled by this DAO and the given
76 * identifier. If the identifier is not match, an exception is thrown
77 *
78 * @param identifier
79 * @return the maching pojo
80 * @throws DataAccessException
81 * @see com.geekologue.md4j.dao.AbstractDAO#get(java.io.Serializable)
82 */
83 public Object load(Serializable identifier) throws DataAccessException {
84 Object o = null;
85 try {
86 o = this.sf.getCurrentSession().load(this.daoClass, identifier);
87 } catch (Exception e) {
88 throw new DataAccessException("Failed to match identifier: "
89 + identifier + ", with identifier type: "
90 + identifier.getClass().getName() + ", for class "
91 + this.daoClass.getName(), e);
92 }
93 return o;
94 }
95
96 /***
97 * Retreive the object matching the class handled by this DAO and the given
98 * identifier or null if no match is found.
99 *
100 * @param identifier
101 * @return the maching pojo if any, <code>null</code> otherwise
102 * @throws DataAccessException
103 * @see com.geekologue.md4j.dao.AbstractDAO#get(java.io.Serializable)
104 */
105 public Object get(Serializable identifier) throws DataAccessException {
106 Object o = null;
107 try {
108 o = this.sf.getCurrentSession().get(this.daoClass, identifier);
109 if (o == null) {
110 log.warn("Failed to match identifier: " + identifier
111 + ", with identifier type: "
112 + identifier.getClass().getName() + ", for class "
113 + this.daoClass.getName());
114 }
115 } catch (Exception e) {
116 throw new DataAccessException("Failed to match identifier: "
117 + identifier + ", with identifier type: "
118 + identifier.getClass().getName() + ", for class "
119 + this.daoClass.getName(), e);
120 }
121 return o;
122 }
123
124 /***
125 * Retreive the properties of the object matching the given identifier as a
126 * Map of attribute-value pairs.
127 *
128 * @param identifier
129 * @param projectionProperties
130 * the names of properties to retrieve
131 * @return the requested properties for the object matching the identifier,
132 * if any and if at least one projection property was supplied,
133 * <code>null</code> otherwise.
134 * @see com.geekologue.md4j.dao.AbstractDAO#get(Serializable, Set)
135 */
136 public Map get(Serializable identifier, Set projectionProperties) {
137 Map result = null;
138 Set relatedNames = null;
139 if (projectionProperties != null && !projectionProperties.isEmpty()) {
140 Session session = this.sf.getCurrentSession();
141 Map params = new HashMap();
142 params.put(this.identifierName, identifier);
143 Criteria query = session.createCriteria(this.daoClass).add(
144 Restrictions.eq(this.identifierName, identifier));
145 for (Iterator iter = projectionProperties.iterator(); iter
146 .hasNext();) {
147 String projectionName = (String) iter.next();
148 int dotIndex = projectionName.indexOf(".");
149 if (dotIndex != -1) {
150 if (relatedNames == null) {
151 relatedNames = new HashSet();
152 }
153 String related = projectionName.substring(0, dotIndex);
154
155 if (!relatedNames.contains(related)) {
156 relatedNames.add(related);
157 log.info("Related: " + related);
158 query.createCriteria(related, related);
159 }
160 }
161 }
162 List list = Helper.getQueryResultAsMapList(query,
163 projectionProperties);
164 if (!list.isEmpty()) {
165 result = (Map) list.get(0);
166 }
167 }
168 return result;
169 }
170
171 /***
172 * Update the object matching the given identifier according to the property
173 * value pairs in the given map. If no match for the identifier is found,
174 * return <code>null</code>
175 *
176 * @param map
177 * the map with the property value pairs to update
178 * @throws DataAccessException
179 * @see com.geekologue.md4j.dao.AbstractDAO#update(java.util.Map,
180 * java.io.Serializable)
181 */
182 public void update(Map map) throws DataAccessException {
183 try {
184 this.sf.getCurrentSession().update(updateFromParams(map));
185 } catch (Exception e) {
186 throw new DataAccessException("Date parsing failed: ", e);
187 }
188 }
189
190 /***
191 *
192 * @see com.geekologue.md4j.dao.AbstractDAO#update(java.util.Map,
193 * java.io.Serializable)
194 */
195 public void update(Object pojo) throws DataAccessException {
196 try {
197 this.sf.getCurrentSession().update(pojo);
198 } catch (Exception e) {
199 throw new DataAccessException("Failed updating "
200 + this.daoClass.getName() + " instance: " + pojo, e);
201 }
202 }
203
204 /***
205 *
206 * @see com.geekologue.md4j.dao.AbstractDAO#listAll()
207 */
208 public List listAll() throws DataAccessException {
209 try {
210 Session sess = this.sf.getCurrentSession();
211 Criteria crit = sess.createCriteria(this.daoClass);
212 return crit.list();
213 } catch (Exception e) {
214 e.printStackTrace();
215 throw new DataAccessException(
216 "Failed to retreive the complete collection of "
217 + this.daoClass + " objects", e);
218 }
219 }
220
221 /***
222 *
223 * @see com.geekologue.md4j.dao.AbstractDAO#save(java.util.Map,
224 * java.io.Serializable) public Object save(Map map, Serializable
225 * identifier){ Object pojo = null; Session session =
226 * this.sf.getCurrentSession(); try { pojo =
227 * this.daoClass.newInstance(); BeanUtils.populate(pojo, map); if
228 * (identifier == null) { pojo = session.save(pojo); } else {
229 * session.save(pojo, identifier); pojo = session.load(this.daoClass,
230 * identifier); } } catch (Exception e) { throw new
231 * DataAccessException("Failed to persist pojo from map: " + map, e); }
232 * return pojo; }
233 *
234 */
235 /***
236 *
237 * @see com.geekologue.md4j.dao.AbstractDAO#save(java.lang.Object)
238 */
239 public Serializable save(Object pojo) throws DataAccessException {
240 try {
241 return this.sf.getCurrentSession().save(pojo);
242 } catch (Exception e) {
243 throw new DataAccessException("Failed to persist pojo: " + pojo, e);
244 }
245 }
246
247 /***
248 *
249 * @see com.geekologue.md4j.dao.AbstractDAO#save(java.lang.Object,
250 * java.io.Serializable) public void save(Object pojo, Serializable
251 * identifier) throws DataAccessException { try { Session session =
252 * this.sf.getCurrentSession(); session.save(pojo, identifier); pojo =
253 * session.load(this.daoClass, identifier); } catch (Exception e) {
254 * throw new DataAccessException("Failed to persist pojo: " + pojo, e); } }
255 *
256 */
257 /***
258 *
259 * @see com.geekologue.md4j.dao.AbstractDAO#saveOrUpdate(java.lang.Object)
260 */
261 public Serializable saveOrUpdate(Object pojo) throws DataAccessException {
262 Serializable identifier;
263 try {
264 Session session = this.sf.getCurrentSession();
265 session.saveOrUpdate(pojo);
266 identifier = session.getIdentifier(pojo);
267 } catch (Exception e) {
268 throw new DataAccessException("Failed to persist pojo: " + pojo, e);
269 }
270 return identifier;
271 }
272
273 /***
274 *
275 * @see com.geekologue.md4j.dao.AbstractDAO#findByExample(java.lang.Object)
276 */
277 protected List findByExample(Object exampleObject)
278 throws DataAccessException {
279 try {
280 Criteria crit = this.sf.getCurrentSession().createCriteria(
281 this.daoClass);
282 return crit.add(Example.create(exampleObject)).list();
283 } catch (Exception e) {
284 e.printStackTrace();
285 throw new DataAccessException("Failed to find objects by example",
286 e);
287 }
288 }
289
290 /***
291 *
292 * @see com.geekologue.md4j.dao.AbstractDAO#getPage(Map, Order, int, int)
293 */
294 public Page getPage(Map params, Order order, int pageNumber, int pageSize) {
295 HbmQueryResultPage page = (HbmQueryResultPage) this.getPage(null,
296 params, order, pageNumber, pageSize);
297 return page;
298 }
299
300 /***
301 *
302 * @see com.geekologue.md4j.dao.AbstractDAO#getPage(Set, Map, Order, int,
303 * int)
304 */
305 public Page getPage(Set projectionProps, Map params, Order order,
306 int pageNumber, int pageSize) {
307 HbmQueryResultPage page = null;
308 try {
309 Session sess = this.sf.getCurrentSession();
310 Criteria criteria = sess.createCriteria(this.daoClass);
311 this.populateCriteria(projectionProps, params, criteria);
312 page = (HbmQueryResultPage) this.getPage(projectionProps,
313 order, criteria, pageNumber, pageSize);
314 } catch (Md4jException e) {
315 throw e;
316 } catch (Exception e) {
317 throw new DataAccessException(e);
318 }
319 return page;
320 }
321
322 protected abstract void populateCriteria(Set projectionProps, Map params,
323 Criteria criteria);
324
325 /***
326 * Create and return a Page of results.
327 *
328 * @param sess
329 * The Hibernate Session object to use for the search
330 * @param projectionProps
331 * The set of properties to return for each result
332 * @param params
333 * The set of search criteria to use
334 * @param criteria
335 * The criteria to use for the search, produced by each DAO in
336 * the object hierarchy using the <code>params</code> map
337 * @param pageNumber
338 * The page number to return
339 * @param pageSize
340 * The page size
341 * @return The resulting Page of results
342 */
343 protected Page getPage(Set projectionProps,
344 Order order, Criteria criteria, int pageNumber, int pageSize) {
345 for (Iterator iter = order.getOrderProperties().listIterator(); iter
346 .hasNext();) {
347 String propertyName = (String) iter.next();
348 boolean ascending = order.isAscending(propertyName);
349 log.info("Adding " + (ascending ? "ascending" : "descending")
350 + " order for: " + propertyName);
351 if (ascending) {
352 criteria.addOrder(Property.forName(propertyName).asc());
353 } else {
354 criteria.addOrder(Property.forName(propertyName).desc());
355 }
356 }
357 HbmQueryResultPage page = new HbmQueryResultPage(criteria,
358 projectionProps, pageNumber, pageSize);
359 page.setParentOptions(this.getParentOptions());
360 return page;
361 }
362
363 public void delete(Serializable pojo) throws DataAccessException {
364 try {
365 this.sf.getCurrentSession().delete(pojo);
366 } catch (Exception e) {
367 throw new DataAccessException("Failed to delete object: " + pojo, e);
368 }
369 }
370
371 protected Object createFromParams(Map map) {
372 Object pojo = getDaoClassInstance();
373 this.copyProperties(map, pojo);
374 return pojo;
375 }
376
377 /***
378 * Subclasses must override this method to obtain the persisted instance
379 * from here and update properties in their implementation of the method
380 *
381 * @param map
382 * @return
383 * @throws DataAccessException
384 */
385 protected Object updateFromParams(Map map) {
386 Object pojo = this.get((Serializable) map.get(this.identifierName));
387 if (pojo == null) {
388 throw new DataAccessException("Could not perform update as no "
389 + this.daoClass.getName()
390 + " instance was found with identifier: "
391 + this.identifierName);
392 }
393 this.copyProperties(map, pojo);
394 return pojo;
395 }
396
397 /***
398 * Checks whether a persisted instance exists with the given property/value
399 * pair. Used to check unique violation constraints before persisting new
400 * records. If an id is provided, the record matching it is excluded from
401 * the search, making the check valid for an update action.
402 *
403 * @param name
404 * the name of the property
405 * @param value
406 * the value to look for
407 * @param id
408 * the record to exclude from the search, usefull for checking
409 * constraints in case of an update (<code>null</code> may be
410 * provided)
411 * @return true if a match is found, false otherwise
412 */
413 public boolean exists(String name, Serializable value, Serializable id) {
414 boolean exists = false;
415 if (id == null || !name.equals(this.identifierName)) {
416 Criteria criteria = this.sf.getCurrentSession().createCriteria(
417 this.daoClass).add(Restrictions.eq(name, value));
418
419
420
421 if (id != null) {
422 criteria.add(Restrictions.ne(this.identifierName, id));
423 }
424 exists = ((Integer) criteria.setProjection(Projections.rowCount())
425 .uniqueResult()).intValue() > 0;
426 }
427 return exists;
428 }
429
430 public Set getBrokenUConstraints(Map params, Serializable id) {
431 Set brokens = null;
432 Set namesToCheck = this.getUniquePropertyNames();
433
434 if (namesToCheck != null && namesToCheck.size() > 0) {
435 brokens = new HashSet();
436 for (Iterator iter = namesToCheck.iterator(); iter.hasNext();) {
437 String key = (String) iter.next();
438 Serializable value = (Serializable) params.get(key);
439 if (value != null) {
440 if (this.exists(key, value, id)) {
441 brokens.add(key);
442 }
443 }
444 }
445 }
446 return brokens;
447 }
448
449 /***
450 * Copy the properties from the given Map to the target POJO object,
451 * converting the value type where appropriate.
452 *
453 * @param from
454 * @param to
455 */
456 public abstract void copyProperties(Map from, Object to);
457
458 /***
459 * Get the property names for which unique constraints exis
460 *
461 * @return the array of property names
462 */
463 public abstract Set getUniquePropertyNames();
464
465 /***
466 * Get data to populate drop downs refering to parent objects by many-to-one
467 * relationships.
468 *
469 * @return a Map where each key is the string name of the property for that
470 * parent object. The value for that key is yet another Map where
471 * the key value pair is used to the value and text (respectively)
472 * of HTML option elements
473 * @see com.geekologue.md4j.dao.AbstractDAO#getParentOptions()
474 */
475 public Map getParentOptions() {
476 Map map = new HashMap();
477 this.addParentOptions(map);
478 return map;
479 }
480
481 public abstract void addParentOptions(Map map);
482 }