1
2
3
4
5
6
7
8
9 package ca.uhn.cache.internal.impl;
10
11 import java.sql.SQLException;
12 import java.text.MessageFormat;
13 import java.util.HashMap;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import net.sf.hibernate.Hibernate;
20 import net.sf.hibernate.HibernateException;
21 import net.sf.hibernate.Query;
22 import net.sf.hibernate.Session;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.springframework.beans.BeanUtils;
27 import org.springframework.beans.factory.InitializingBean;
28 import org.springframework.dao.DataAccessException;
29 import org.springframework.orm.hibernate.HibernateCallback;
30 import org.springframework.orm.hibernate.support.HibernateDaoSupport;
31
32 import ca.uhn.cache.IDataItem;
33 import ca.uhn.cache.IDimension;
34 import ca.uhn.cache.IGroupQueryParam;
35 import ca.uhn.cache.IParamSpace;
36 import ca.uhn.cache.IPointQueryParam;
37 import ca.uhn.cache.IQuery;
38 import ca.uhn.cache.IQueryParam;
39 import ca.uhn.cache.IQueryResult;
40 import ca.uhn.cache.impl.DataItem;
41 import ca.uhn.cache.impl.DateParam;
42 import ca.uhn.cache.impl.DateRangeParam;
43 import ca.uhn.cache.impl.QueryResult;
44 import ca.uhn.cache.impl.StringParam;
45 import ca.uhn.cache.impl.StringSetParam;
46 import ca.uhn.cache.internal.IQueryResultStore;
47 import ca.uhn.cache.internal.exception.QueryResultStoreException;
48 import ca.uhn.cache.internal.hibernate.IQueryParamHelper;
49 import ca.uhn.cache.internal.hibernate.exception.HibernateQueryResultStoreException;
50 import ca.uhn.cache.internal.hibernate.impl.DateParamHelper;
51 import ca.uhn.cache.internal.hibernate.impl.Field;
52 import ca.uhn.cache.internal.hibernate.impl.Record;
53 import ca.uhn.cache.internal.hibernate.impl.StringParamHelper;
54 import ca.uhn.cache.internal.util.TimestampUtils;
55
56
57 /***
58 * <b>Hibernate</b> implementation of <code>IQueryResultStore</code>.
59 *
60 * @author <a href="mailto:alexei.guevara@uhn.on.ca">Alexei Guevara</a>
61 * @version $Revision: 1.1 $ updated on $Date: 2005/01/24 22:54:03 $ by $Author: bryan_tripp $
62 */
63 public class HibernateQueryResultStore extends HibernateDaoSupport implements IQueryResultStore, InitializingBean {
64
65 private static final Log ourLog = LogFactory.getLog( HibernateQueryResultStore.class );
66 private final Map myDimensionNameToDimensionMap;
67
68 private IParamSpace myParamSpace;
69
70 private final Map myQueryParamToFieldMap;
71
72 /***
73 */
74 public HibernateQueryResultStore() {
75 myQueryParamToFieldMap = new HashMap();
76 myDimensionNameToDimensionMap = new HashMap();
77
78 registerQueryParamToQueryParamHelper( StringParam.class, new StringParamHelper() );
79 registerQueryParamToQueryParamHelper( DateParam.class, new DateParamHelper() );
80 registerQueryParamToQueryParamHelper( StringSetParam.class, new StringParamHelper() );
81 registerQueryParamToQueryParamHelper( DateRangeParam.class, new DateParamHelper() );
82
83 }
84
85 /***
86 * {@inheritDoc}
87 */
88 public int delete( IQuery theProjection ) throws QueryResultStoreException {
89 int retVal;
90
91
92
93 final String hql = generateSelectHql( theProjection );
94 final Map hqlParamMap = generateHqlParamMap( theProjection );
95
96 final List recordList = getHibernateTemplate().executeFind(
97 new HibernateCallback() {
98 public Object doInHibernate( Session theSession ) throws HibernateException, SQLException {
99 Query query = theSession.createQuery( hql );
100
101 Set paramNames = hqlParamMap.keySet();
102 for (Iterator iter = paramNames.iterator(); iter.hasNext();) {
103 String paramName = (String) iter.next();
104 query.setParameter( paramName, hqlParamMap.get( paramName ) );
105 }
106
107 return query.list();
108 }
109 } );
110
111 retVal = recordList.size();
112
113
114 getHibernateTemplate().execute(
115 new HibernateCallback() {
116 public Object doInHibernate( Session theSession ) throws HibernateException, SQLException {
117 for (Iterator iter = recordList.iterator(); iter.hasNext();) {
118 Record record = (Record) iter.next();
119 theSession.delete( record );
120 }
121 return null;
122 }
123 } );
124
125 return retVal;
126 }
127
128 /***
129 * @return Returns the paramSpace.
130 */
131 public IParamSpace getParamSpace() {
132 return myParamSpace;
133 }
134
135 /***
136 * {@inheritDoc}
137 */
138 public void insert( IQueryResult theQueryResult ) throws QueryResultStoreException {
139 for (Iterator qrIter = theQueryResult.iterator(); qrIter.hasNext();) {
140
141 IDataItem dataItem = (IDataItem) qrIter.next();
142
143 final Record record = createRecordFrom( dataItem );
144
145
146 final IQuery projection = dataItem.getProjection();
147
148 try {
149
150 getHibernateTemplate().execute(
151 new HibernateCallback() {
152 /***
153 * {@inheritDoc}
154 */
155 public Object doInHibernate(Session theSession) throws HibernateException {
156
157 Record foundRecord = findRecordByDataItemId( theSession, record.getDataItemId() );
158 if ( foundRecord != null ) {
159
160
161 copy( record, foundRecord );
162 theSession.update( foundRecord );
163
164
165 Set foundFields = foundRecord.getFields();
166 Iterator foundFieldsIter = foundFields.iterator();
167
168 Set parameters = projection.getParameters();
169 for (Iterator queryParamIter = parameters.iterator(); queryParamIter.hasNext();) {
170 IPointQueryParam pqp = (IPointQueryParam) queryParamIter.next();
171
172 Field foundField = (Field) foundFieldsIter.next();
173
174
175 Field field = createFieldFrom( record, pqp );
176
177
178 copy( field, foundField );
179
180
181 theSession.update( foundField );
182 }
183 }
184 else {
185
186 theSession.save( record );
187
188 Set parameters = projection.getParameters();
189 for (Iterator queryParamIter = parameters.iterator(); queryParamIter.hasNext();) {
190
191 IPointQueryParam pqp = (IPointQueryParam) queryParamIter.next();
192
193 Field field = createFieldFrom( record, pqp );
194
195 theSession.save( field );
196 }
197 }
198
199
200 return null;
201
202 }
203 }
204 );
205 }
206 catch (DataAccessException e) {
207 throw new HibernateQueryResultStoreException(
208 "Exception thrown by Hibernate while trying to insert the record" + record, e );
209 }
210 }
211 }
212
213 /***
214 * copy all the attributes except: hibernateId and fields.
215 *
216 * TODO use reflection
217 */
218 protected void copy( Record theSource, Record theDestination ) {
219 BeanUtils.copyProperties( theSource, theDestination, new String[] { "hibernateId" , "fields" } );
220 }
221
222 /***
223 * copy all the attributes except: hibernateId and record.
224 *
225 * TODO use reflection
226 */
227 protected void copy( Field theSource, Field theDestination ) {
228 BeanUtils.copyProperties( theSource, theDestination, new String[] { "hibernateId", "record"} );
229 }
230
231 /***
232 * {@inheritDoc}
233 */
234 public IQueryResult select( IQuery theQuery ) throws QueryResultStoreException {
235 IQueryResult retVal;
236
237 final String hql = generateSelectHql( theQuery );
238 final Map hqlParamMap = generateHqlParamMap( theQuery );
239
240 List recordList = getHibernateTemplate().executeFind(
241 new HibernateCallback() {
242 public Object doInHibernate( Session theSession ) throws HibernateException, SQLException {
243 Query query = theSession.createQuery( hql );
244
245 Set paramNames = hqlParamMap.keySet();
246 for (Iterator iter = paramNames.iterator(); iter.hasNext();) {
247 String paramName = (String) iter.next();
248 query.setParameter( paramName, hqlParamMap.get( paramName ) );
249 }
250
251 return query.list();
252 }
253 } );
254
255 retVal = createQueryResultFromRecordList( recordList );
256
257 return retVal;
258 }
259 /***
260 * @param theParamSpace The paramSpace to set.
261 */
262 public void setParamSpace( IParamSpace theParamSpace ) {
263 myParamSpace = theParamSpace;
264
265 IDimension[] dimensions = theParamSpace.getDimensions();
266 for (int i = 0; i < dimensions.length; i++) {
267 IDimension dimension = dimensions[i];
268 myDimensionNameToDimensionMap.put( dimension.getName(), dimension );
269 }
270
271 }
272
273 private IDataItem createCachedDataItemFromRecord( Record theRecord ) {
274 IDataItem retVal;
275
276 retVal = new DataItem(
277 theRecord.getDataItemId(),
278 theRecord.getValue(),
279 createProjection( theRecord ),
280 theRecord.getVolatility(),
281 TimestampUtils.timestampToDate(theRecord.getLastUpdate()));
282
283 return retVal;
284 }
285
286 private IQuery createProjection( Record theRecord ) {
287 IQuery retVal = new ca.uhn.cache.impl.Query();
288
289 Set fields = theRecord.getFields();
290 for (Iterator iter = fields.iterator(); iter.hasNext();) {
291 Field field = (Field) iter.next();
292 IQueryParam qp = field.toQueryParam( myDimensionNameToDimensionMap );
293 retVal.addParameter( qp );
294 }
295
296 return retVal;
297 }
298
299 private IQueryResult createQueryResultFromRecordList( List theRecordList ) {
300 IQueryResult retVal = new QueryResult();
301
302 for (Iterator iter = theRecordList.iterator(); iter.hasNext();) {
303 Record record = (Record) iter.next();
304 IDataItem di = createCachedDataItemFromRecord( record );
305 retVal.add( di );
306 }
307
308 return retVal;
309 }
310
311 private String generateSelectHql( IQuery theQuery ) {
312 String retVal;
313
314 String fromAndWhereHql = generateFromAndWhereHql( theQuery );
315 retVal = MessageFormat.format( "SELECT f.record {0}", new Object[] { fromAndWhereHql } );
316
317 return retVal;
318 }
319
320 private String generateFromAndWhereHql( IQuery theQuery ) {
321 String retVal;
322
323 Set queryParams = theQuery.getParameters();
324
325 StringBuffer fromBuffer = new StringBuffer( "FROM " +Field.class.getName()+" f" );
326 int qpIndex = 1;
327 for (Iterator iter = queryParams.iterator(); iter.hasNext();) {
328 IQueryParam qp = (IQueryParam) iter.next();
329 IQueryParamHelper queryParamHelper = getQueryParamHelper(qp);
330 Class fieldClass = queryParamHelper.getFieldClass();
331
332 fromBuffer.append(
333 MessageFormat.format(
334 ", {0} f{1}",
335 new Object[] { fieldClass.getName(), new Integer(qpIndex++) } ) );
336
337 }
338
339 StringBuffer whereBuffer = new StringBuffer( "WHERE " );
340 qpIndex = 1;
341 for (Iterator iter = queryParams.iterator(); iter.hasNext();) {
342 IQueryParam qp = (IQueryParam) iter.next();
343 IQueryParamHelper queryParamHelper = getQueryParamHelper(qp);
344
345 if ( qpIndex > 1 ) {
346 whereBuffer.append( " AND " );
347 }
348
349 if ( qp instanceof IPointQueryParam ) {
350 whereBuffer.append( queryParamHelper.generateHql( (IPointQueryParam) qp, qpIndex++ ) );
351 }
352 else if ( qp instanceof IGroupQueryParam ) {
353 whereBuffer.append( queryParamHelper.generateHql( (IGroupQueryParam) qp, qpIndex++ ) );
354 }
355 else {
356 throw new RuntimeException( "The query parameter " +qp+
357 " MUST implement the interface "
358 +IPointQueryParam.class.getName()+
359 " xor the interface "
360 +IGroupQueryParam.class.getName());
361 }
362
363 }
364
365 retVal = MessageFormat.format( "{0} {1}", new Object[] { fromBuffer, whereBuffer } );
366
367 return retVal;
368 }
369
370
371 private Map generateHqlParamMap( IQuery theQuery ) {
372 Map retVal = new HashMap();
373
374 int qpIndex = 1;
375 Set parameters = theQuery.getParameters();
376 for (Iterator iter = parameters.iterator(); iter.hasNext();) {
377 IQueryParam qp = (IQueryParam) iter.next();
378 IQueryParamHelper qpHelper = getQueryParamHelper(qp);
379
380 if ( qp instanceof IPointQueryParam ) {
381 retVal.putAll( qpHelper.generateHqlParamMap( (IPointQueryParam) qp, qpIndex++ ) );
382 }
383 else if ( qp instanceof IGroupQueryParam ) {
384 retVal.putAll( qpHelper.generateHqlParamMap( (IGroupQueryParam) qp, qpIndex++ ) );
385 }
386 else {
387 throw new RuntimeException( "The query parameter " +qp+
388 " MUST implement the interface "
389 +IPointQueryParam.class.getName()+
390 " xor the interface "
391 +IGroupQueryParam.class.getName());
392 }
393 }
394
395 return retVal;
396 }
397
398 /***
399 * Creates the corresponding <code>Field</code> subclass for the provided <code>IQueryParam</code>.
400 *
401 * Use {@link #registerQueryParamToField(Class, IFieldFactory)} to register mapping between an
402 * <code>IQueryParam</code> and the corresponding <code>Field</code> subclass.
403 *
404 * @param theRecord The record the field will be associated with.
405 * @param theQueryParam The provided <code>IPointQueryParam</code>.
406 * @return The created <code>Field</code>, or <b>null</b> if no mapping has
407 * been registered for the provided <code>IQueryParam</code>
408 */
409 protected Field createFieldFrom( Record theRecord, IPointQueryParam theQueryParam ) {
410 Field retVal = null;
411
412 IQueryParamHelper queryParamHelper = getQueryParamHelper( theQueryParam );
413 if ( queryParamHelper != null ) {
414 retVal = queryParamHelper.createField( theRecord, theQueryParam );
415 }
416
417 return retVal;
418 }
419
420 /***
421 * Creates the corresponding <code>Record</code> for the provided <code>IQuery</code>.
422 *
423 * @param theDataItem The provided <code>IQuery</code>.
424 * @return The created <code>Record</code>.
425 */
426 protected Record createRecordFrom( IDataItem theDataItem ) {
427 Record retVal = new Record();
428
429 retVal.setDataItemId( theDataItem.getId() );
430 retVal.setValue( theDataItem.getValue() );
431 retVal.setVolatility( theDataItem.getVolatility() );
432
433 if ( theDataItem.getLastUpdate() != null) {
434 retVal.setLastUpdate( TimestampUtils.dateToTimestamp( theDataItem.getLastUpdate() ) );
435 }
436
437 return retVal;
438 }
439
440 /***
441 * Retrieves the <code>IQueryParamHelper</code> corresponding to the provided query param.
442 *
443 * @param theQueryParam The provided query param.
444 * @return The corresponding <code>IQueryParamHelper</code>, or <b>null</b> if there is no
445 * corresponding <code>IQueryParamHelper</code> for the provided query param.
446 *
447 * @see #registerQueryParamToQueryParamHelper(Class, IQueryParamHelper)
448 */
449 protected IQueryParamHelper getQueryParamHelper( IQueryParam theQueryParam ) {
450 IQueryParamHelper queryParamHelper = (IQueryParamHelper) myQueryParamToFieldMap.get( theQueryParam.getClass() );
451 return queryParamHelper;
452 }
453
454 /***
455 * Registers the mapping between an <code>IQueryParam</code> and the corresponding <code>Field</code> subclass.
456 *
457 * @param theQueryParamClazz The query param class.
458 * @param theFieldFactory The <code>Field</code> subclass factory.
459 */
460 protected void registerQueryParamToQueryParamHelper( Class theQueryParamClazz, IQueryParamHelper theFieldFactory ) {
461 myQueryParamToFieldMap.put( theQueryParamClazz, theFieldFactory );
462 }
463
464
465
466 private Record findRecordByDataItemId( Session theSession, String theDataItemId ) throws HibernateException {
467 Record retVal = null;
468
469 List recordList =
470 theSession.find(
471 "FROM " +Record.class.getName()+ " WHERE dataItemId = ?" ,
472 theDataItemId,
473 Hibernate.STRING );
474
475 if ( !recordList.isEmpty() ) {
476 retVal = (Record) recordList.get( 0 );
477 }
478 return retVal;
479 }
480
481
482
483 }
484