0001: /*
0002: * Copyright 2002-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.springframework.orm.hibernate;
0018:
0019: import java.io.Serializable;
0020: import java.lang.reflect.InvocationHandler;
0021: import java.lang.reflect.InvocationTargetException;
0022: import java.lang.reflect.Method;
0023: import java.lang.reflect.Proxy;
0024: import java.sql.SQLException;
0025: import java.util.Collection;
0026: import java.util.Iterator;
0027: import java.util.List;
0028:
0029: import net.sf.hibernate.Criteria;
0030: import net.sf.hibernate.FlushMode;
0031: import net.sf.hibernate.Hibernate;
0032: import net.sf.hibernate.HibernateException;
0033: import net.sf.hibernate.LockMode;
0034: import net.sf.hibernate.Query;
0035: import net.sf.hibernate.ReplicationMode;
0036: import net.sf.hibernate.Session;
0037: import net.sf.hibernate.SessionFactory;
0038: import net.sf.hibernate.type.Type;
0039:
0040: import org.springframework.dao.DataAccessException;
0041: import org.springframework.dao.InvalidDataAccessApiUsageException;
0042: import org.springframework.util.Assert;
0043:
0044: /**
0045: * Helper class that simplifies Hibernate data access code, and converts
0046: * checked HibernateExceptions into unchecked DataAccessExceptions,
0047: * following the <code>org.springframework.dao</code> exception hierarchy.
0048: * Uses the same SQLExceptionTranslator mechanism as JdbcTemplate.
0049: *
0050: * <p>Typically used to implement data access or business logic services that
0051: * use Hibernate within their implementation but are Hibernate-agnostic in their
0052: * interface. The latter or code calling the latter only have to deal with
0053: * domain objects, query objects, and <code>org.springframework.dao</code> exceptions.
0054: *
0055: * <p>The central method is <code>execute</code>, supporting Hibernate access code
0056: * implementing the {@link HibernateCallback} interface. It provides Hibernate Session
0057: * handling such that neither the HibernateCallback implementation nor the calling
0058: * code needs to explicitly care about retrieving/closing Hibernate Sessions,
0059: * or handling Session lifecycle exceptions. For typical single step actions,
0060: * there are various convenience methods (find, load, saveOrUpdate, delete).
0061: *
0062: * <p>Can be used within a service implementation via direct instantiation
0063: * with a SessionFactory reference, or get prepared in an application context
0064: * and given to services as bean reference. Note: The SessionFactory should
0065: * always be configured as bean in the application context, in the first case
0066: * given to the service directly, in the second case to the prepared template.
0067: *
0068: * <p>This class can be considered as direct alternative to working with the raw
0069: * Hibernate Session API (through <code>SessionFactoryUtils.getSession()</code>).
0070: * The major advantage is its automatic conversion to DataAccessExceptions, the
0071: * major disadvantage that no checked application exceptions can get thrown from
0072: * within data access code. Corresponding checks and the actual throwing of such
0073: * exceptions can often be deferred to after callback execution, though.
0074: *
0075: * <p>Note that even if {@link HibernateTransactionManager} is used for transaction
0076: * demarcation in higher-level services, all those services above the data
0077: * access layer don't need to be Hibernate-aware. Setting such a special
0078: * PlatformTransactionManager is a configuration issue: For example,
0079: * switching to JTA is just a matter of Spring configuration (use
0080: * JtaTransactionManager instead) that does not affect application code.
0081: *
0082: * <p>{@link LocalSessionFactoryBean} is the preferred way of obtaining a reference
0083: * to a specific Hibernate SessionFactory, at least in a non-EJB environment.
0084: * The Spring application context will manage its lifecycle, initializing and
0085: * shutting down the factory as part of the application.
0086: *
0087: * <p>Note that operations that return an Iterator (i.e. <code>iterate</code>)
0088: * are supposed to be used within Spring-driven or JTA-driven transactions
0089: * (with HibernateTransactionManager, JtaTransactionManager, or EJB CMT).
0090: * Else, the Iterator won't be able to read results from its ResultSet anymore,
0091: * as the underlying Hibernate Session will already have been closed.
0092: *
0093: * <p>Lazy loading will also just work with an open Hibernate Session,
0094: * either within a transaction or within OpenSessionInViewFilter/Interceptor.
0095: * Furthermore, some operations just make sense within transactions,
0096: * for example: <code>contains</code>, <code>evict</code>, <code>lock</code>,
0097: * <code>flush</code>, <code>clear</code>.
0098: *
0099: * <p>Note: Spring's Hibernate support in this package requires Hibernate 2.1.
0100: * Dedicated Hibernate3 support can be found in a separate package:
0101: * <code>org.springframework.orm.hibernate3</code>.
0102: *
0103: * @author Juergen Hoeller
0104: * @since 02.05.2003
0105: * @see #setSessionFactory
0106: * @see #setJdbcExceptionTranslator
0107: * @see HibernateCallback
0108: * @see net.sf.hibernate.Session
0109: * @see HibernateInterceptor
0110: * @see LocalSessionFactoryBean
0111: * @see org.springframework.jndi.JndiObjectFactoryBean
0112: * @see org.springframework.jdbc.support.SQLExceptionTranslator
0113: * @see HibernateTransactionManager
0114: * @see org.springframework.transaction.jta.JtaTransactionManager
0115: * @see org.springframework.orm.hibernate.support.OpenSessionInViewFilter
0116: * @see org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor
0117: */
0118: public class HibernateTemplate extends HibernateAccessor implements
0119: HibernateOperations {
0120:
0121: private boolean allowCreate = true;
0122:
0123: private boolean alwaysUseNewSession = false;
0124:
0125: private boolean exposeNativeSession = false;
0126:
0127: private boolean checkWriteOperations = true;
0128:
0129: private boolean cacheQueries = false;
0130:
0131: private String queryCacheRegion;
0132:
0133: private int fetchSize = 0;
0134:
0135: private int maxResults = 0;
0136:
0137: /**
0138: * Create a new HibernateTemplate instance.
0139: */
0140: public HibernateTemplate() {
0141: }
0142:
0143: /**
0144: * Create a new HibernateTemplate instance.
0145: * @param sessionFactory SessionFactory to create Sessions
0146: */
0147: public HibernateTemplate(SessionFactory sessionFactory) {
0148: setSessionFactory(sessionFactory);
0149: afterPropertiesSet();
0150: }
0151:
0152: /**
0153: * Create a new HibernateTemplate instance.
0154: * @param sessionFactory SessionFactory to create Sessions
0155: * @param allowCreate if a non-transactional Session should be created when no
0156: * transactional Session can be found for the current thread
0157: */
0158: public HibernateTemplate(SessionFactory sessionFactory,
0159: boolean allowCreate) {
0160: setSessionFactory(sessionFactory);
0161: setAllowCreate(allowCreate);
0162: afterPropertiesSet();
0163: }
0164:
0165: /**
0166: * Set if a new Session should be created when no transactional Session
0167: * can be found for the current thread.
0168: * <p>HibernateTemplate is aware of a corresponding Session bound to the
0169: * current thread, for example when using HibernateTransactionManager.
0170: * If allowCreate is true, a new non-transactional Session will be created
0171: * if none found, which needs to be closed at the end of the operation.
0172: * If false, an IllegalStateException will get thrown in this case.
0173: * @see SessionFactoryUtils#getSession(SessionFactory, boolean)
0174: */
0175: public void setAllowCreate(boolean allowCreate) {
0176: this .allowCreate = allowCreate;
0177: }
0178:
0179: /**
0180: * Return if a new Session should be created if no thread-bound found.
0181: */
0182: public boolean isAllowCreate() {
0183: return this .allowCreate;
0184: }
0185:
0186: /**
0187: * Set whether to always use a new Hibernate Session for this template.
0188: * Default is "false"; if activated, all operations on this template will
0189: * work on a new Hibernate Session even in case of a pre-bound Session
0190: * (for example, within a transaction or OpenSessionInViewFilter).
0191: * <p>Within a transaction, a new Hibernate Session used by this template
0192: * will participate in the transaction through using the same JDBC
0193: * Connection. In such a scenario, multiple Sessions will participate
0194: * in the same database transaction.
0195: * <p>Turn this on for operations that are supposed to always execute
0196: * independently, without side effects caused by a shared Hibernate Session.
0197: */
0198: public void setAlwaysUseNewSession(boolean alwaysUseNewSession) {
0199: this .alwaysUseNewSession = alwaysUseNewSession;
0200: }
0201:
0202: /**
0203: * Return whether to always use a new Hibernate Session for this template.
0204: */
0205: public boolean isAlwaysUseNewSession() {
0206: return this .alwaysUseNewSession;
0207: }
0208:
0209: /**
0210: * Set whether to expose the native Hibernate Session to HibernateCallback
0211: * code. Default is "false": a Session proxy will be returned,
0212: * suppressing <code>close</code> calls and automatically applying
0213: * query cache settings and transaction timeouts.
0214: * @see HibernateCallback
0215: * @see net.sf.hibernate.Session
0216: * @see #setCacheQueries
0217: * @see #setQueryCacheRegion
0218: * @see #prepareQuery
0219: * @see #prepareCriteria
0220: */
0221: public void setExposeNativeSession(boolean exposeNativeSession) {
0222: this .exposeNativeSession = exposeNativeSession;
0223: }
0224:
0225: /**
0226: * Return whether to expose the native Hibernate Session to HibernateCallback
0227: * code, or rather a Session proxy.
0228: */
0229: public boolean isExposeNativeSession() {
0230: return this .exposeNativeSession;
0231: }
0232:
0233: /**
0234: * Set whether to check that the Hibernate Session is not in read-only mode
0235: * in case of write operations (save/update/delete).
0236: * <p>Default is "true", for fail-fast behavior when attempting write operations
0237: * within a read-only transaction. Turn this off to allow save/update/delete
0238: * on a Session with flush mode NEVER.
0239: * @see #setFlushMode
0240: * @see #checkWriteOperationAllowed
0241: * @see org.springframework.transaction.TransactionDefinition#isReadOnly
0242: */
0243: public void setCheckWriteOperations(boolean checkWriteOperations) {
0244: this .checkWriteOperations = checkWriteOperations;
0245: }
0246:
0247: /**
0248: * Return whether to check that the Hibernate Session is not in read-only
0249: * mode in case of write operations (save/update/delete).
0250: */
0251: public boolean isCheckWriteOperations() {
0252: return this .checkWriteOperations;
0253: }
0254:
0255: /**
0256: * Set whether to cache all queries executed by this template.
0257: * If this is true, all Query and Criteria objects created by
0258: * this template will be marked as cacheable (including all
0259: * queries through find methods).
0260: * <p>To specify the query region to be used for queries cached
0261: * by this template, set the "queryCacheRegion" property.
0262: * @see #setQueryCacheRegion
0263: * @see net.sf.hibernate.Query#setCacheable
0264: * @see net.sf.hibernate.Criteria#setCacheable
0265: */
0266: public void setCacheQueries(boolean cacheQueries) {
0267: this .cacheQueries = cacheQueries;
0268: }
0269:
0270: /**
0271: * Return whether to cache all queries executed by this template.
0272: */
0273: public boolean isCacheQueries() {
0274: return this .cacheQueries;
0275: }
0276:
0277: /**
0278: * Set the name of the cache region for queries executed by this template.
0279: * If this is specified, it will be applied to all Query and Criteria objects
0280: * created by this template (including all queries through find methods).
0281: * <p>The cache region will not take effect unless queries created by this
0282: * template are configured to be cached via the "cacheQueries" property.
0283: * @see #setCacheQueries
0284: * @see net.sf.hibernate.Query#setCacheRegion
0285: * @see net.sf.hibernate.Criteria#setCacheRegion
0286: */
0287: public void setQueryCacheRegion(String queryCacheRegion) {
0288: this .queryCacheRegion = queryCacheRegion;
0289: }
0290:
0291: /**
0292: * Return the name of the cache region for queries executed by this template.
0293: */
0294: public String getQueryCacheRegion() {
0295: return this .queryCacheRegion;
0296: }
0297:
0298: /**
0299: * Set the fetch size for this HibernateTemplate. This is important for processing
0300: * large result sets: Setting this higher than the default value will increase
0301: * processing speed at the cost of memory consumption; setting this lower can
0302: * avoid transferring row data that will never be read by the application.
0303: * <p>Default is 0, indicating to use the JDBC driver's default.
0304: */
0305: public void setFetchSize(int fetchSize) {
0306: this .fetchSize = fetchSize;
0307: }
0308:
0309: /**
0310: * Return the fetch size specified for this HibernateTemplate.
0311: */
0312: public int getFetchSize() {
0313: return this .fetchSize;
0314: }
0315:
0316: /**
0317: * Set the maximum number of rows for this HibernateTemplate. This is important
0318: * for processing subsets of large result sets, avoiding to read and hold
0319: * the entire result set in the database or in the JDBC driver if we're
0320: * never interested in the entire result in the first place (for example,
0321: * when performing searches that might return a large number of matches).
0322: * <p>Default is 0, indicating to use the JDBC driver's default.
0323: */
0324: public void setMaxResults(int maxResults) {
0325: this .maxResults = maxResults;
0326: }
0327:
0328: /**
0329: * Return the maximum number of rows specified for this HibernateTemplate.
0330: */
0331: public int getMaxResults() {
0332: return this .maxResults;
0333: }
0334:
0335: public Object execute(HibernateCallback action)
0336: throws DataAccessException {
0337: return execute(action, isExposeNativeSession());
0338: }
0339:
0340: public List executeFind(HibernateCallback action)
0341: throws DataAccessException {
0342: Object result = execute(action, isExposeNativeSession());
0343: if (result != null && !(result instanceof List)) {
0344: throw new InvalidDataAccessApiUsageException(
0345: "Result object returned from HibernateCallback isn't a List: ["
0346: + result + "]");
0347: }
0348: return (List) result;
0349: }
0350:
0351: /**
0352: * Execute the action specified by the given action object within a Session.
0353: * @param action callback object that specifies the Hibernate action
0354: * @param exposeNativeSession whether to expose the native Hibernate Session
0355: * to callback code
0356: * @return a result object returned by the action, or <code>null</code>
0357: * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
0358: */
0359: public Object execute(HibernateCallback action,
0360: boolean exposeNativeSession) throws DataAccessException {
0361: Assert.notNull(action, "Callback object must not be null");
0362:
0363: Session session = getSession();
0364: boolean existingTransaction = SessionFactoryUtils
0365: .isSessionTransactional(session, getSessionFactory());
0366: if (existingTransaction) {
0367: logger
0368: .debug("Found thread-bound Session for HibernateTemplate");
0369: }
0370:
0371: FlushMode previousFlushMode = null;
0372: try {
0373: previousFlushMode = applyFlushMode(session,
0374: existingTransaction);
0375: Session sessionToExpose = (exposeNativeSession ? session
0376: : createSessionProxy(session));
0377: Object result = action.doInHibernate(sessionToExpose);
0378: flushIfNecessary(session, existingTransaction);
0379: return result;
0380: } catch (HibernateException ex) {
0381: throw convertHibernateAccessException(ex);
0382: } catch (SQLException ex) {
0383: throw convertJdbcAccessException(ex);
0384: } catch (RuntimeException ex) {
0385: // Callback code threw application exception...
0386: throw ex;
0387: } finally {
0388: if (existingTransaction) {
0389: logger
0390: .debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
0391: if (previousFlushMode != null) {
0392: session.setFlushMode(previousFlushMode);
0393: }
0394: } else {
0395: // Never use deferred close for an explicitly new Session.
0396: if (isAlwaysUseNewSession()) {
0397: SessionFactoryUtils.closeSession(session);
0398: } else {
0399: SessionFactoryUtils
0400: .closeSessionOrRegisterDeferredClose(
0401: session, getSessionFactory());
0402: }
0403: }
0404: }
0405: }
0406:
0407: /**
0408: * Return a Session for use by this template.
0409: * <p>Returns a new Session in case of "alwaysUseNewSession" (using the same
0410: * JDBC Connection as a transactional Session, if applicable), a pre-bound
0411: * Session in case of "allowCreate" turned off, and a pre-bound or new Session
0412: * else (new only if no transactional or otherwise pre-bound Session exists).
0413: * @see SessionFactoryUtils#getSession
0414: * @see SessionFactoryUtils#getNewSession
0415: * @see #setAlwaysUseNewSession
0416: * @see #setAllowCreate
0417: */
0418: protected Session getSession() {
0419: if (isAlwaysUseNewSession()) {
0420: return SessionFactoryUtils.getNewSession(
0421: getSessionFactory(), getEntityInterceptor());
0422: } else if (!isAllowCreate()) {
0423: return SessionFactoryUtils.getSession(getSessionFactory(),
0424: false);
0425: } else {
0426: return SessionFactoryUtils.getSession(getSessionFactory(),
0427: getEntityInterceptor(),
0428: getJdbcExceptionTranslator());
0429: }
0430: }
0431:
0432: /**
0433: * Create a close-suppressing proxy for the given Hibernate Session.
0434: * The proxy also prepares returned Query and Criteria objects.
0435: * @param session the Hibernate Session to create a proxy for
0436: * @return the Session proxy
0437: * @see net.sf.hibernate.Session#close()
0438: * @see #prepareQuery
0439: * @see #prepareCriteria
0440: */
0441: protected Session createSessionProxy(Session session) {
0442: return (Session) Proxy.newProxyInstance(getClass()
0443: .getClassLoader(), new Class[] { Session.class },
0444: new CloseSuppressingInvocationHandler(session));
0445: }
0446:
0447: //-------------------------------------------------------------------------
0448: // Convenience methods for loading individual objects
0449: //-------------------------------------------------------------------------
0450:
0451: public Object get(Class entityClass, Serializable id)
0452: throws DataAccessException {
0453: return get(entityClass, id, null);
0454: }
0455:
0456: public Object get(final Class entityClass, final Serializable id,
0457: final LockMode lockMode) throws DataAccessException {
0458:
0459: return execute(new HibernateCallback() {
0460: public Object doInHibernate(Session session)
0461: throws HibernateException {
0462: if (lockMode != null) {
0463: return session.get(entityClass, id, lockMode);
0464: } else {
0465: return session.get(entityClass, id);
0466: }
0467: }
0468: }, true);
0469: }
0470:
0471: public Object load(Class entityClass, Serializable id)
0472: throws DataAccessException {
0473: return load(entityClass, id, null);
0474: }
0475:
0476: public Object load(final Class entityClass, final Serializable id,
0477: final LockMode lockMode) throws DataAccessException {
0478:
0479: return execute(new HibernateCallback() {
0480: public Object doInHibernate(Session session)
0481: throws HibernateException {
0482: if (lockMode != null) {
0483: return session.load(entityClass, id, lockMode);
0484: } else {
0485: return session.load(entityClass, id);
0486: }
0487: }
0488: }, true);
0489: }
0490:
0491: public List loadAll(final Class entityClass)
0492: throws DataAccessException {
0493: return (List) execute(new HibernateCallback() {
0494: public Object doInHibernate(Session session)
0495: throws HibernateException {
0496: Criteria criteria = session.createCriteria(entityClass);
0497: prepareCriteria(criteria);
0498: return criteria.list();
0499: }
0500: }, true);
0501: }
0502:
0503: public void load(final Object entity, final Serializable id)
0504: throws DataAccessException {
0505: execute(new HibernateCallback() {
0506: public Object doInHibernate(Session session)
0507: throws HibernateException {
0508: session.load(entity, id);
0509: return null;
0510: }
0511: }, true);
0512: }
0513:
0514: public void refresh(final Object entity) throws DataAccessException {
0515: refresh(entity, null);
0516: }
0517:
0518: public void refresh(final Object entity, final LockMode lockMode)
0519: throws DataAccessException {
0520: execute(new HibernateCallback() {
0521: public Object doInHibernate(Session session)
0522: throws HibernateException {
0523: if (lockMode != null) {
0524: session.refresh(entity, lockMode);
0525: } else {
0526: session.refresh(entity);
0527: }
0528: return null;
0529: }
0530: }, true);
0531: }
0532:
0533: public boolean contains(final Object entity)
0534: throws DataAccessException {
0535: Boolean result = (Boolean) execute(new HibernateCallback() {
0536: public Object doInHibernate(Session session) {
0537: return (session.contains(entity) ? Boolean.TRUE
0538: : Boolean.FALSE);
0539: }
0540: }, true);
0541: return result.booleanValue();
0542: }
0543:
0544: public void evict(final Object entity) throws DataAccessException {
0545: execute(new HibernateCallback() {
0546: public Object doInHibernate(Session session)
0547: throws HibernateException {
0548: session.evict(entity);
0549: return null;
0550: }
0551: }, true);
0552: }
0553:
0554: public void initialize(Object proxy) throws DataAccessException {
0555: try {
0556: Hibernate.initialize(proxy);
0557: } catch (HibernateException ex) {
0558: throw SessionFactoryUtils
0559: .convertHibernateAccessException(ex);
0560: }
0561: }
0562:
0563: //-------------------------------------------------------------------------
0564: // Convenience methods for storing individual objects
0565: //-------------------------------------------------------------------------
0566:
0567: public void lock(final Object entity, final LockMode lockMode)
0568: throws DataAccessException {
0569: execute(new HibernateCallback() {
0570: public Object doInHibernate(Session session)
0571: throws HibernateException {
0572: session.lock(entity, lockMode);
0573: return null;
0574: }
0575: }, true);
0576: }
0577:
0578: public Serializable save(final Object entity)
0579: throws DataAccessException {
0580: return (Serializable) execute(new HibernateCallback() {
0581: public Object doInHibernate(Session session)
0582: throws HibernateException {
0583: checkWriteOperationAllowed(session);
0584: return session.save(entity);
0585: }
0586: }, true);
0587: }
0588:
0589: public void save(final Object entity, final Serializable id)
0590: throws DataAccessException {
0591: execute(new HibernateCallback() {
0592: public Object doInHibernate(Session session)
0593: throws HibernateException {
0594: checkWriteOperationAllowed(session);
0595: session.save(entity, id);
0596: return null;
0597: }
0598: }, true);
0599: }
0600:
0601: public void update(Object entity) throws DataAccessException {
0602: update(entity, null);
0603: }
0604:
0605: public void update(final Object entity, final LockMode lockMode)
0606: throws DataAccessException {
0607: execute(new HibernateCallback() {
0608: public Object doInHibernate(Session session)
0609: throws HibernateException {
0610: checkWriteOperationAllowed(session);
0611: session.update(entity);
0612: if (lockMode != null) {
0613: session.lock(entity, lockMode);
0614: }
0615: return null;
0616: }
0617: }, true);
0618: }
0619:
0620: public void saveOrUpdate(final Object entity)
0621: throws DataAccessException {
0622: execute(new HibernateCallback() {
0623: public Object doInHibernate(Session session)
0624: throws HibernateException {
0625: checkWriteOperationAllowed(session);
0626: session.saveOrUpdate(entity);
0627: return null;
0628: }
0629: }, true);
0630: }
0631:
0632: public void saveOrUpdateAll(final Collection entities)
0633: throws DataAccessException {
0634: execute(new HibernateCallback() {
0635: public Object doInHibernate(Session session)
0636: throws HibernateException {
0637: checkWriteOperationAllowed(session);
0638: for (Iterator it = entities.iterator(); it.hasNext();) {
0639: session.saveOrUpdate(it.next());
0640: }
0641: return null;
0642: }
0643: }, true);
0644: }
0645:
0646: public Object saveOrUpdateCopy(final Object entity)
0647: throws DataAccessException {
0648: return execute(new HibernateCallback() {
0649: public Object doInHibernate(Session session)
0650: throws HibernateException {
0651: checkWriteOperationAllowed(session);
0652: return session.saveOrUpdateCopy(entity);
0653: }
0654: }, true);
0655: }
0656:
0657: public void replicate(final Object entity,
0658: final ReplicationMode replicationMode)
0659: throws DataAccessException {
0660:
0661: execute(new HibernateCallback() {
0662: public Object doInHibernate(Session session)
0663: throws HibernateException {
0664: checkWriteOperationAllowed(session);
0665: session.replicate(entity, replicationMode);
0666: return null;
0667: }
0668: }, true);
0669: }
0670:
0671: public void delete(Object entity) throws DataAccessException {
0672: delete(entity, null);
0673: }
0674:
0675: public void delete(final Object entity, final LockMode lockMode)
0676: throws DataAccessException {
0677: execute(new HibernateCallback() {
0678: public Object doInHibernate(Session session)
0679: throws HibernateException {
0680: checkWriteOperationAllowed(session);
0681: if (lockMode != null) {
0682: session.lock(entity, lockMode);
0683: }
0684: session.delete(entity);
0685: return null;
0686: }
0687: }, true);
0688: }
0689:
0690: public void deleteAll(final Collection entities)
0691: throws DataAccessException {
0692: execute(new HibernateCallback() {
0693: public Object doInHibernate(Session session)
0694: throws HibernateException {
0695: checkWriteOperationAllowed(session);
0696: for (Iterator it = entities.iterator(); it.hasNext();) {
0697: session.delete(it.next());
0698: }
0699: return null;
0700: }
0701: }, true);
0702: }
0703:
0704: public void flush() throws DataAccessException {
0705: execute(new HibernateCallback() {
0706: public Object doInHibernate(Session session)
0707: throws HibernateException {
0708: session.flush();
0709: return null;
0710: }
0711: }, true);
0712: }
0713:
0714: public void clear() throws DataAccessException {
0715: execute(new HibernateCallback() {
0716: public Object doInHibernate(Session session) {
0717: session.clear();
0718: return null;
0719: }
0720: }, true);
0721: }
0722:
0723: //-------------------------------------------------------------------------
0724: // Convenience finder methods for HQL strings
0725: //-------------------------------------------------------------------------
0726:
0727: public List find(String queryString) throws DataAccessException {
0728: return find(queryString, (Object[]) null, (Type[]) null);
0729: }
0730:
0731: public List find(String queryString, Object value)
0732: throws DataAccessException {
0733: return find(queryString, new Object[] { value }, (Type[]) null);
0734: }
0735:
0736: public List find(String queryString, Object value, Type type)
0737: throws DataAccessException {
0738: return find(queryString, new Object[] { value },
0739: new Type[] { type });
0740: }
0741:
0742: public List find(String queryString, Object[] values)
0743: throws DataAccessException {
0744: return find(queryString, values, (Type[]) null);
0745: }
0746:
0747: public List find(final String queryString, final Object[] values,
0748: final Type[] types) throws DataAccessException {
0749:
0750: if (values != null && types != null
0751: && values.length != types.length) {
0752: throw new IllegalArgumentException(
0753: "Length of values array must match length of types array");
0754: }
0755: return (List) execute(new HibernateCallback() {
0756: public Object doInHibernate(Session session)
0757: throws HibernateException {
0758: Query queryObject = session.createQuery(queryString);
0759: prepareQuery(queryObject);
0760: if (values != null) {
0761: for (int i = 0; i < values.length; i++) {
0762: if (types != null && types[i] != null) {
0763: queryObject.setParameter(i, values[i],
0764: types[i]);
0765: } else {
0766: queryObject.setParameter(i, values[i]);
0767: }
0768: }
0769: }
0770: return queryObject.list();
0771: }
0772: }, true);
0773: }
0774:
0775: public List findByNamedParam(String queryString, String paramName,
0776: Object value) throws DataAccessException {
0777:
0778: return findByNamedParam(queryString, paramName, value, null);
0779: }
0780:
0781: public List findByNamedParam(String queryString, String paramName,
0782: Object value, Type type) throws DataAccessException {
0783:
0784: return findByNamedParam(queryString,
0785: new String[] { paramName }, new Object[] { value },
0786: new Type[] { type });
0787: }
0788:
0789: public List findByNamedParam(String queryString,
0790: String[] paramNames, Object[] values)
0791: throws DataAccessException {
0792:
0793: return findByNamedParam(queryString, paramNames, values, null);
0794: }
0795:
0796: public List findByNamedParam(final String queryString,
0797: final String[] paramNames, final Object[] values,
0798: final Type[] types) throws DataAccessException {
0799:
0800: if (paramNames.length != values.length) {
0801: throw new IllegalArgumentException(
0802: "Length of paramNames array must match length of values array");
0803: }
0804: if (types != null && paramNames.length != types.length) {
0805: throw new IllegalArgumentException(
0806: "Length of paramNames array must match length of types array");
0807: }
0808: return (List) execute(new HibernateCallback() {
0809: public Object doInHibernate(Session session)
0810: throws HibernateException {
0811: Query queryObject = session.createQuery(queryString);
0812: prepareQuery(queryObject);
0813: if (values != null) {
0814: for (int i = 0; i < values.length; i++) {
0815: applyNamedParameterToQuery(queryObject,
0816: paramNames[i], values[i],
0817: (types != null ? types[i] : null));
0818: }
0819: }
0820: return queryObject.list();
0821: }
0822: }, true);
0823: }
0824:
0825: public List findByValueBean(final String queryString,
0826: final Object valueBean) throws DataAccessException {
0827:
0828: return (List) execute(new HibernateCallback() {
0829: public Object doInHibernate(Session session)
0830: throws HibernateException {
0831: Query queryObject = session.createQuery(queryString);
0832: prepareQuery(queryObject);
0833: queryObject.setProperties(valueBean);
0834: return queryObject.list();
0835: }
0836: }, true);
0837: }
0838:
0839: //-------------------------------------------------------------------------
0840: // Convenience finder methods for named queries
0841: //-------------------------------------------------------------------------
0842:
0843: public List findByNamedQuery(String queryName)
0844: throws DataAccessException {
0845: return findByNamedQuery(queryName, (Object[]) null,
0846: (Type[]) null);
0847: }
0848:
0849: public List findByNamedQuery(String queryName, Object value)
0850: throws DataAccessException {
0851: return findByNamedQuery(queryName, new Object[] { value },
0852: (Type[]) null);
0853: }
0854:
0855: public List findByNamedQuery(String queryName, Object value,
0856: Type type) throws DataAccessException {
0857: return findByNamedQuery(queryName, new Object[] { value },
0858: new Type[] { type });
0859: }
0860:
0861: public List findByNamedQuery(String queryName, Object[] values)
0862: throws DataAccessException {
0863: return findByNamedQuery(queryName, values, (Type[]) null);
0864: }
0865:
0866: public List findByNamedQuery(final String queryName,
0867: final Object[] values, final Type[] types)
0868: throws DataAccessException {
0869:
0870: if (values != null && types != null
0871: && values.length != types.length) {
0872: throw new IllegalArgumentException(
0873: "Length of values array must match length of types array");
0874: }
0875: return (List) execute(new HibernateCallback() {
0876: public Object doInHibernate(Session session)
0877: throws HibernateException {
0878: Query queryObject = session.getNamedQuery(queryName);
0879: prepareQuery(queryObject);
0880: if (values != null) {
0881: for (int i = 0; i < values.length; i++) {
0882: if (types != null && types[i] != null) {
0883: queryObject.setParameter(i, values[i],
0884: types[i]);
0885: } else {
0886: queryObject.setParameter(i, values[i]);
0887: }
0888: }
0889: }
0890: return queryObject.list();
0891: }
0892: }, true);
0893: }
0894:
0895: public List findByNamedQueryAndNamedParam(String queryName,
0896: String paramName, Object value) throws DataAccessException {
0897:
0898: return findByNamedQueryAndNamedParam(queryName, paramName,
0899: value, null);
0900: }
0901:
0902: public List findByNamedQueryAndNamedParam(String queryName,
0903: String paramName, Object value, Type type)
0904: throws DataAccessException {
0905:
0906: return findByNamedQueryAndNamedParam(queryName,
0907: new String[] { paramName }, new Object[] { value },
0908: new Type[] { type });
0909: }
0910:
0911: public List findByNamedQueryAndNamedParam(String queryName,
0912: String[] paramNames, Object[] values)
0913: throws DataAccessException {
0914:
0915: return findByNamedQueryAndNamedParam(queryName, paramNames,
0916: values, null);
0917: }
0918:
0919: public List findByNamedQueryAndNamedParam(final String queryName,
0920: final String[] paramNames, final Object[] values,
0921: final Type[] types) throws DataAccessException {
0922:
0923: if (paramNames != null && values != null
0924: && paramNames.length != values.length) {
0925: throw new IllegalArgumentException(
0926: "Length of paramNames array must match length of values array");
0927: }
0928: if (values != null && types != null
0929: && paramNames.length != types.length) {
0930: throw new IllegalArgumentException(
0931: "Length of paramNames array must match length of types array");
0932: }
0933: return (List) execute(new HibernateCallback() {
0934: public Object doInHibernate(Session session)
0935: throws HibernateException {
0936: Query queryObject = session.getNamedQuery(queryName);
0937: prepareQuery(queryObject);
0938: if (values != null) {
0939: for (int i = 0; i < values.length; i++) {
0940: applyNamedParameterToQuery(queryObject,
0941: paramNames[i], values[i],
0942: (types != null ? types[i] : null));
0943: }
0944: }
0945: return queryObject.list();
0946: }
0947: }, true);
0948: }
0949:
0950: public List findByNamedQueryAndValueBean(final String queryName,
0951: final Object valueBean) throws DataAccessException {
0952:
0953: return (List) execute(new HibernateCallback() {
0954: public Object doInHibernate(Session session)
0955: throws HibernateException {
0956: Query queryObject = session.getNamedQuery(queryName);
0957: prepareQuery(queryObject);
0958: queryObject.setProperties(valueBean);
0959: return queryObject.list();
0960: }
0961: }, true);
0962: }
0963:
0964: //-------------------------------------------------------------------------
0965: // Convenience query methods for iterate and delete
0966: //-------------------------------------------------------------------------
0967:
0968: public Iterator iterate(String queryString)
0969: throws DataAccessException {
0970: return iterate(queryString, (Object[]) null, (Type[]) null);
0971: }
0972:
0973: public Iterator iterate(String queryString, Object value)
0974: throws DataAccessException {
0975: return iterate(queryString, new Object[] { value },
0976: (Type[]) null);
0977: }
0978:
0979: public Iterator iterate(String queryString, Object value, Type type)
0980: throws DataAccessException {
0981:
0982: return iterate(queryString, new Object[] { value },
0983: new Type[] { type });
0984: }
0985:
0986: public Iterator iterate(String queryString, Object[] values)
0987: throws DataAccessException {
0988: return iterate(queryString, values, (Type[]) null);
0989: }
0990:
0991: public Iterator iterate(final String queryString,
0992: final Object[] values, final Type[] types)
0993: throws DataAccessException {
0994:
0995: if (values != null && types != null
0996: && values.length != types.length) {
0997: throw new IllegalArgumentException(
0998: "Length of values array must match length of types array");
0999: }
1000: return (Iterator) execute(new HibernateCallback() {
1001: public Object doInHibernate(Session session)
1002: throws HibernateException {
1003: Query queryObject = session.createQuery(queryString);
1004: prepareQuery(queryObject);
1005: if (values != null) {
1006: for (int i = 0; i < values.length; i++) {
1007: if (types != null && types[i] != null) {
1008: queryObject.setParameter(i, values[i],
1009: types[i]);
1010: } else {
1011: queryObject.setParameter(i, values[i]);
1012: }
1013: }
1014: }
1015: return queryObject.iterate();
1016: }
1017: }, true);
1018: }
1019:
1020: public void closeIterator(Iterator it) throws DataAccessException {
1021: try {
1022: Hibernate.close(it);
1023: } catch (HibernateException ex) {
1024: throw SessionFactoryUtils
1025: .convertHibernateAccessException(ex);
1026: }
1027: }
1028:
1029: public int delete(String queryString) throws DataAccessException {
1030: return delete(queryString, (Object[]) null, (Type[]) null);
1031: }
1032:
1033: public int delete(String queryString, Object value, Type type)
1034: throws DataAccessException {
1035:
1036: return delete(queryString, new Object[] { value },
1037: new Type[] { type });
1038: }
1039:
1040: public int delete(final String queryString, final Object[] values,
1041: final Type[] types) throws DataAccessException {
1042:
1043: if (values != null && types != null
1044: && values.length != types.length) {
1045: throw new IllegalArgumentException(
1046: "Length of values array must match length of types array");
1047: }
1048: Integer deleteCount = (Integer) execute(
1049: new HibernateCallback() {
1050: public Object doInHibernate(Session session)
1051: throws HibernateException {
1052: checkWriteOperationAllowed(session);
1053: if (values != null) {
1054: return new Integer(session.delete(
1055: queryString, values, types));
1056: } else {
1057: return new Integer(session
1058: .delete(queryString));
1059: }
1060: }
1061: }, true);
1062: return deleteCount.intValue();
1063: }
1064:
1065: //-------------------------------------------------------------------------
1066: // Helper methods used by the operations above
1067: //-------------------------------------------------------------------------
1068:
1069: /**
1070: * Check whether write operations are allowed on the given Session.
1071: * <p>Default implementation throws an InvalidDataAccessApiUsageException
1072: * in case of <code>FlushMode.NEVER</code>. Can be overridden in subclasses.
1073: * @param session current Hibernate Session
1074: * @throws InvalidDataAccessApiUsageException if write operations are not allowed
1075: * @see #setCheckWriteOperations
1076: * @see #getFlushMode()
1077: * @see #FLUSH_EAGER
1078: * @see net.sf.hibernate.Session#getFlushMode()
1079: * @see net.sf.hibernate.FlushMode#NEVER
1080: */
1081: protected void checkWriteOperationAllowed(Session session)
1082: throws InvalidDataAccessApiUsageException {
1083: if (isCheckWriteOperations() && getFlushMode() != FLUSH_EAGER
1084: && FlushMode.NEVER.equals(session.getFlushMode())) {
1085: throw new InvalidDataAccessApiUsageException(
1086: "Write operations are not allowed in read-only mode (FlushMode.NEVER): "
1087: + "Turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition.");
1088: }
1089: }
1090:
1091: /**
1092: * Prepare the given Query object, applying cache settings and/or
1093: * a transaction timeout.
1094: * @param queryObject the Query object to prepare
1095: * @see #setCacheQueries
1096: * @see #setQueryCacheRegion
1097: * @see SessionFactoryUtils#applyTransactionTimeout
1098: */
1099: protected void prepareQuery(Query queryObject) {
1100: if (isCacheQueries()) {
1101: queryObject.setCacheable(true);
1102: if (getQueryCacheRegion() != null) {
1103: queryObject.setCacheRegion(getQueryCacheRegion());
1104: }
1105: }
1106: if (getFetchSize() > 0) {
1107: queryObject.setFetchSize(getFetchSize());
1108: }
1109: if (getMaxResults() > 0) {
1110: queryObject.setMaxResults(getMaxResults());
1111: }
1112: SessionFactoryUtils.applyTransactionTimeout(queryObject,
1113: getSessionFactory());
1114: }
1115:
1116: /**
1117: * Prepare the given Criteria object, applying cache settings and/or
1118: * a transaction timeout.
1119: * @param criteria the Criteria object to prepare
1120: * @see #setCacheQueries
1121: * @see #setQueryCacheRegion
1122: * @see SessionFactoryUtils#applyTransactionTimeout
1123: */
1124: protected void prepareCriteria(Criteria criteria) {
1125: if (isCacheQueries()) {
1126: criteria.setCacheable(true);
1127: if (getQueryCacheRegion() != null) {
1128: criteria.setCacheRegion(getQueryCacheRegion());
1129: }
1130: }
1131: if (getFetchSize() > 0) {
1132: criteria.setFetchSize(getFetchSize());
1133: }
1134: if (getMaxResults() > 0) {
1135: criteria.setMaxResults(getMaxResults());
1136: }
1137: SessionFactoryUtils.applyTransactionTimeout(criteria,
1138: getSessionFactory());
1139: }
1140:
1141: /**
1142: * Apply the given name parameter to the given Query object.
1143: * @param queryObject the Query object
1144: * @param paramName the name of the parameter
1145: * @param value the value of the parameter
1146: * @param type Hibernate type of the parameter (or <code>null</code> if none specified)
1147: * @throws HibernateException if thrown by the Query object
1148: */
1149: protected void applyNamedParameterToQuery(Query queryObject,
1150: String paramName, Object value, Type type)
1151: throws HibernateException {
1152:
1153: if (value instanceof Collection) {
1154: if (type != null) {
1155: queryObject.setParameterList(paramName,
1156: (Collection) value, type);
1157: } else {
1158: queryObject.setParameterList(paramName,
1159: (Collection) value);
1160: }
1161: } else if (value instanceof Object[]) {
1162: if (type != null) {
1163: queryObject.setParameterList(paramName,
1164: (Object[]) value, type);
1165: } else {
1166: queryObject.setParameterList(paramName,
1167: (Object[]) value);
1168: }
1169: } else {
1170: if (type != null) {
1171: queryObject.setParameter(paramName, value, type);
1172: } else {
1173: queryObject.setParameter(paramName, value);
1174: }
1175: }
1176: }
1177:
1178: /**
1179: * Invocation handler that suppresses close calls on Hibernate Sessions.
1180: * Also prepares returned Query and Criteria objects.
1181: * @see net.sf.hibernate.Session#close
1182: */
1183: private class CloseSuppressingInvocationHandler implements
1184: InvocationHandler {
1185:
1186: private final Session target;
1187:
1188: public CloseSuppressingInvocationHandler(Session target) {
1189: this .target = target;
1190: }
1191:
1192: public Object invoke(Object proxy, Method method, Object[] args)
1193: throws Throwable {
1194: // Invocation on Session interface coming in...
1195:
1196: if (method.getName().equals("equals")) {
1197: // Only consider equal when proxies are identical.
1198: return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
1199: } else if (method.getName().equals("hashCode")) {
1200: // Use hashCode of Session proxy.
1201: return new Integer(hashCode());
1202: } else if (method.getName().equals("close")) {
1203: // Handle close method: suppress, not valid.
1204: return null;
1205: }
1206:
1207: // Invoke method on target Session.
1208: try {
1209: Object retVal = method.invoke(this .target, args);
1210:
1211: // If return value is a Query or Criteria, apply transaction timeout.
1212: // Applies to createQuery, getNamedQuery, createCriteria.
1213: if (retVal instanceof Query) {
1214: prepareQuery(((Query) retVal));
1215: }
1216: if (retVal instanceof Criteria) {
1217: prepareCriteria(((Criteria) retVal));
1218: }
1219:
1220: return retVal;
1221: } catch (InvocationTargetException ex) {
1222: throw ex.getTargetException();
1223: }
1224: }
1225: }
1226:
1227: }
|