1 /*** 2 * Copyright (c) 2003 held jointly by the individual authors. 3 * 4 * This library is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU Lesser General Public License as published 6 * by the Free Software Foundation; either version 2.1 of the License, or 7 * (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; with out even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with this library; if not, write to the Free Software Foundation, 16 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 17 * 18 * > http://www.gnu.org/copyleft/lesser.html 19 * > http://www.opensource.org/licenses/lgpl-license.php 20 */ 21 package net.mlw.vlh.adapter.hibernate3; 22 23 import java.lang.reflect.InvocationTargetException; 24 import java.text.ParseException; 25 import java.util.ArrayList; 26 import java.util.List; 27 28 import net.mlw.vlh.DefaultListBackedValueList; 29 import net.mlw.vlh.ValueList; 30 import net.mlw.vlh.ValueListInfo; 31 import net.mlw.vlh.adapter.AbstractValueListAdapter; 32 import net.mlw.vlh.adapter.hibernate3.util.ScrollableResultsDecorator; 33 import net.mlw.vlh.adapter.hibernate3.util.StatementBuilder; 34 import net.mlw.vlh.adapter.util.ObjectValidator; 35 36 import org.apache.commons.beanutils.PropertyUtils; 37 import org.apache.commons.logging.Log; 38 import org.apache.commons.logging.LogFactory; 39 import org.hibernate.HibernateException; 40 import org.hibernate.Query; 41 import org.hibernate.ScrollableResults; 42 import org.hibernate.Session; 43 import org.hibernate.SessionFactory; 44 import org.springframework.orm.hibernate3.SessionFactoryUtils; 45 46 /*** 47 * This adapter wraps the functionality of Hibernate. 48 * Add extra functionality such as paging, focusing 49 * and validating of current result set. 50 * <i> 51 * "Hibernate is a powerful, ultra-high performance 52 * object/relational persistence and query service 53 * for Java. Hibernate lets you develop persistent 54 * classes following common Java idiom - including 55 * association, inheritance, polymorphism, composition 56 * and the Java collections framework. The Hibernate 57 * Query Language, designed as a "minimal" 58 * object-oriented extension to SQL, provides an 59 * elegant bridge between the object and relational 60 * worlds. Hibernate also allows you to express queries 61 * using native SQL or Java-based Criteria and Example 62 * queries. Hibernate is now the most popular 63 * object/relational mapping solution for Java." 64 * </i> -http://www.hibernate.org/ 65 * 66 * @author Matthew L. Wilson, Andrej Zachar 67 * @version $Revision: 1.2 $ $Date: 2006/03/29 19:47:49 $ 68 */ 69 public class HibernateAdapter extends AbstractValueListAdapter 70 { 71 /*** The Hibernate SessionFactory. */ 72 private SessionFactory sessionFactory; 73 74 /*** 75 * <p> 76 * If is set, it use special ScrollableResultsDecorator, that enable or 77 * disable to add object in final list. 78 * </p> 79 * <h4>NOTE:</h4> 80 * <p> 81 * Also, it respects the total count of entries that overlap your paged 82 * list. 83 * </p> 84 */ 85 private ObjectValidator _validator = null; 86 87 /*** Commons logger. */ 88 private static final Log LOGGER = LogFactory.getLog(HibernateAdapter.class); 89 90 /*** If a new Session should be created if no thread-bound found. */ 91 private boolean allowCreate = true; 92 93 /*** The hibernate query. */ 94 private String hql; 95 96 private String namedQuery; 97 98 /*** The max rows in ResulSet to doFocus 99 * @author Andrej Zachar 100 */ 101 private long maxRowsForFocus = Long.MAX_VALUE; 102 103 /*** The name of object use to get focus property in hibernate sql syntax 104 * SELECT defaultFocusPropertyObjectAlias.getFocusProperty ... 105 * 106 * @author Andrej Zachar 107 */ 108 private String defaultFocusPropertyObjectAlias = ""; 109 110 /*** 111 * Enable or Disable String length checking of given filters values. If 112 * filter value is null or empty is removed from query. 113 * @author Andrej Zachar 114 */ 115 private boolean _isRemoveEmptyStrings = false; 116 117 private StatementBuilder statementBuilder; 118 119 /*** 120 * Enable or disable optimalization of the query for focus property. 121 */ 122 private boolean _focusOptimalization = true; 123 124 /*** 125 * @return Returns the focusOptimalization. 126 */ 127 public boolean isFocusOptimalization() 128 { 129 return _focusOptimalization; 130 } 131 132 /*** 133 * Enable or disable optimalization of the query for focus property. 134 * @param focusOptimalization true - enable query with short select, false - query with full select 135 */ 136 public void setFocusOptimalization(boolean focusOptimalization) 137 { 138 _focusOptimalization = focusOptimalization; 139 } 140 141 /*** 142 * <p> 143 * If is set, it use special ScrollableResultsDecorator, that enable or 144 * disable to add object in final list. 145 * </p> 146 * <h4>NOTE:</h4> 147 * <p> 148 * Also, it respects the total count of entries that overlap your paged 149 * list. 150 * </p> 151 * @param validator The validator to set. 152 */ 153 public void setValidator(ObjectValidator validator) 154 { 155 _validator = validator; 156 } 157 158 /*** 159 * @return Returns the isPrefilterEmpty. 160 */ 161 public boolean isRemoveEmptyStrings() 162 { 163 return _isRemoveEmptyStrings; 164 } 165 166 /*** 167 * Enable or Disable String length checking of given filters values. If 168 * filter value is null or empty is removed from query. 169 * 170 * @param isPrefilterEmpty 171 * true-remove null and empty, false - remove only null filters. 172 */ 173 public void setRemoveEmptyStrings(boolean isPrefilterEmpty) 174 { 175 _isRemoveEmptyStrings = isPrefilterEmpty; 176 } 177 178 /*** 179 * @see net.mlw.vlh.ValueListAdapter#getValueList(java.lang.String, 180 * net.mlw.vlh.ValueListInfo) 181 */ 182 public ValueList getValueList(String name, ValueListInfo info) 183 { 184 185 LOGGER.debug("getValueList(String, ValueListInfo) - start"); 186 187 if (info.getSortingColumn() == null) 188 { 189 info.setPrimarySortColumn(getDefaultSortColumn()); 190 info.setPrimarySortDirection(getDefaultSortDirectionInteger()); 191 if (LOGGER.isDebugEnabled()) 192 { 193 LOGGER.debug("The default sort column '" + getDefaultSortColumn() + "' with direction '" + getDefaultSortDirectionInteger() 194 + "' was set."); 195 } 196 } 197 198 int numberPerPage = info.getPagingNumberPer(); 199 200 if (numberPerPage == Integer.MAX_VALUE) 201 { 202 numberPerPage = getDefaultNumberPerPage(); 203 info.setPagingNumberPer(numberPerPage); 204 if (LOGGER.isDebugEnabled()) 205 { 206 LOGGER.debug("The paging number per page '" + numberPerPage + "' was set."); 207 } 208 } 209 210 Session session = SessionFactoryUtils.getSession(getSessionFactory(), allowCreate); 211 try 212 { 213 Query query; 214 215 boolean doFocus = ((getAdapterType() & DO_FOCUS) == 0) && info.isFocusEnabled() && info.isDoFocus() && (namedQuery == null); 216 217 if (doFocus) 218 { 219 if (LOGGER.isDebugEnabled()) 220 { 221 LOGGER.debug("Start to focusing adapterName '" + name + "', ValueListInfo info = " + info + "'"); 222 } 223 ScrollableResults results = getScrollableResults(getQueryForFocus(info, session), info); 224 results.beforeFirst(); 225 doFocusFor(info, results); 226 227 if (LOGGER.isDebugEnabled()) 228 { 229 LOGGER.debug("Focusing finished for adapterName '" + name + "', ValueListInfo info '" + info + "'"); 230 } 231 } 232 233 query = getQuery(info, session); 234 235 boolean doPaging = ((getAdapterType() & DO_PAGE) == 0); 236 237 List list; 238 239 if (doPaging) 240 { 241 if (LOGGER.isDebugEnabled()) 242 { 243 LOGGER.debug("getValueList(String adapterName = " + name + ", ValueListInfo info = " + info 244 + ") - Start to paging result set"); 245 } 246 247 list = new ArrayList(numberPerPage); 248 ScrollableResults results = getScrollableResults(query, info); 249 250 results.last(); 251 int lastRowNumber = results.getRowNumber(); 252 info.setTotalNumberOfEntries(lastRowNumber + 1); 253 254 if (numberPerPage == 0) 255 { 256 numberPerPage = getDefaultNumberPerPage(); 257 } 258 259 int pageNumber = info.getPagingPage(); 260 boolean isResult; 261 if (pageNumber > 1) 262 { 263 if ((pageNumber - 1) * numberPerPage > lastRowNumber) 264 { 265 pageNumber = (lastRowNumber / numberPerPage) + 1; 266 info.setPagingPage(pageNumber); 267 } 268 } 269 if (pageNumber > 1) 270 { 271 isResult = results.scroll((pageNumber - 1) * numberPerPage - lastRowNumber); 272 } 273 else 274 { 275 isResult = results.first(); 276 } 277 278 for (int i = 0; i < numberPerPage && isResult; i++) 279 { 280 list.add(results.get(0)); 281 isResult = results.next(); 282 } 283 284 LOGGER.debug("Sorting finished."); 285 286 } 287 else 288 { 289 290 LOGGER.debug("Retrieving a list directly from the query."); 291 292 list = query.list(); 293 info.setTotalNumberOfEntries(list.size()); 294 } 295 296 ValueList returnValueList = getListBackedValueList(info, list); 297 if (LOGGER.isDebugEnabled()) 298 { 299 LOGGER.debug("Retrieved list was wrapped in valuelist, info=" + info); 300 } 301 return returnValueList; 302 } 303 catch (HibernateException e) 304 { 305 LOGGER.error("Error getting data in adapater '" + name + "' with info = '" + info + "'", e); 306 throw SessionFactoryUtils.convertHibernateAccessException(e); 307 } 308 catch (Exception e) 309 { 310 LOGGER.fatal("Fatal error getting data in adapater '" + name + "' with info = '" + info + "'", e); 311 return null; 312 } 313 finally 314 { 315 SessionFactoryUtils.releaseSession(session, getSessionFactory()); 316 } 317 } 318 319 /*** 320 * @param info 321 * @param list 322 * @return DefaultListBackValueList instance 323 */ 324 protected ValueList getListBackedValueList(ValueListInfo info, List list) 325 { 326 return new DefaultListBackedValueList(list, info); 327 } 328 329 /*** 330 * @param info 331 * @param results 332 * @throws HibernateException 333 * @throws NoSuchMethodException 334 * @throws InvocationTargetException 335 * @throws IllegalAccessException 336 */ 337 private void doFocusFor(ValueListInfo info, ScrollableResults results) throws HibernateException 338 { 339 info.setFocusStatus(ValueListInfo.FOCUS_NOT_FOUND); 340 341 int currentRow; 342 if (isFocusOptimalization()) 343 { 344 if (LOGGER.isDebugEnabled()) 345 { 346 LOGGER.debug("Focusing only property '" + info.getFocusProperty() + "' == '" + info.getFocusValue() + "'."); 347 } 348 for (currentRow = 0; ((results.next()) && (currentRow < maxRowsForFocus)); currentRow++) 349 { 350 String value = results.get(0).toString(); 351 if (value.equalsIgnoreCase(info.getFocusValue())) 352 { 353 if (LOGGER.isInfoEnabled()) 354 { 355 LOGGER.info("Focus property '" + info.getFocusProperty() + "' in row '" + currentRow + "'."); 356 } 357 info.setPagingPageFromRowNumber(results.getRowNumber()); 358 info.setFocusedRowNumberInTable(results.getRowNumber()); 359 info.setFocusStatus(ValueListInfo.FOCUS_FOUND); 360 break; 361 } 362 } 363 } 364 else 365 { 366 if (LOGGER.isDebugEnabled()) 367 { 368 LOGGER.debug("Focusing object with the property '" + info.getFocusProperty() + "' == '" + info.getFocusValue() + "'."); 369 } 370 for (currentRow = 0; ((results.next()) && (currentRow < maxRowsForFocus)); currentRow++) 371 { 372 373 Object value; 374 try 375 { 376 value = PropertyUtils.getProperty(results.get(0), info.getFocusProperty()); 377 } 378 catch (HibernateException e) 379 { 380 LOGGER.error("Error getting focus property '" + info.getFocusProperty() + "'", e); 381 throw e; 382 } 383 catch (Exception e) 384 { 385 LOGGER.warn("Ingoring error while getting focus property '" + info.getFocusProperty() + "'", e); 386 continue; 387 } 388 389 if (value.toString().equalsIgnoreCase(info.getFocusValue())) 390 { 391 if (LOGGER.isInfoEnabled()) 392 { 393 LOGGER.info("Focus object's property '" + info.getFocusProperty() + "' was found in the row '" + currentRow + "'."); 394 } 395 info.setPagingPageFromRowNumber(results.getRowNumber()); 396 info.setFocusedRowNumberInTable(results.getRowNumber()); 397 info.setFocusStatus(ValueListInfo.FOCUS_FOUND); 398 break; 399 } 400 } 401 } 402 if (currentRow == maxRowsForFocus) 403 { 404 if (LOGGER.isInfoEnabled()) 405 { 406 LOGGER.info("Focus for property '" + info.getFocusProperty() + "' exceded maximum rows for focus '" + maxRowsForFocus + "'."); 407 } 408 info.setFocusStatus(ValueListInfo.FOCUS_TOO_MANY_ITEMS); 409 } 410 } 411 412 /*** 413 * @param query 414 * @param info ValueListInfo This info will be set to validator. 415 * @return ScrollableResults, if is set non null _validator, it returns the 416 * ScrollableResultsDecorator. 417 * @throws HibernateException 418 */ 419 private ScrollableResults getScrollableResults(Query query, ValueListInfo info) throws HibernateException 420 { 421 ScrollableResults results; 422 423 if (_validator == null) 424 { 425 LOGGER.debug("Validator is null, using normal ScrollableResults"); 426 results = query.scroll(); 427 428 } 429 else 430 { 431 LOGGER.info("Using decorator of the ScrollableResults with your validator."); 432 _validator.setValueListInfo(info); 433 results = new ScrollableResultsDecorator(query.scroll(), _validator); 434 } 435 436 return results; 437 } 438 439 /*** 440 * @param info 441 * @param session 442 * @return @throws 443 * HibernateException 444 */ 445 private Query getQuery(ValueListInfo info, Session session) throws HibernateException, ParseException 446 { 447 448 if (getHql() != null) 449 { 450 return getStatementBuilder().generate(session, new StringBuffer(getHql()), info.getFilters(), _isRemoveEmptyStrings); 451 } 452 else 453 { 454 if (namedQuery != null) 455 { 456 return session.getNamedQuery(getNamedQuery()); 457 } 458 else 459 { 460 throw new HibernateException("Please define any QUERY in value list retrieve adpater!"); 461 } 462 } 463 } 464 465 /*** 466 * If focus optimalization is true, it select only focus property. For 467 * validator is recommended to set it to false, while you want to validate 468 * properties of retrieved objects. 469 * 470 * @param info 471 * @param session 472 * @return 473 * @throws HibernateException 474 */ 475 private Query getQueryForFocus(ValueListInfo info, Session session) throws HibernateException, ParseException 476 { 477 if (isFocusOptimalization()) 478 { 479 LOGGER.info("Focus will use optimalizated query."); 480 return getOptimizedQuery(info, session); 481 } 482 else 483 { 484 LOGGER.info("Focus will use normal (full) query."); 485 return getQuery(info, session); 486 } 487 } 488 489 /*** 490 * 491 * @param info 492 * @param session 493 * @return query that select only focus property. 494 * @throws HibernateException 495 * @throws ParseException 496 */ 497 private Query getOptimizedQuery(ValueListInfo info, Session session) throws HibernateException, ParseException 498 { 499 if (getHql() != null) 500 { 501 return getStatementBuilder().generateForFocus(session, new StringBuffer(getHql()), info.getFilters(), _isRemoveEmptyStrings, 502 defaultFocusPropertyObjectAlias, info.getFocusProperty()); 503 504 } 505 else 506 { 507 throw new HibernateException( 508 "Please define any HQL QUERY in value list retrieve adpater, function is not implemented for NamedQuery!"); 509 } 510 } 511 512 /*** Set the Hibernate SessionFactory to be used by this DAO. 513 * 514 * @param sessionFactory The Hibernate SessionFactory to be used by this DAO. 515 */ 516 public final void setSessionFactory(SessionFactory sessionFactory) 517 { 518 this.sessionFactory = sessionFactory; 519 } 520 521 /*** Return the Hibernate SessionFactory used by this DAO. 522 * 523 * @return The Hibernate SessionFactory used by this DAO. 524 */ 525 protected final SessionFactory getSessionFactory() 526 { 527 return this.sessionFactory; 528 } 529 530 /*** Sets the hql used to retrieve the data. 531 * @param hql The hql to set. 532 * @deprecated use setHql(String) 533 */ 534 public void setHsql(String hql) 535 { 536 this.hql = hql; 537 } 538 539 /*** Sets the hql used to retrieve the data. 540 * @param hql The hql to set. 541 */ 542 public void setHql(String hql) 543 { 544 this.hql = hql; 545 } 546 547 /*** Returns the namedQuery. 548 * @return Returns the namedQuery. 549 */ 550 public String getNamedQuery() 551 { 552 return namedQuery; 553 } 554 555 /*** Sets the namedQuery. 556 * <p> 557 * NOTE: by using this you can not enable sorting, filtering, 558 * paging of the data, and focusing of rows. 559 * </p> 560 * @param namedQuery 561 * The namedQuery to set. 562 */ 563 public void setNamedQuery(String namedQuery) 564 { 565 this.namedQuery = namedQuery; 566 } 567 568 /*** Gets the hql used to retrieve the data. 569 * @return Returns the hql. 570 */ 571 public String getHql() 572 { 573 return hql; 574 } 575 576 /*** Sets: If a new Session should be created if no thread-bound found. 577 * 578 * @param allowCreate 579 * The allowCreate to set. 580 */ 581 public void setAllowCreate(boolean allowCreate) 582 { 583 this.allowCreate = allowCreate; 584 } 585 586 /*** 587 * Maximum rows to search with Focus 588 * 589 * @return Returns the maxRowsForFocus. 590 */ 591 public long getMaxRowsForFocus() 592 { 593 return maxRowsForFocus; 594 } 595 596 /*** 597 * Maximum rows to search with Focus 598 * 599 * @param maxRowsForFocus 600 * The maxRowsForFocus to set. 601 */ 602 public void setMaxRowsForFocus(long maxRowsForFocus) 603 { 604 this.maxRowsForFocus = maxRowsForFocus; 605 } 606 607 /*** 608 * @return Returns the defaultFocusPropertyObject. 609 */ 610 public String getDefaultFocusPropertyObjectAlias() 611 { 612 return defaultFocusPropertyObjectAlias; 613 } 614 615 /*** 616 * The name of object use to get focus property in hibernate hql syntax 617 * SELECT defaultFocusPropertyObjectAlias.getFocusProperty ... 618 * 619 * @param defaultFocusPropertyObjectAlias 620 * The defaultFocusPropertyObjectAlias to set. 621 */ 622 public void setDefaultFocusPropertyObjectAlias(String defaultFocusPropertyObjectAlias) 623 { 624 this.defaultFocusPropertyObjectAlias = defaultFocusPropertyObjectAlias + "."; 625 } 626 627 /*** 628 * @return Returns the statementBuilder. 629 */ 630 public StatementBuilder getStatementBuilder() 631 { 632 if (statementBuilder == null) 633 { 634 statementBuilder = new StatementBuilder(); 635 } 636 return statementBuilder; 637 } 638 639 /*** 640 * @param statementBuilder The statementBuilder to set. 641 */ 642 public void setStatementBuilder(StatementBuilder statementBuilder) 643 { 644 this.statementBuilder = statementBuilder; 645 } 646 }