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