001/* 002 * Copyright 2002-2015 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.orm.hibernate3; 018 019import java.io.Serializable; 020import java.lang.reflect.InvocationHandler; 021import java.lang.reflect.InvocationTargetException; 022import java.lang.reflect.Method; 023import java.lang.reflect.Proxy; 024import java.sql.SQLException; 025import java.util.Collection; 026import java.util.Iterator; 027import java.util.List; 028 029import org.hibernate.Criteria; 030import org.hibernate.Filter; 031import org.hibernate.FlushMode; 032import org.hibernate.Hibernate; 033import org.hibernate.HibernateException; 034import org.hibernate.LockMode; 035import org.hibernate.Query; 036import org.hibernate.ReplicationMode; 037import org.hibernate.Session; 038import org.hibernate.SessionFactory; 039import org.hibernate.criterion.DetachedCriteria; 040import org.hibernate.criterion.Example; 041import org.hibernate.engine.SessionImplementor; 042import org.hibernate.event.EventSource; 043 044import org.springframework.dao.DataAccessException; 045import org.springframework.dao.DataAccessResourceFailureException; 046import org.springframework.dao.InvalidDataAccessApiUsageException; 047import org.springframework.util.Assert; 048 049/** 050 * Helper class that simplifies Hibernate data access code. Automatically 051 * converts HibernateExceptions into DataAccessExceptions, following the 052 * {@code org.springframework.dao} exception hierarchy. 053 * 054 * <p>The central method is {@code execute}, supporting Hibernate access code 055 * implementing the {@link HibernateCallback} interface. It provides Hibernate Session 056 * handling such that neither the HibernateCallback implementation nor the calling 057 * code needs to explicitly care about retrieving/closing Hibernate Sessions, 058 * or handling Session lifecycle exceptions. For typical single step actions, 059 * there are various convenience methods (find, load, saveOrUpdate, delete). 060 * 061 * <p>Can be used within a service implementation via direct instantiation 062 * with a SessionFactory reference, or get prepared in an application context 063 * and given to services as bean reference. Note: The SessionFactory should 064 * always be configured as bean in the application context, in the first case 065 * given to the service directly, in the second case to the prepared template. 066 * 067 * <p><b>NOTE: As of Hibernate 3.0.1, transactional Hibernate access code can 068 * also be coded in plain Hibernate style. Hence, for newly started projects, 069 * consider adopting the standard Hibernate3 style of coding data access objects 070 * instead, based on {@link org.hibernate.SessionFactory#getCurrentSession()}.</b> 071 * 072 * <p>This class can be considered as direct alternative to working with the raw 073 * Hibernate3 Session API (through {@code SessionFactory.getCurrentSession()}). 074 * The major advantage is its automatic conversion to DataAccessExceptions as well 075 * as its capability to fall back to 'auto-commit' style behavior when used outside 076 * of transactions. <b>Note that HibernateTemplate will perform its own Session 077 * management, not participating in a custom Hibernate CurrentSessionContext 078 * unless you explicitly switch {@link #setAllowCreate "allowCreate"} to "false".</b> 079 * 080 * <p>{@link LocalSessionFactoryBean} is the preferred way of obtaining a reference 081 * to a specific Hibernate SessionFactory, at least in a non-EJB environment. 082 * The Spring application context will manage its lifecycle, initializing and 083 * shutting down the factory as part of the application. 084 * 085 * <p>Note that operations that return an Iterator (i.e. {@code iterate}) 086 * are supposed to be used within Spring-driven or JTA-driven transactions 087 * (with HibernateTransactionManager, JtaTransactionManager, or EJB CMT). 088 * Else, the Iterator won't be able to read results from its ResultSet anymore, 089 * as the underlying Hibernate Session will already have been closed. 090 * 091 * <p>Lazy loading will also just work with an open Hibernate Session, 092 * either within a transaction or within OpenSessionInViewFilter/Interceptor. 093 * Furthermore, some operations just make sense within transactions, 094 * for example: {@code contains}, {@code evict}, {@code lock}, 095 * {@code flush}, {@code clear}. 096 * 097 * @author Juergen Hoeller 098 * @since 1.2 099 * @see #setSessionFactory 100 * @see HibernateCallback 101 * @see org.hibernate.Session 102 * @see LocalSessionFactoryBean 103 * @see HibernateTransactionManager 104 * @see org.springframework.transaction.jta.JtaTransactionManager 105 * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter 106 * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor 107 * @deprecated as of Spring 4.3, in favor of Hibernate 4.x/5.x 108 */ 109@Deprecated 110public class HibernateTemplate extends HibernateAccessor implements HibernateOperations { 111 112 private boolean allowCreate = true; 113 114 private boolean alwaysUseNewSession = false; 115 116 private boolean exposeNativeSession = false; 117 118 private boolean checkWriteOperations = true; 119 120 private boolean cacheQueries = false; 121 122 private String queryCacheRegion; 123 124 private int fetchSize = 0; 125 126 private int maxResults = 0; 127 128 129 /** 130 * Create a new HibernateTemplate instance. 131 */ 132 public HibernateTemplate() { 133 } 134 135 /** 136 * Create a new HibernateTemplate instance. 137 * @param sessionFactory the SessionFactory to create Sessions with 138 */ 139 public HibernateTemplate(SessionFactory sessionFactory) { 140 setSessionFactory(sessionFactory); 141 afterPropertiesSet(); 142 } 143 144 /** 145 * Create a new HibernateTemplate instance. 146 * @param sessionFactory the SessionFactory to create Sessions with 147 * @param allowCreate if a non-transactional Session should be created when no 148 * transactional Session can be found for the current thread 149 */ 150 public HibernateTemplate(SessionFactory sessionFactory, boolean allowCreate) { 151 setSessionFactory(sessionFactory); 152 setAllowCreate(allowCreate); 153 afterPropertiesSet(); 154 } 155 156 157 /** 158 * Set if a new {@link Session} should be created when no transactional 159 * {@code Session} can be found for the current thread. 160 * The default value is {@code true}. 161 * <p>{@code HibernateTemplate} is aware of a corresponding 162 * {@code Session} bound to the current thread, for example when using 163 * {@link HibernateTransactionManager}. If {@code allowCreate} is 164 * {@code true}, a new non-transactional {@code Session} will be 165 * created if none is found, which needs to be closed at the end of the operation. 166 * If {@code false}, an {@link IllegalStateException} will get thrown in 167 * this case. 168 * <p><b>NOTE: As of Spring 2.5, switching {@code allowCreate} 169 * to {@code false} will delegate to Hibernate's 170 * {@link org.hibernate.SessionFactory#getCurrentSession()} method,</b> 171 * which - with Spring-based setup - will by default delegate to Spring's 172 * {@code SessionFactoryUtils.getSession(sessionFactory, false)}. 173 * This mode also allows for custom Hibernate CurrentSessionContext strategies 174 * to be plugged in, whereas {@code allowCreate} set to {@code true} 175 * will always use a Spring-managed Hibernate Session. 176 * @see SessionFactoryUtils#getSession(SessionFactory, boolean) 177 */ 178 public void setAllowCreate(boolean allowCreate) { 179 this.allowCreate = allowCreate; 180 } 181 182 /** 183 * Return if a new Session should be created if no thread-bound found. 184 */ 185 public boolean isAllowCreate() { 186 return this.allowCreate; 187 } 188 189 /** 190 * Set whether to always use a new Hibernate Session for this template. 191 * Default is "false"; if activated, all operations on this template will 192 * work on a new Hibernate Session even in case of a pre-bound Session 193 * (for example, within a transaction or OpenSessionInViewFilter). 194 * <p>Within a transaction, a new Hibernate Session used by this template 195 * will participate in the transaction through using the same JDBC 196 * Connection. In such a scenario, multiple Sessions will participate 197 * in the same database transaction. 198 * <p>Turn this on for operations that are supposed to always execute 199 * independently, without side effects caused by a shared Hibernate Session. 200 */ 201 public void setAlwaysUseNewSession(boolean alwaysUseNewSession) { 202 this.alwaysUseNewSession = alwaysUseNewSession; 203 } 204 205 /** 206 * Return whether to always use a new Hibernate Session for this template. 207 */ 208 public boolean isAlwaysUseNewSession() { 209 return this.alwaysUseNewSession; 210 } 211 212 /** 213 * Set whether to expose the native Hibernate Session to 214 * HibernateCallback code. 215 * <p>Default is "false": a Session proxy will be returned, suppressing 216 * {@code close} calls and automatically applying query cache 217 * settings and transaction timeouts. 218 * @see HibernateCallback 219 * @see org.hibernate.Session 220 * @see #setCacheQueries 221 * @see #setQueryCacheRegion 222 * @see #prepareQuery 223 * @see #prepareCriteria 224 */ 225 public void setExposeNativeSession(boolean exposeNativeSession) { 226 this.exposeNativeSession = exposeNativeSession; 227 } 228 229 /** 230 * Return whether to expose the native Hibernate Session to 231 * HibernateCallback code, or rather a Session proxy. 232 */ 233 public boolean isExposeNativeSession() { 234 return this.exposeNativeSession; 235 } 236 237 /** 238 * Set whether to check that the Hibernate Session is not in read-only mode 239 * in case of write operations (save/update/delete). 240 * <p>Default is "true", for fail-fast behavior when attempting write operations 241 * within a read-only transaction. Turn this off to allow save/update/delete 242 * on a Session with flush mode NEVER. 243 * @see #setFlushMode 244 * @see #checkWriteOperationAllowed 245 * @see org.springframework.transaction.TransactionDefinition#isReadOnly 246 */ 247 public void setCheckWriteOperations(boolean checkWriteOperations) { 248 this.checkWriteOperations = checkWriteOperations; 249 } 250 251 /** 252 * Return whether to check that the Hibernate Session is not in read-only 253 * mode in case of write operations (save/update/delete). 254 */ 255 public boolean isCheckWriteOperations() { 256 return this.checkWriteOperations; 257 } 258 259 /** 260 * Set whether to cache all queries executed by this template. 261 * <p>If this is "true", all Query and Criteria objects created by 262 * this template will be marked as cacheable (including all 263 * queries through find methods). 264 * <p>To specify the query region to be used for queries cached 265 * by this template, set the "queryCacheRegion" property. 266 * @see #setQueryCacheRegion 267 * @see org.hibernate.Query#setCacheable 268 * @see org.hibernate.Criteria#setCacheable 269 */ 270 public void setCacheQueries(boolean cacheQueries) { 271 this.cacheQueries = cacheQueries; 272 } 273 274 /** 275 * Return whether to cache all queries executed by this template. 276 */ 277 public boolean isCacheQueries() { 278 return this.cacheQueries; 279 } 280 281 /** 282 * Set the name of the cache region for queries executed by this template. 283 * <p>If this is specified, it will be applied to all Query and Criteria objects 284 * created by this template (including all queries through find methods). 285 * <p>The cache region will not take effect unless queries created by this 286 * template are configured to be cached via the "cacheQueries" property. 287 * @see #setCacheQueries 288 * @see org.hibernate.Query#setCacheRegion 289 * @see org.hibernate.Criteria#setCacheRegion 290 */ 291 public void setQueryCacheRegion(String queryCacheRegion) { 292 this.queryCacheRegion = queryCacheRegion; 293 } 294 295 /** 296 * Return the name of the cache region for queries executed by this template. 297 */ 298 public String getQueryCacheRegion() { 299 return this.queryCacheRegion; 300 } 301 302 /** 303 * Set the fetch size for this HibernateTemplate. This is important for processing 304 * large result sets: Setting this higher than the default value will increase 305 * processing speed at the cost of memory consumption; setting this lower can 306 * avoid transferring row data that will never be read by the application. 307 * <p>Default is 0, indicating to use the JDBC driver's default. 308 */ 309 public void setFetchSize(int fetchSize) { 310 this.fetchSize = fetchSize; 311 } 312 313 /** 314 * Return the fetch size specified for this HibernateTemplate. 315 */ 316 public int getFetchSize() { 317 return this.fetchSize; 318 } 319 320 /** 321 * Set the maximum number of rows for this HibernateTemplate. This is important 322 * for processing subsets of large result sets, avoiding to read and hold 323 * the entire result set in the database or in the JDBC driver if we're 324 * never interested in the entire result in the first place (for example, 325 * when performing searches that might return a large number of matches). 326 * <p>Default is 0, indicating to use the JDBC driver's default. 327 */ 328 public void setMaxResults(int maxResults) { 329 this.maxResults = maxResults; 330 } 331 332 /** 333 * Return the maximum number of rows specified for this HibernateTemplate. 334 */ 335 public int getMaxResults() { 336 return this.maxResults; 337 } 338 339 340 @Override 341 public <T> T execute(HibernateCallback<T> action) throws DataAccessException { 342 return doExecute(action, false, false); 343 } 344 345 @Override 346 @Deprecated 347 public List<?> executeFind(HibernateCallback<?> action) throws DataAccessException { 348 Object result = doExecute(action, false, false); 349 if (result != null && !(result instanceof List)) { 350 throw new InvalidDataAccessApiUsageException( 351 "Result object returned from HibernateCallback isn't a List: [" + result + "]"); 352 } 353 return (List<?>) result; 354 } 355 356 /** 357 * Execute the action specified by the given action object within a 358 * new {@link org.hibernate.Session}. 359 * <p>This execute variant overrides the template-wide 360 * {@link #isAlwaysUseNewSession() "alwaysUseNewSession"} setting. 361 * @param action callback object that specifies the Hibernate action 362 * @return a result object returned by the action, or {@code null} 363 * @throws org.springframework.dao.DataAccessException in case of Hibernate errors 364 */ 365 public <T> T executeWithNewSession(HibernateCallback<T> action) { 366 return doExecute(action, true, false); 367 } 368 369 /** 370 * Execute the action specified by the given action object within a 371 * native {@link org.hibernate.Session}. 372 * <p>This execute variant overrides the template-wide 373 * {@link #isExposeNativeSession() "exposeNativeSession"} setting. 374 * @param action callback object that specifies the Hibernate action 375 * @return a result object returned by the action, or {@code null} 376 * @throws org.springframework.dao.DataAccessException in case of Hibernate errors 377 */ 378 public <T> T executeWithNativeSession(HibernateCallback<T> action) { 379 return doExecute(action, false, true); 380 } 381 382 /** 383 * Execute the action specified by the given action object within a Session. 384 * @param action callback object that specifies the Hibernate action 385 * @param enforceNewSession whether to enforce a new Session for this template 386 * even if there is a pre-bound transactional Session 387 * @param enforceNativeSession whether to enforce exposure of the native 388 * Hibernate Session to callback code 389 * @return a result object returned by the action, or {@code null} 390 * @throws org.springframework.dao.DataAccessException in case of Hibernate errors 391 */ 392 protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNewSession, boolean enforceNativeSession) 393 throws DataAccessException { 394 395 Assert.notNull(action, "Callback object must not be null"); 396 397 Session session = (enforceNewSession ? 398 SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession()); 399 boolean existingTransaction = (!enforceNewSession && 400 (!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory()))); 401 if (existingTransaction) { 402 logger.debug("Found thread-bound Session for HibernateTemplate"); 403 } 404 405 FlushMode previousFlushMode = null; 406 try { 407 previousFlushMode = applyFlushMode(session, existingTransaction); 408 enableFilters(session); 409 Session sessionToExpose = 410 (enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session)); 411 T result = action.doInHibernate(sessionToExpose); 412 flushIfNecessary(session, existingTransaction); 413 return result; 414 } 415 catch (HibernateException ex) { 416 throw convertHibernateAccessException(ex); 417 } 418 catch (SQLException ex) { 419 throw convertJdbcAccessException(ex); 420 } 421 catch (RuntimeException ex) { 422 // Callback code threw application exception... 423 throw ex; 424 } 425 finally { 426 if (existingTransaction) { 427 logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate"); 428 disableFilters(session); 429 if (previousFlushMode != null) { 430 session.setFlushMode(previousFlushMode); 431 } 432 } 433 else { 434 // Never use deferred close for an explicitly new Session. 435 if (isAlwaysUseNewSession()) { 436 SessionFactoryUtils.closeSession(session); 437 } 438 else { 439 SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory()); 440 } 441 } 442 } 443 } 444 445 /** 446 * Return a Session for use by this template. 447 * <p>Returns a new Session in case of "alwaysUseNewSession" (using the same 448 * JDBC Connection as a transactional Session, if applicable), a pre-bound 449 * Session in case of "allowCreate" turned off, and a pre-bound or new Session 450 * otherwise (new only if no transactional or otherwise pre-bound Session exists). 451 * @return the Session to use (never {@code null}) 452 * @see SessionFactoryUtils#getSession 453 * @see SessionFactoryUtils#getNewSession 454 * @see #setAlwaysUseNewSession 455 * @see #setAllowCreate 456 */ 457 protected Session getSession() { 458 if (isAlwaysUseNewSession()) { 459 return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()); 460 } 461 else if (isAllowCreate()) { 462 return SessionFactoryUtils.getSession( 463 getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator()); 464 } 465 else if (SessionFactoryUtils.hasTransactionalSession(getSessionFactory())) { 466 return SessionFactoryUtils.getSession(getSessionFactory(), false); 467 } 468 else { 469 try { 470 return getSessionFactory().getCurrentSession(); 471 } 472 catch (HibernateException ex) { 473 throw new DataAccessResourceFailureException("Could not obtain current Hibernate Session", ex); 474 } 475 } 476 } 477 478 /** 479 * Create a close-suppressing proxy for the given Hibernate Session. 480 * The proxy also prepares returned Query and Criteria objects. 481 * @param session the Hibernate Session to create a proxy for 482 * @return the Session proxy 483 * @see org.hibernate.Session#close() 484 * @see #prepareQuery 485 * @see #prepareCriteria 486 */ 487 protected Session createSessionProxy(Session session) { 488 Class<?>[] sessionIfcs; 489 Class<?> mainIfc = (session instanceof org.hibernate.classic.Session ? 490 org.hibernate.classic.Session.class : Session.class); 491 if (session instanceof EventSource) { 492 sessionIfcs = new Class<?>[] {mainIfc, EventSource.class}; 493 } 494 else if (session instanceof SessionImplementor) { 495 sessionIfcs = new Class<?>[] {mainIfc, SessionImplementor.class}; 496 } 497 else { 498 sessionIfcs = new Class<?>[] {mainIfc}; 499 } 500 return (Session) Proxy.newProxyInstance( 501 session.getClass().getClassLoader(), sessionIfcs, 502 new CloseSuppressingInvocationHandler(session)); 503 } 504 505 506 //------------------------------------------------------------------------- 507 // Convenience methods for loading individual objects 508 //------------------------------------------------------------------------- 509 510 @Override 511 public <T> T get(Class<T> entityClass, Serializable id) throws DataAccessException { 512 return get(entityClass, id, null); 513 } 514 515 @Override 516 public <T> T get(final Class<T> entityClass, final Serializable id, final LockMode lockMode) 517 throws DataAccessException { 518 519 return executeWithNativeSession(new HibernateCallback<T>() { 520 @Override 521 @SuppressWarnings("unchecked") 522 public T doInHibernate(Session session) throws HibernateException { 523 if (lockMode != null) { 524 return (T) session.get(entityClass, id, lockMode); 525 } 526 else { 527 return (T) session.get(entityClass, id); 528 } 529 } 530 }); 531 } 532 533 @Override 534 public Object get(String entityName, Serializable id) throws DataAccessException { 535 return get(entityName, id, null); 536 } 537 538 @Override 539 public Object get(final String entityName, final Serializable id, final LockMode lockMode) 540 throws DataAccessException { 541 542 return executeWithNativeSession(new HibernateCallback<Object>() { 543 @Override 544 public Object doInHibernate(Session session) throws HibernateException { 545 if (lockMode != null) { 546 return session.get(entityName, id, lockMode); 547 } 548 else { 549 return session.get(entityName, id); 550 } 551 } 552 }); 553 } 554 555 @Override 556 public <T> T load(Class<T> entityClass, Serializable id) throws DataAccessException { 557 return load(entityClass, id, null); 558 } 559 560 @Override 561 public <T> T load(final Class<T> entityClass, final Serializable id, final LockMode lockMode) 562 throws DataAccessException { 563 564 return executeWithNativeSession(new HibernateCallback<T>() { 565 @Override 566 @SuppressWarnings("unchecked") 567 public T doInHibernate(Session session) throws HibernateException { 568 if (lockMode != null) { 569 return (T) session.load(entityClass, id, lockMode); 570 } 571 else { 572 return (T) session.load(entityClass, id); 573 } 574 } 575 }); 576 } 577 578 @Override 579 public Object load(String entityName, Serializable id) throws DataAccessException { 580 return load(entityName, id, null); 581 } 582 583 @Override 584 public Object load(final String entityName, final Serializable id, final LockMode lockMode) 585 throws DataAccessException { 586 587 return executeWithNativeSession(new HibernateCallback<Object>() { 588 @Override 589 public Object doInHibernate(Session session) throws HibernateException { 590 if (lockMode != null) { 591 return session.load(entityName, id, lockMode); 592 } 593 else { 594 return session.load(entityName, id); 595 } 596 } 597 }); 598 } 599 600 @Override 601 public <T> List<T> loadAll(final Class<T> entityClass) throws DataAccessException { 602 return executeWithNativeSession(new HibernateCallback<List<T>>() { 603 @Override 604 @SuppressWarnings("unchecked") 605 public List<T> doInHibernate(Session session) throws HibernateException { 606 Criteria criteria = session.createCriteria(entityClass); 607 criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 608 prepareCriteria(criteria); 609 return criteria.list(); 610 } 611 }); 612 } 613 614 @Override 615 public void load(final Object entity, final Serializable id) throws DataAccessException { 616 executeWithNativeSession(new HibernateCallback<Object>() { 617 @Override 618 public Object doInHibernate(Session session) throws HibernateException { 619 session.load(entity, id); 620 return null; 621 } 622 }); 623 } 624 625 @Override 626 public void refresh(final Object entity) throws DataAccessException { 627 refresh(entity, null); 628 } 629 630 @Override 631 public void refresh(final Object entity, final LockMode lockMode) throws DataAccessException { 632 executeWithNativeSession(new HibernateCallback<Object>() { 633 @Override 634 public Object doInHibernate(Session session) throws HibernateException { 635 if (lockMode != null) { 636 session.refresh(entity, lockMode); 637 } 638 else { 639 session.refresh(entity); 640 } 641 return null; 642 } 643 }); 644 } 645 646 @Override 647 public boolean contains(final Object entity) throws DataAccessException { 648 return executeWithNativeSession(new HibernateCallback<Boolean>() { 649 @Override 650 public Boolean doInHibernate(Session session) { 651 return session.contains(entity); 652 } 653 }); 654 } 655 656 @Override 657 public void evict(final Object entity) throws DataAccessException { 658 executeWithNativeSession(new HibernateCallback<Object>() { 659 @Override 660 public Object doInHibernate(Session session) throws HibernateException { 661 session.evict(entity); 662 return null; 663 } 664 }); 665 } 666 667 @Override 668 public void initialize(Object proxy) throws DataAccessException { 669 try { 670 Hibernate.initialize(proxy); 671 } 672 catch (HibernateException ex) { 673 throw SessionFactoryUtils.convertHibernateAccessException(ex); 674 } 675 } 676 677 @Override 678 public Filter enableFilter(String filterName) throws IllegalStateException { 679 Session session = SessionFactoryUtils.getSession(getSessionFactory(), false); 680 Filter filter = session.getEnabledFilter(filterName); 681 if (filter == null) { 682 filter = session.enableFilter(filterName); 683 } 684 return filter; 685 } 686 687 688 //------------------------------------------------------------------------- 689 // Convenience methods for storing individual objects 690 //------------------------------------------------------------------------- 691 692 @Override 693 public void lock(final Object entity, final LockMode lockMode) throws DataAccessException { 694 executeWithNativeSession(new HibernateCallback<Object>() { 695 @Override 696 public Object doInHibernate(Session session) throws HibernateException { 697 session.lock(entity, lockMode); 698 return null; 699 } 700 }); 701 } 702 703 @Override 704 public void lock(final String entityName, final Object entity, final LockMode lockMode) 705 throws DataAccessException { 706 707 executeWithNativeSession(new HibernateCallback<Object>() { 708 @Override 709 public Object doInHibernate(Session session) throws HibernateException { 710 session.lock(entityName, entity, lockMode); 711 return null; 712 } 713 }); 714 } 715 716 @Override 717 public Serializable save(final Object entity) throws DataAccessException { 718 return executeWithNativeSession(new HibernateCallback<Serializable>() { 719 @Override 720 public Serializable doInHibernate(Session session) throws HibernateException { 721 checkWriteOperationAllowed(session); 722 return session.save(entity); 723 } 724 }); 725 } 726 727 @Override 728 public Serializable save(final String entityName, final Object entity) throws DataAccessException { 729 return executeWithNativeSession(new HibernateCallback<Serializable>() { 730 @Override 731 public Serializable doInHibernate(Session session) throws HibernateException { 732 checkWriteOperationAllowed(session); 733 return session.save(entityName, entity); 734 } 735 }); 736 } 737 738 @Override 739 public void update(Object entity) throws DataAccessException { 740 update(entity, null); 741 } 742 743 @Override 744 public void update(final Object entity, final LockMode lockMode) throws DataAccessException { 745 executeWithNativeSession(new HibernateCallback<Object>() { 746 @Override 747 public Object doInHibernate(Session session) throws HibernateException { 748 checkWriteOperationAllowed(session); 749 session.update(entity); 750 if (lockMode != null) { 751 session.lock(entity, lockMode); 752 } 753 return null; 754 } 755 }); 756 } 757 758 @Override 759 public void update(String entityName, Object entity) throws DataAccessException { 760 update(entityName, entity, null); 761 } 762 763 @Override 764 public void update(final String entityName, final Object entity, final LockMode lockMode) 765 throws DataAccessException { 766 767 executeWithNativeSession(new HibernateCallback<Object>() { 768 @Override 769 public Object doInHibernate(Session session) throws HibernateException { 770 checkWriteOperationAllowed(session); 771 session.update(entityName, entity); 772 if (lockMode != null) { 773 session.lock(entity, lockMode); 774 } 775 return null; 776 } 777 }); 778 } 779 780 @Override 781 public void saveOrUpdate(final Object entity) throws DataAccessException { 782 executeWithNativeSession(new HibernateCallback<Object>() { 783 @Override 784 public Object doInHibernate(Session session) throws HibernateException { 785 checkWriteOperationAllowed(session); 786 session.saveOrUpdate(entity); 787 return null; 788 } 789 }); 790 } 791 792 @Override 793 public void saveOrUpdate(final String entityName, final Object entity) throws DataAccessException { 794 executeWithNativeSession(new HibernateCallback<Object>() { 795 @Override 796 public Object doInHibernate(Session session) throws HibernateException { 797 checkWriteOperationAllowed(session); 798 session.saveOrUpdate(entityName, entity); 799 return null; 800 } 801 }); 802 } 803 804 @Override 805 public void replicate(final Object entity, final ReplicationMode replicationMode) 806 throws DataAccessException { 807 808 executeWithNativeSession(new HibernateCallback<Object>() { 809 @Override 810 public Object doInHibernate(Session session) throws HibernateException { 811 checkWriteOperationAllowed(session); 812 session.replicate(entity, replicationMode); 813 return null; 814 } 815 }); 816 } 817 818 @Override 819 public void replicate(final String entityName, final Object entity, final ReplicationMode replicationMode) 820 throws DataAccessException { 821 822 executeWithNativeSession(new HibernateCallback<Object>() { 823 @Override 824 public Object doInHibernate(Session session) throws HibernateException { 825 checkWriteOperationAllowed(session); 826 session.replicate(entityName, entity, replicationMode); 827 return null; 828 } 829 }); 830 } 831 832 @Override 833 public void persist(final Object entity) throws DataAccessException { 834 executeWithNativeSession(new HibernateCallback<Object>() { 835 @Override 836 public Object doInHibernate(Session session) throws HibernateException { 837 checkWriteOperationAllowed(session); 838 session.persist(entity); 839 return null; 840 } 841 }); 842 } 843 844 @Override 845 public void persist(final String entityName, final Object entity) throws DataAccessException { 846 executeWithNativeSession(new HibernateCallback<Object>() { 847 @Override 848 public Object doInHibernate(Session session) throws HibernateException { 849 checkWriteOperationAllowed(session); 850 session.persist(entityName, entity); 851 return null; 852 } 853 }); 854 } 855 856 @Override 857 public <T> T merge(final T entity) throws DataAccessException { 858 return executeWithNativeSession(new HibernateCallback<T>() { 859 @Override 860 @SuppressWarnings("unchecked") 861 public T doInHibernate(Session session) throws HibernateException { 862 checkWriteOperationAllowed(session); 863 return (T) session.merge(entity); 864 } 865 }); 866 } 867 868 @Override 869 public <T> T merge(final String entityName, final T entity) throws DataAccessException { 870 return executeWithNativeSession(new HibernateCallback<T>() { 871 @Override 872 @SuppressWarnings("unchecked") 873 public T doInHibernate(Session session) throws HibernateException { 874 checkWriteOperationAllowed(session); 875 return (T) session.merge(entityName, entity); 876 } 877 }); 878 } 879 880 @Override 881 public void delete(Object entity) throws DataAccessException { 882 delete(entity, null); 883 } 884 885 @Override 886 public void delete(final Object entity, final LockMode lockMode) throws DataAccessException { 887 executeWithNativeSession(new HibernateCallback<Object>() { 888 @Override 889 public Object doInHibernate(Session session) throws HibernateException { 890 checkWriteOperationAllowed(session); 891 if (lockMode != null) { 892 session.lock(entity, lockMode); 893 } 894 session.delete(entity); 895 return null; 896 } 897 }); 898 } 899 900 @Override 901 public void delete(String entityName, Object entity) throws DataAccessException { 902 delete(entityName, entity, null); 903 } 904 905 @Override 906 public void delete(final String entityName, final Object entity, final LockMode lockMode) 907 throws DataAccessException { 908 909 executeWithNativeSession(new HibernateCallback<Object>() { 910 @Override 911 public Object doInHibernate(Session session) throws HibernateException { 912 checkWriteOperationAllowed(session); 913 if (lockMode != null) { 914 session.lock(entityName, entity, lockMode); 915 } 916 session.delete(entityName, entity); 917 return null; 918 } 919 }); 920 } 921 922 @Override 923 public void deleteAll(final Collection<?> entities) throws DataAccessException { 924 executeWithNativeSession(new HibernateCallback<Object>() { 925 @Override 926 public Object doInHibernate(Session session) throws HibernateException { 927 checkWriteOperationAllowed(session); 928 for (Object entity : entities) { 929 session.delete(entity); 930 } 931 return null; 932 } 933 }); 934 } 935 936 @Override 937 public void flush() throws DataAccessException { 938 executeWithNativeSession(new HibernateCallback<Object>() { 939 @Override 940 public Object doInHibernate(Session session) throws HibernateException { 941 session.flush(); 942 return null; 943 } 944 }); 945 } 946 947 @Override 948 public void clear() throws DataAccessException { 949 executeWithNativeSession(new HibernateCallback<Object>() { 950 @Override 951 public Object doInHibernate(Session session) { 952 session.clear(); 953 return null; 954 } 955 }); 956 } 957 958 959 //------------------------------------------------------------------------- 960 // Convenience finder methods for HQL strings 961 //------------------------------------------------------------------------- 962 963 @Override 964 public List<?> find(String queryString) throws DataAccessException { 965 return find(queryString, (Object[]) null); 966 } 967 968 @Override 969 public List<?> find(String queryString, Object value) throws DataAccessException { 970 return find(queryString, new Object[] {value}); 971 } 972 973 @Override 974 public List<?> find(final String queryString, final Object... values) throws DataAccessException { 975 return executeWithNativeSession(new HibernateCallback<List<?>>() { 976 @Override 977 public List<?> doInHibernate(Session session) throws HibernateException { 978 Query queryObject = session.createQuery(queryString); 979 prepareQuery(queryObject); 980 if (values != null) { 981 for (int i = 0; i < values.length; i++) { 982 queryObject.setParameter(i, values[i]); 983 } 984 } 985 return queryObject.list(); 986 } 987 }); 988 } 989 990 @Override 991 public List<?> findByNamedParam(String queryString, String paramName, Object value) 992 throws DataAccessException { 993 994 return findByNamedParam(queryString, new String[] {paramName}, new Object[] {value}); 995 } 996 997 @Override 998 public List<?> findByNamedParam(final String queryString, final String[] paramNames, final Object[] values) 999 throws DataAccessException { 1000 1001 if (paramNames.length != values.length) { 1002 throw new IllegalArgumentException("Length of paramNames array must match length of values array"); 1003 } 1004 return executeWithNativeSession(new HibernateCallback<List<?>>() { 1005 @Override 1006 public List<?> doInHibernate(Session session) throws HibernateException { 1007 Query queryObject = session.createQuery(queryString); 1008 prepareQuery(queryObject); 1009 if (values != null) { 1010 for (int i = 0; i < values.length; i++) { 1011 applyNamedParameterToQuery(queryObject, paramNames[i], values[i]); 1012 } 1013 } 1014 return queryObject.list(); 1015 } 1016 }); 1017 } 1018 1019 @Override 1020 public List<?> findByValueBean(final String queryString, final Object valueBean) 1021 throws DataAccessException { 1022 1023 return executeWithNativeSession(new HibernateCallback<List<?>>() { 1024 @Override 1025 public List<?> doInHibernate(Session session) throws HibernateException { 1026 Query queryObject = session.createQuery(queryString); 1027 prepareQuery(queryObject); 1028 queryObject.setProperties(valueBean); 1029 return queryObject.list(); 1030 } 1031 }); 1032 } 1033 1034 1035 //------------------------------------------------------------------------- 1036 // Convenience finder methods for named queries 1037 //------------------------------------------------------------------------- 1038 1039 @Override 1040 public List<?> findByNamedQuery(String queryName) throws DataAccessException { 1041 return findByNamedQuery(queryName, (Object[]) null); 1042 } 1043 1044 @Override 1045 public List<?> findByNamedQuery(String queryName, Object value) throws DataAccessException { 1046 return findByNamedQuery(queryName, new Object[] {value}); 1047 } 1048 1049 @Override 1050 public List<?> findByNamedQuery(final String queryName, final Object... values) throws DataAccessException { 1051 return executeWithNativeSession(new HibernateCallback<List<?>>() { 1052 @Override 1053 public List<?> doInHibernate(Session session) throws HibernateException { 1054 Query queryObject = session.getNamedQuery(queryName); 1055 prepareQuery(queryObject); 1056 if (values != null) { 1057 for (int i = 0; i < values.length; i++) { 1058 queryObject.setParameter(i, values[i]); 1059 } 1060 } 1061 return queryObject.list(); 1062 } 1063 }); 1064 } 1065 1066 @Override 1067 public List<?> findByNamedQueryAndNamedParam(String queryName, String paramName, Object value) 1068 throws DataAccessException { 1069 1070 return findByNamedQueryAndNamedParam(queryName, new String[] {paramName}, new Object[] {value}); 1071 } 1072 1073 @Override 1074 public List<?> findByNamedQueryAndNamedParam( 1075 final String queryName, final String[] paramNames, final Object[] values) 1076 throws DataAccessException { 1077 1078 if (values != null && (paramNames == null || paramNames.length != values.length)) { 1079 throw new IllegalArgumentException("Length of paramNames array must match length of values array"); 1080 } 1081 return executeWithNativeSession(new HibernateCallback<List<?>>() { 1082 @Override 1083 public List<?> doInHibernate(Session session) throws HibernateException { 1084 Query queryObject = session.getNamedQuery(queryName); 1085 prepareQuery(queryObject); 1086 if (values != null) { 1087 for (int i = 0; i < values.length; i++) { 1088 applyNamedParameterToQuery(queryObject, paramNames[i], values[i]); 1089 } 1090 } 1091 return queryObject.list(); 1092 } 1093 }); 1094 } 1095 1096 @Override 1097 public List<?> findByNamedQueryAndValueBean(final String queryName, final Object valueBean) 1098 throws DataAccessException { 1099 1100 return executeWithNativeSession(new HibernateCallback<List<?>>() { 1101 @Override 1102 public List<?> doInHibernate(Session session) throws HibernateException { 1103 Query queryObject = session.getNamedQuery(queryName); 1104 prepareQuery(queryObject); 1105 queryObject.setProperties(valueBean); 1106 return queryObject.list(); 1107 } 1108 }); 1109 } 1110 1111 1112 //------------------------------------------------------------------------- 1113 // Convenience finder methods for detached criteria 1114 //------------------------------------------------------------------------- 1115 1116 @Override 1117 public List<?> findByCriteria(DetachedCriteria criteria) throws DataAccessException { 1118 return findByCriteria(criteria, -1, -1); 1119 } 1120 1121 @Override 1122 public List<?> findByCriteria(final DetachedCriteria criteria, final int firstResult, final int maxResults) 1123 throws DataAccessException { 1124 1125 Assert.notNull(criteria, "DetachedCriteria must not be null"); 1126 return executeWithNativeSession(new HibernateCallback<List<?>>() { 1127 @Override 1128 public List<?> doInHibernate(Session session) throws HibernateException { 1129 Criteria executableCriteria = criteria.getExecutableCriteria(session); 1130 prepareCriteria(executableCriteria); 1131 if (firstResult >= 0) { 1132 executableCriteria.setFirstResult(firstResult); 1133 } 1134 if (maxResults > 0) { 1135 executableCriteria.setMaxResults(maxResults); 1136 } 1137 return executableCriteria.list(); 1138 } 1139 }); 1140 } 1141 1142 @Override 1143 public <T> List<T> findByExample(T exampleEntity) throws DataAccessException { 1144 return findByExample(null, exampleEntity, -1, -1); 1145 } 1146 1147 @Override 1148 public <T> List<T> findByExample(String entityName, T exampleEntity) throws DataAccessException { 1149 return findByExample(entityName, exampleEntity, -1, -1); 1150 } 1151 1152 @Override 1153 public <T> List<T> findByExample(T exampleEntity, int firstResult, int maxResults) throws DataAccessException { 1154 return findByExample(null, exampleEntity, firstResult, maxResults); 1155 } 1156 1157 @Override 1158 public <T> List<T> findByExample( 1159 final String entityName, final T exampleEntity, final int firstResult, final int maxResults) 1160 throws DataAccessException { 1161 1162 Assert.notNull(exampleEntity, "Example entity must not be null"); 1163 return executeWithNativeSession(new HibernateCallback<List<T>>() { 1164 @Override 1165 @SuppressWarnings("unchecked") 1166 public List<T> doInHibernate(Session session) throws HibernateException { 1167 Criteria executableCriteria = (entityName != null ? 1168 session.createCriteria(entityName) : session.createCriteria(exampleEntity.getClass())); 1169 executableCriteria.add(Example.create(exampleEntity)); 1170 prepareCriteria(executableCriteria); 1171 if (firstResult >= 0) { 1172 executableCriteria.setFirstResult(firstResult); 1173 } 1174 if (maxResults > 0) { 1175 executableCriteria.setMaxResults(maxResults); 1176 } 1177 return executableCriteria.list(); 1178 } 1179 }); 1180 } 1181 1182 1183 //------------------------------------------------------------------------- 1184 // Convenience query methods for iteration and bulk updates/deletes 1185 //------------------------------------------------------------------------- 1186 1187 @Override 1188 public Iterator<?> iterate(String queryString) throws DataAccessException { 1189 return iterate(queryString, (Object[]) null); 1190 } 1191 1192 @Override 1193 public Iterator<?> iterate(String queryString, Object value) throws DataAccessException { 1194 return iterate(queryString, new Object[] {value}); 1195 } 1196 1197 @Override 1198 public Iterator<?> iterate(final String queryString, final Object... values) throws DataAccessException { 1199 return executeWithNativeSession(new HibernateCallback<Iterator<?>>() { 1200 @Override 1201 public Iterator<?> doInHibernate(Session session) throws HibernateException { 1202 Query queryObject = session.createQuery(queryString); 1203 prepareQuery(queryObject); 1204 if (values != null) { 1205 for (int i = 0; i < values.length; i++) { 1206 queryObject.setParameter(i, values[i]); 1207 } 1208 } 1209 return queryObject.iterate(); 1210 } 1211 }); 1212 } 1213 1214 @Override 1215 public void closeIterator(Iterator<?> it) throws DataAccessException { 1216 try { 1217 Hibernate.close(it); 1218 } 1219 catch (HibernateException ex) { 1220 throw SessionFactoryUtils.convertHibernateAccessException(ex); 1221 } 1222 } 1223 1224 @Override 1225 public int bulkUpdate(String queryString) throws DataAccessException { 1226 return bulkUpdate(queryString, (Object[]) null); 1227 } 1228 1229 @Override 1230 public int bulkUpdate(String queryString, Object value) throws DataAccessException { 1231 return bulkUpdate(queryString, new Object[] {value}); 1232 } 1233 1234 @Override 1235 public int bulkUpdate(final String queryString, final Object... values) throws DataAccessException { 1236 return executeWithNativeSession(new HibernateCallback<Integer>() { 1237 @Override 1238 public Integer doInHibernate(Session session) throws HibernateException { 1239 Query queryObject = session.createQuery(queryString); 1240 prepareQuery(queryObject); 1241 if (values != null) { 1242 for (int i = 0; i < values.length; i++) { 1243 queryObject.setParameter(i, values[i]); 1244 } 1245 } 1246 return queryObject.executeUpdate(); 1247 } 1248 }); 1249 } 1250 1251 1252 //------------------------------------------------------------------------- 1253 // Helper methods used by the operations above 1254 //------------------------------------------------------------------------- 1255 1256 /** 1257 * Check whether write operations are allowed on the given Session. 1258 * <p>Default implementation throws an InvalidDataAccessApiUsageException in 1259 * case of {@code FlushMode.MANUAL}. Can be overridden in subclasses. 1260 * @param session current Hibernate Session 1261 * @throws InvalidDataAccessApiUsageException if write operations are not allowed 1262 * @see #setCheckWriteOperations 1263 * @see #getFlushMode() 1264 * @see #FLUSH_EAGER 1265 * @see org.hibernate.Session#getFlushMode() 1266 * @see org.hibernate.FlushMode#MANUAL 1267 */ 1268 protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException { 1269 if (isCheckWriteOperations() && getFlushMode() != FLUSH_EAGER && 1270 session.getFlushMode().lessThan(FlushMode.COMMIT)) { 1271 throw new InvalidDataAccessApiUsageException( 1272 "Write operations are not allowed in read-only mode (FlushMode.MANUAL): "+ 1273 "Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition."); 1274 } 1275 } 1276 1277 /** 1278 * Prepare the given Query object, applying cache settings and/or 1279 * a transaction timeout. 1280 * @param queryObject the Query object to prepare 1281 * @see #setCacheQueries 1282 * @see #setQueryCacheRegion 1283 * @see SessionFactoryUtils#applyTransactionTimeout 1284 */ 1285 protected void prepareQuery(Query queryObject) { 1286 if (isCacheQueries()) { 1287 queryObject.setCacheable(true); 1288 if (getQueryCacheRegion() != null) { 1289 queryObject.setCacheRegion(getQueryCacheRegion()); 1290 } 1291 } 1292 if (getFetchSize() > 0) { 1293 queryObject.setFetchSize(getFetchSize()); 1294 } 1295 if (getMaxResults() > 0) { 1296 queryObject.setMaxResults(getMaxResults()); 1297 } 1298 SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory()); 1299 } 1300 1301 /** 1302 * Prepare the given Criteria object, applying cache settings and/or 1303 * a transaction timeout. 1304 * @param criteria the Criteria object to prepare 1305 * @see #setCacheQueries 1306 * @see #setQueryCacheRegion 1307 * @see SessionFactoryUtils#applyTransactionTimeout 1308 */ 1309 protected void prepareCriteria(Criteria criteria) { 1310 if (isCacheQueries()) { 1311 criteria.setCacheable(true); 1312 if (getQueryCacheRegion() != null) { 1313 criteria.setCacheRegion(getQueryCacheRegion()); 1314 } 1315 } 1316 if (getFetchSize() > 0) { 1317 criteria.setFetchSize(getFetchSize()); 1318 } 1319 if (getMaxResults() > 0) { 1320 criteria.setMaxResults(getMaxResults()); 1321 } 1322 SessionFactoryUtils.applyTransactionTimeout(criteria, getSessionFactory()); 1323 } 1324 1325 /** 1326 * Apply the given name parameter to the given Query object. 1327 * @param queryObject the Query object 1328 * @param paramName the name of the parameter 1329 * @param value the value of the parameter 1330 * @throws HibernateException if thrown by the Query object 1331 */ 1332 protected void applyNamedParameterToQuery(Query queryObject, String paramName, Object value) 1333 throws HibernateException { 1334 1335 if (value instanceof Collection) { 1336 queryObject.setParameterList(paramName, (Collection<?>) value); 1337 } 1338 else if (value instanceof Object[]) { 1339 queryObject.setParameterList(paramName, (Object[]) value); 1340 } 1341 else { 1342 queryObject.setParameter(paramName, value); 1343 } 1344 } 1345 1346 1347 /** 1348 * Invocation handler that suppresses close calls on Hibernate Sessions. 1349 * Also prepares returned Query and Criteria objects. 1350 * @see org.hibernate.Session#close 1351 */ 1352 private class CloseSuppressingInvocationHandler implements InvocationHandler { 1353 1354 private final Session target; 1355 1356 public CloseSuppressingInvocationHandler(Session target) { 1357 this.target = target; 1358 } 1359 1360 @Override 1361 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 1362 // Invocation on Session interface coming in... 1363 1364 if (method.getName().equals("equals")) { 1365 // Only consider equal when proxies are identical. 1366 return (proxy == args[0]); 1367 } 1368 else if (method.getName().equals("hashCode")) { 1369 // Use hashCode of Session proxy. 1370 return System.identityHashCode(proxy); 1371 } 1372 else if (method.getName().equals("close")) { 1373 // Handle close method: suppress, not valid. 1374 return null; 1375 } 1376 1377 // Invoke method on target Session. 1378 try { 1379 Object retVal = method.invoke(this.target, args); 1380 1381 // If return value is a Query or Criteria, apply transaction timeout. 1382 // Applies to createQuery, getNamedQuery, createCriteria. 1383 if (retVal instanceof Query) { 1384 prepareQuery(((Query) retVal)); 1385 } 1386 if (retVal instanceof Criteria) { 1387 prepareCriteria(((Criteria) retVal)); 1388 } 1389 1390 return retVal; 1391 } 1392 catch (InvocationTargetException ex) { 1393 throw ex.getTargetException(); 1394 } 1395 } 1396 } 1397 1398}