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.InvalidDataAccessApiUsageException;
0045: import org.springframework.util.Assert;
0046:
0047: /**
0048: * Helper class that simplifies Hibernate data access code. Automatically
0049: * converts HibernateExceptions into DataAccessExceptions, following the
0050: * <code>org.springframework.dao</code> exception hierarchy.
0051: *
0052: * <p><b>NOTE: As of Hibernate 3.0.1, transactional Hibernate access code can
0053: * also be coded in plain Hibernate style. Hence, for newly started projects,
0054: * consider adopting the standard Hibernate3 style of coding data access objects
0055: * instead, based on <code>SessionFactory.getCurrentSession()</code>.</b>
0056: * (Spring's LocalSessionFactoryBean automatically supports Spring transaction
0057: * management for the Hibernate3 <code>getCurrentSession()</code> method.)
0058: *
0059: * <p>The central method is <code>execute</code>, supporting Hibernate access code
0060: * implementing the {@link HibernateCallback} interface. It provides Hibernate Session
0061: * handling such that neither the HibernateCallback implementation nor the calling
0062: * code needs to explicitly care about retrieving/closing Hibernate Sessions,
0063: * or handling Session lifecycle exceptions. For typical single step actions,
0064: * there are various convenience methods (find, load, saveOrUpdate, delete).
0065: *
0066: * <p>Can be used within a service implementation via direct instantiation
0067: * with a SessionFactory reference, or get prepared in an application context
0068: * and given to services as bean reference. Note: The SessionFactory should
0069: * always be configured as bean in the application context, in the first case
0070: * given to the service directly, in the second case to the prepared template.
0071: *
0072: * <p>This class can be considered as direct alternative to working with the raw
0073: * Hibernate3 Session API (through <code>SessionFactory.getCurrentSession()</code>).
0074: * The major advantage is its automatic conversion to DataAccessExceptions, the
0075: * major disadvantage that no checked application exceptions can get thrown from
0076: * within data access code. Corresponding checks and the actual throwing of such
0077: * exceptions can often be deferred to after callback execution, though.
0078: *
0079: * <p>Note that even if {@link HibernateTransactionManager} is used for transaction
0080: * demarcation in higher-level services, all those services above the data
0081: * access layer don't need to be Hibernate-aware. Setting such a special
0082: * PlatformTransactionManager is a configuration issue: For example,
0083: * switching to JTA is just a matter of Spring configuration (use
0084: * JtaTransactionManager instead) that does not affect application code.
0085: *
0086: * <p>{@link LocalSessionFactoryBean} is the preferred way of obtaining a reference
0087: * to a specific Hibernate SessionFactory, at least in a non-EJB environment.
0088: * The Spring application context will manage its lifecycle, initializing and
0089: * shutting down the factory as part of the application.
0090: *
0091: * <p>Note that operations that return an Iterator (i.e. <code>iterate</code>)
0092: * are supposed to be used within Spring-driven or JTA-driven transactions
0093: * (with HibernateTransactionManager, JtaTransactionManager, or EJB CMT).
0094: * Else, the Iterator won't be able to read results from its ResultSet anymore,
0095: * as the underlying Hibernate Session will already have been closed.
0096: *
0097: * <p>Lazy loading will also just work with an open Hibernate Session,
0098: * either within a transaction or within OpenSessionInViewFilter/Interceptor.
0099: * Furthermore, some operations just make sense within transactions,
0100: * for example: <code>contains</code>, <code>evict</code>, <code>lock</code>,
0101: * <code>flush</code>, <code>clear</code>.
0102: *
0103: * @author Juergen Hoeller
0104: * @since 1.2
0105: * @see org.hibernate.SessionFactory#getCurrentSession()
0106: * @see #setSessionFactory
0107: * @see HibernateCallback
0108: * @see org.hibernate.Session
0109: * @see LocalSessionFactoryBean
0110: * @see HibernateTransactionManager
0111: * @see org.springframework.transaction.jta.JtaTransactionManager
0112: * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
0113: * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
0114: */
0115: public class HibernateTemplate extends HibernateAccessor implements
0116: HibernateOperations {
0117:
0118: private boolean allowCreate = true;
0119:
0120: private boolean alwaysUseNewSession = false;
0121:
0122: private boolean exposeNativeSession = false;
0123:
0124: private boolean checkWriteOperations = true;
0125:
0126: private boolean cacheQueries = false;
0127:
0128: private String queryCacheRegion;
0129:
0130: private int fetchSize = 0;
0131:
0132: private int maxResults = 0;
0133:
0134: /**
0135: * Create a new HibernateTemplate instance.
0136: */
0137: public HibernateTemplate() {
0138: }
0139:
0140: /**
0141: * Create a new HibernateTemplate instance.
0142: * @param sessionFactory SessionFactory to create Sessions
0143: */
0144: public HibernateTemplate(SessionFactory sessionFactory) {
0145: setSessionFactory(sessionFactory);
0146: afterPropertiesSet();
0147: }
0148:
0149: /**
0150: * Create a new HibernateTemplate instance.
0151: * @param sessionFactory SessionFactory to create Sessions
0152: * @param allowCreate if a non-transactional Session should be created when no
0153: * transactional Session can be found for the current thread
0154: */
0155: public HibernateTemplate(SessionFactory sessionFactory,
0156: boolean allowCreate) {
0157: setSessionFactory(sessionFactory);
0158: setAllowCreate(allowCreate);
0159: afterPropertiesSet();
0160: }
0161:
0162: /**
0163: * Set if a new {@link Session} should be created when no transactional
0164: * <code>Session</code> can be found for the current thread.
0165: * The default value is "<code>true</code>".
0166: * <p><code>HibernateTemplate</code> is aware of a corresponding
0167: * <code>Session</code> bound to the current thread, for example when using
0168: * {@link HibernateTransactionManager}. If <code>allowCreate</code> is
0169: * <code>true</code>, a new non-transactional <code>Session</code> will be
0170: * created if none is found, which needs to be closed at the end of the operation.
0171: * If <code>false</code>, an {@link IllegalStateException} will get thrown in
0172: * 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
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 = 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: * @see SessionFactoryUtils#getSession
0417: * @see SessionFactoryUtils#getNewSession
0418: * @see #setAlwaysUseNewSession
0419: * @see #setAllowCreate
0420: */
0421: protected Session getSession() {
0422: if (isAlwaysUseNewSession()) {
0423: return SessionFactoryUtils.getNewSession(
0424: getSessionFactory(), getEntityInterceptor());
0425: } else if (!isAllowCreate()) {
0426: return SessionFactoryUtils.getSession(getSessionFactory(),
0427: false);
0428: } else {
0429: return SessionFactoryUtils.getSession(getSessionFactory(),
0430: getEntityInterceptor(),
0431: getJdbcExceptionTranslator());
0432: }
0433: }
0434:
0435: /**
0436: * Create a close-suppressing proxy for the given Hibernate Session.
0437: * The proxy also prepares returned Query and Criteria objects.
0438: * @param session the Hibernate Session to create a proxy for
0439: * @return the Session proxy
0440: * @see org.hibernate.Session#close()
0441: * @see #prepareQuery
0442: * @see #prepareCriteria
0443: */
0444: protected Session createSessionProxy(Session session) {
0445: Class[] sessionIfcs = null;
0446: if (session instanceof SessionImplementor) {
0447: sessionIfcs = new Class[] { Session.class,
0448: SessionImplementor.class };
0449: } else {
0450: sessionIfcs = new Class[] { Session.class };
0451: }
0452: return (Session) Proxy.newProxyInstance(getClass()
0453: .getClassLoader(), sessionIfcs,
0454: new CloseSuppressingInvocationHandler(session));
0455: }
0456:
0457: //-------------------------------------------------------------------------
0458: // Convenience methods for loading individual objects
0459: //-------------------------------------------------------------------------
0460:
0461: public Object get(Class entityClass, Serializable id)
0462: throws DataAccessException {
0463: return get(entityClass, id, null);
0464: }
0465:
0466: public Object get(final Class entityClass, final Serializable id,
0467: final LockMode lockMode) throws DataAccessException {
0468:
0469: return execute(new HibernateCallback() {
0470: public Object doInHibernate(Session session)
0471: throws HibernateException {
0472: if (lockMode != null) {
0473: return session.get(entityClass, id, lockMode);
0474: } else {
0475: return session.get(entityClass, id);
0476: }
0477: }
0478: }, true);
0479: }
0480:
0481: public Object get(String entityName, Serializable id)
0482: throws DataAccessException {
0483: return get(entityName, id, null);
0484: }
0485:
0486: public Object get(final String entityName, final Serializable id,
0487: final LockMode lockMode) throws DataAccessException {
0488:
0489: return execute(new HibernateCallback() {
0490: public Object doInHibernate(Session session)
0491: throws HibernateException {
0492: if (lockMode != null) {
0493: return session.get(entityName, id, lockMode);
0494: } else {
0495: return session.get(entityName, id);
0496: }
0497: }
0498: }, true);
0499: }
0500:
0501: public Object load(Class entityClass, Serializable id)
0502: throws DataAccessException {
0503: return load(entityClass, id, null);
0504: }
0505:
0506: public Object load(final Class entityClass, final Serializable id,
0507: final LockMode lockMode) throws DataAccessException {
0508:
0509: return execute(new HibernateCallback() {
0510: public Object doInHibernate(Session session)
0511: throws HibernateException {
0512: if (lockMode != null) {
0513: return session.load(entityClass, id, lockMode);
0514: } else {
0515: return session.load(entityClass, id);
0516: }
0517: }
0518: }, true);
0519: }
0520:
0521: public Object load(String entityName, Serializable id)
0522: throws DataAccessException {
0523: return load(entityName, id, null);
0524: }
0525:
0526: public Object load(final String entityName, final Serializable id,
0527: final LockMode lockMode) throws DataAccessException {
0528:
0529: return execute(new HibernateCallback() {
0530: public Object doInHibernate(Session session)
0531: throws HibernateException {
0532: if (lockMode != null) {
0533: return session.load(entityName, id, lockMode);
0534: } else {
0535: return session.load(entityName, id);
0536: }
0537: }
0538: }, true);
0539: }
0540:
0541: public List loadAll(final Class entityClass)
0542: throws DataAccessException {
0543: return (List) execute(new HibernateCallback() {
0544: public Object doInHibernate(Session session)
0545: throws HibernateException {
0546: Criteria criteria = session.createCriteria(entityClass);
0547: prepareCriteria(criteria);
0548: return criteria.list();
0549: }
0550: }, true);
0551: }
0552:
0553: public void load(final Object entity, final Serializable id)
0554: throws DataAccessException {
0555: execute(new HibernateCallback() {
0556: public Object doInHibernate(Session session)
0557: throws HibernateException {
0558: session.load(entity, id);
0559: return null;
0560: }
0561: }, true);
0562: }
0563:
0564: public void refresh(final Object entity) throws DataAccessException {
0565: refresh(entity, null);
0566: }
0567:
0568: public void refresh(final Object entity, final LockMode lockMode)
0569: throws DataAccessException {
0570: execute(new HibernateCallback() {
0571: public Object doInHibernate(Session session)
0572: throws HibernateException {
0573: if (lockMode != null) {
0574: session.refresh(entity, lockMode);
0575: } else {
0576: session.refresh(entity);
0577: }
0578: return null;
0579: }
0580: }, true);
0581: }
0582:
0583: public boolean contains(final Object entity)
0584: throws DataAccessException {
0585: Boolean result = (Boolean) execute(new HibernateCallback() {
0586: public Object doInHibernate(Session session) {
0587: return (session.contains(entity) ? Boolean.TRUE
0588: : Boolean.FALSE);
0589: }
0590: }, true);
0591: return result.booleanValue();
0592: }
0593:
0594: public void evict(final Object entity) throws DataAccessException {
0595: execute(new HibernateCallback() {
0596: public Object doInHibernate(Session session)
0597: throws HibernateException {
0598: session.evict(entity);
0599: return null;
0600: }
0601: }, true);
0602: }
0603:
0604: public void initialize(Object proxy) throws DataAccessException {
0605: try {
0606: Hibernate.initialize(proxy);
0607: } catch (HibernateException ex) {
0608: throw SessionFactoryUtils
0609: .convertHibernateAccessException(ex);
0610: }
0611: }
0612:
0613: public Filter enableFilter(String filterName)
0614: throws IllegalStateException {
0615: Session session = SessionFactoryUtils.getSession(
0616: getSessionFactory(), false);
0617: Filter filter = session.getEnabledFilter(filterName);
0618: if (filter == null) {
0619: filter = session.enableFilter(filterName);
0620: }
0621: return filter;
0622: }
0623:
0624: //-------------------------------------------------------------------------
0625: // Convenience methods for storing individual objects
0626: //-------------------------------------------------------------------------
0627:
0628: public void lock(final Object entity, final LockMode lockMode)
0629: throws DataAccessException {
0630: execute(new HibernateCallback() {
0631: public Object doInHibernate(Session session)
0632: throws HibernateException {
0633: session.lock(entity, lockMode);
0634: return null;
0635: }
0636: }, true);
0637: }
0638:
0639: public void lock(final String entityName, final Object entity,
0640: final LockMode lockMode) throws DataAccessException {
0641:
0642: execute(new HibernateCallback() {
0643: public Object doInHibernate(Session session)
0644: throws HibernateException {
0645: session.lock(entityName, entity, lockMode);
0646: return null;
0647: }
0648: }, true);
0649: }
0650:
0651: public Serializable save(final Object entity)
0652: throws DataAccessException {
0653: return (Serializable) execute(new HibernateCallback() {
0654: public Object doInHibernate(Session session)
0655: throws HibernateException {
0656: checkWriteOperationAllowed(session);
0657: return session.save(entity);
0658: }
0659: }, true);
0660: }
0661:
0662: public Serializable save(final String entityName,
0663: final Object entity) throws DataAccessException {
0664: return (Serializable) execute(new HibernateCallback() {
0665: public Object doInHibernate(Session session)
0666: throws HibernateException {
0667: checkWriteOperationAllowed(session);
0668: return session.save(entityName, entity);
0669: }
0670: }, true);
0671: }
0672:
0673: public void update(Object entity) throws DataAccessException {
0674: update(entity, null);
0675: }
0676:
0677: public void update(final Object entity, final LockMode lockMode)
0678: throws DataAccessException {
0679: execute(new HibernateCallback() {
0680: public Object doInHibernate(Session session)
0681: throws HibernateException {
0682: checkWriteOperationAllowed(session);
0683: session.update(entity);
0684: if (lockMode != null) {
0685: session.lock(entity, lockMode);
0686: }
0687: return null;
0688: }
0689: }, true);
0690: }
0691:
0692: public void update(String entityName, Object entity)
0693: throws DataAccessException {
0694: update(entityName, entity, null);
0695: }
0696:
0697: public void update(final String entityName, final Object entity,
0698: final LockMode lockMode) throws DataAccessException {
0699:
0700: execute(new HibernateCallback() {
0701: public Object doInHibernate(Session session)
0702: throws HibernateException {
0703: checkWriteOperationAllowed(session);
0704: session.update(entityName, entity);
0705: if (lockMode != null) {
0706: session.lock(entity, lockMode);
0707: }
0708: return null;
0709: }
0710: }, true);
0711: }
0712:
0713: public void saveOrUpdate(final Object entity)
0714: throws DataAccessException {
0715: execute(new HibernateCallback() {
0716: public Object doInHibernate(Session session)
0717: throws HibernateException {
0718: checkWriteOperationAllowed(session);
0719: session.saveOrUpdate(entity);
0720: return null;
0721: }
0722: }, true);
0723: }
0724:
0725: public void saveOrUpdate(final String entityName,
0726: final Object entity) throws DataAccessException {
0727: execute(new HibernateCallback() {
0728: public Object doInHibernate(Session session)
0729: throws HibernateException {
0730: checkWriteOperationAllowed(session);
0731: session.saveOrUpdate(entityName, entity);
0732: return null;
0733: }
0734: }, true);
0735: }
0736:
0737: public void saveOrUpdateAll(final Collection entities)
0738: throws DataAccessException {
0739: execute(new HibernateCallback() {
0740: public Object doInHibernate(Session session)
0741: throws HibernateException {
0742: checkWriteOperationAllowed(session);
0743: for (Iterator it = entities.iterator(); it.hasNext();) {
0744: session.saveOrUpdate(it.next());
0745: }
0746: return null;
0747: }
0748: }, true);
0749: }
0750:
0751: public void replicate(final Object entity,
0752: final ReplicationMode replicationMode)
0753: throws DataAccessException {
0754:
0755: execute(new HibernateCallback() {
0756: public Object doInHibernate(Session session)
0757: throws HibernateException {
0758: checkWriteOperationAllowed(session);
0759: session.replicate(entity, replicationMode);
0760: return null;
0761: }
0762: }, true);
0763: }
0764:
0765: public void replicate(final String entityName, final Object entity,
0766: final ReplicationMode replicationMode)
0767: throws DataAccessException {
0768:
0769: execute(new HibernateCallback() {
0770: public Object doInHibernate(Session session)
0771: throws HibernateException {
0772: checkWriteOperationAllowed(session);
0773: session.replicate(entityName, entity, replicationMode);
0774: return null;
0775: }
0776: }, true);
0777: }
0778:
0779: public void persist(final Object entity) throws DataAccessException {
0780: execute(new HibernateCallback() {
0781: public Object doInHibernate(Session session)
0782: throws HibernateException {
0783: checkWriteOperationAllowed(session);
0784: session.persist(entity);
0785: return null;
0786: }
0787: }, true);
0788: }
0789:
0790: public void persist(final String entityName, final Object entity)
0791: throws DataAccessException {
0792: execute(new HibernateCallback() {
0793: public Object doInHibernate(Session session)
0794: throws HibernateException {
0795: checkWriteOperationAllowed(session);
0796: session.persist(entityName, entity);
0797: return null;
0798: }
0799: }, true);
0800: }
0801:
0802: public Object merge(final Object entity) throws DataAccessException {
0803: return execute(new HibernateCallback() {
0804: public Object doInHibernate(Session session)
0805: throws HibernateException {
0806: checkWriteOperationAllowed(session);
0807: return session.merge(entity);
0808: }
0809: }, true);
0810: }
0811:
0812: public Object merge(final String entityName, final Object entity)
0813: throws DataAccessException {
0814: return execute(new HibernateCallback() {
0815: public Object doInHibernate(Session session)
0816: throws HibernateException {
0817: checkWriteOperationAllowed(session);
0818: return session.merge(entityName, entity);
0819: }
0820: }, true);
0821: }
0822:
0823: public void delete(Object entity) throws DataAccessException {
0824: delete(entity, null);
0825: }
0826:
0827: public void delete(final Object entity, final LockMode lockMode)
0828: throws DataAccessException {
0829: execute(new HibernateCallback() {
0830: public Object doInHibernate(Session session)
0831: throws HibernateException {
0832: checkWriteOperationAllowed(session);
0833: if (lockMode != null) {
0834: session.lock(entity, lockMode);
0835: }
0836: session.delete(entity);
0837: return null;
0838: }
0839: }, true);
0840: }
0841:
0842: public void deleteAll(final Collection entities)
0843: throws DataAccessException {
0844: execute(new HibernateCallback() {
0845: public Object doInHibernate(Session session)
0846: throws HibernateException {
0847: checkWriteOperationAllowed(session);
0848: for (Iterator it = entities.iterator(); it.hasNext();) {
0849: session.delete(it.next());
0850: }
0851: return null;
0852: }
0853: }, true);
0854: }
0855:
0856: public void flush() throws DataAccessException {
0857: execute(new HibernateCallback() {
0858: public Object doInHibernate(Session session)
0859: throws HibernateException {
0860: session.flush();
0861: return null;
0862: }
0863: }, true);
0864: }
0865:
0866: public void clear() throws DataAccessException {
0867: execute(new HibernateCallback() {
0868: public Object doInHibernate(Session session) {
0869: session.clear();
0870: return null;
0871: }
0872: }, true);
0873: }
0874:
0875: //-------------------------------------------------------------------------
0876: // Convenience finder methods for HQL strings
0877: //-------------------------------------------------------------------------
0878:
0879: public List find(String queryString) throws DataAccessException {
0880: return find(queryString, (Object[]) null);
0881: }
0882:
0883: public List find(String queryString, Object value)
0884: throws DataAccessException {
0885: return find(queryString, new Object[] { value });
0886: }
0887:
0888: public List find(final String queryString, final Object[] values)
0889: throws DataAccessException {
0890: return (List) execute(new HibernateCallback() {
0891: public Object doInHibernate(Session session)
0892: throws HibernateException {
0893: Query queryObject = session.createQuery(queryString);
0894: prepareQuery(queryObject);
0895: if (values != null) {
0896: for (int i = 0; i < values.length; i++) {
0897: queryObject.setParameter(i, values[i]);
0898: }
0899: }
0900: return queryObject.list();
0901: }
0902: }, true);
0903: }
0904:
0905: public List findByNamedParam(String queryString, String paramName,
0906: Object value) throws DataAccessException {
0907:
0908: return findByNamedParam(queryString,
0909: new String[] { paramName }, new Object[] { value });
0910: }
0911:
0912: public List findByNamedParam(final String queryString,
0913: final String[] paramNames, final Object[] values)
0914: throws DataAccessException {
0915:
0916: if (paramNames.length != values.length) {
0917: throw new IllegalArgumentException(
0918: "Length of paramNames array must match length of values array");
0919: }
0920: return (List) execute(new HibernateCallback() {
0921: public Object doInHibernate(Session session)
0922: throws HibernateException {
0923: Query queryObject = session.createQuery(queryString);
0924: prepareQuery(queryObject);
0925: if (values != null) {
0926: for (int i = 0; i < values.length; i++) {
0927: applyNamedParameterToQuery(queryObject,
0928: paramNames[i], values[i]);
0929: }
0930: }
0931: return queryObject.list();
0932: }
0933: }, true);
0934: }
0935:
0936: public List findByValueBean(final String queryString,
0937: final Object valueBean) throws DataAccessException {
0938:
0939: return (List) execute(new HibernateCallback() {
0940: public Object doInHibernate(Session session)
0941: throws HibernateException {
0942: Query queryObject = session.createQuery(queryString);
0943: prepareQuery(queryObject);
0944: queryObject.setProperties(valueBean);
0945: return queryObject.list();
0946: }
0947: }, true);
0948: }
0949:
0950: //-------------------------------------------------------------------------
0951: // Convenience finder methods for named queries
0952: //-------------------------------------------------------------------------
0953:
0954: public List findByNamedQuery(String queryName)
0955: throws DataAccessException {
0956: return findByNamedQuery(queryName, (Object[]) null);
0957: }
0958:
0959: public List findByNamedQuery(String queryName, Object value)
0960: throws DataAccessException {
0961: return findByNamedQuery(queryName, new Object[] { value });
0962: }
0963:
0964: public List findByNamedQuery(final String queryName,
0965: final Object[] values) throws DataAccessException {
0966: return (List) execute(new HibernateCallback() {
0967: public Object doInHibernate(Session session)
0968: throws HibernateException {
0969: Query queryObject = session.getNamedQuery(queryName);
0970: prepareQuery(queryObject);
0971: if (values != null) {
0972: for (int i = 0; i < values.length; i++) {
0973: queryObject.setParameter(i, values[i]);
0974: }
0975: }
0976: return queryObject.list();
0977: }
0978: }, true);
0979: }
0980:
0981: public List findByNamedQueryAndNamedParam(String queryName,
0982: String paramName, Object value) throws DataAccessException {
0983:
0984: return findByNamedQueryAndNamedParam(queryName,
0985: new String[] { paramName }, new Object[] { value });
0986: }
0987:
0988: public List findByNamedQueryAndNamedParam(final String queryName,
0989: final String[] paramNames, final Object[] values)
0990: throws DataAccessException {
0991:
0992: if (paramNames != null && values != null
0993: && paramNames.length != values.length) {
0994: throw new IllegalArgumentException(
0995: "Length of paramNames array must match length of values array");
0996: }
0997: return (List) execute(new HibernateCallback() {
0998: public Object doInHibernate(Session session)
0999: throws HibernateException {
1000: Query queryObject = session.getNamedQuery(queryName);
1001: prepareQuery(queryObject);
1002: if (values != null) {
1003: for (int i = 0; i < values.length; i++) {
1004: applyNamedParameterToQuery(queryObject,
1005: paramNames[i], values[i]);
1006: }
1007: }
1008: return queryObject.list();
1009: }
1010: }, true);
1011: }
1012:
1013: public List findByNamedQueryAndValueBean(final String queryName,
1014: final Object valueBean) throws DataAccessException {
1015:
1016: return (List) execute(new HibernateCallback() {
1017: public Object doInHibernate(Session session)
1018: throws HibernateException {
1019: Query queryObject = session.getNamedQuery(queryName);
1020: prepareQuery(queryObject);
1021: queryObject.setProperties(valueBean);
1022: return queryObject.list();
1023: }
1024: }, true);
1025: }
1026:
1027: //-------------------------------------------------------------------------
1028: // Convenience finder methods for detached criteria
1029: //-------------------------------------------------------------------------
1030:
1031: public List findByCriteria(DetachedCriteria criteria)
1032: throws DataAccessException {
1033: return findByCriteria(criteria, -1, -1);
1034: }
1035:
1036: public List findByCriteria(final DetachedCriteria criteria,
1037: final int firstResult, final int maxResults)
1038: throws DataAccessException {
1039:
1040: Assert.notNull(criteria, "DetachedCriteria must not be null");
1041: return (List) execute(new HibernateCallback() {
1042: public Object doInHibernate(Session session)
1043: throws HibernateException {
1044: Criteria executableCriteria = criteria
1045: .getExecutableCriteria(session);
1046: prepareCriteria(executableCriteria);
1047: if (firstResult >= 0) {
1048: executableCriteria.setFirstResult(firstResult);
1049: }
1050: if (maxResults > 0) {
1051: executableCriteria.setMaxResults(maxResults);
1052: }
1053: return executableCriteria.list();
1054: }
1055: }, true);
1056: }
1057:
1058: public List findByExample(Object exampleEntity)
1059: throws DataAccessException {
1060: return findByExample(exampleEntity, -1, -1);
1061: }
1062:
1063: public List findByExample(final Object exampleEntity,
1064: final int firstResult, final int maxResults)
1065: throws DataAccessException {
1066:
1067: Assert
1068: .notNull(exampleEntity,
1069: "Example entity must not be null");
1070: return (List) execute(new HibernateCallback() {
1071: public Object doInHibernate(Session session)
1072: throws HibernateException {
1073: Criteria executableCriteria = session
1074: .createCriteria(exampleEntity.getClass());
1075: executableCriteria.add(Example.create(exampleEntity));
1076: prepareCriteria(executableCriteria);
1077: if (firstResult >= 0) {
1078: executableCriteria.setFirstResult(firstResult);
1079: }
1080: if (maxResults > 0) {
1081: executableCriteria.setMaxResults(maxResults);
1082: }
1083: return executableCriteria.list();
1084: }
1085: }, true);
1086: }
1087:
1088: //-------------------------------------------------------------------------
1089: // Convenience query methods for iteration and bulk updates/deletes
1090: //-------------------------------------------------------------------------
1091:
1092: public Iterator iterate(String queryString)
1093: throws DataAccessException {
1094: return iterate(queryString, (Object[]) null);
1095: }
1096:
1097: public Iterator iterate(String queryString, Object value)
1098: throws DataAccessException {
1099: return iterate(queryString, new Object[] { value });
1100: }
1101:
1102: public Iterator iterate(final String queryString,
1103: final Object[] values) throws DataAccessException {
1104: return (Iterator) execute(new HibernateCallback() {
1105: public Object doInHibernate(Session session)
1106: throws HibernateException {
1107: Query queryObject = session.createQuery(queryString);
1108: prepareQuery(queryObject);
1109: if (values != null) {
1110: for (int i = 0; i < values.length; i++) {
1111: queryObject.setParameter(i, values[i]);
1112: }
1113: }
1114: return queryObject.iterate();
1115: }
1116: }, true);
1117: }
1118:
1119: public void closeIterator(Iterator it) throws DataAccessException {
1120: try {
1121: Hibernate.close(it);
1122: } catch (HibernateException ex) {
1123: throw SessionFactoryUtils
1124: .convertHibernateAccessException(ex);
1125: }
1126: }
1127:
1128: public int bulkUpdate(String queryString)
1129: throws DataAccessException {
1130: return bulkUpdate(queryString, (Object[]) null);
1131: }
1132:
1133: public int bulkUpdate(String queryString, Object value)
1134: throws DataAccessException {
1135: return bulkUpdate(queryString, new Object[] { value });
1136: }
1137:
1138: public int bulkUpdate(final String queryString,
1139: final Object[] values) throws DataAccessException {
1140: Integer updateCount = (Integer) execute(
1141: new HibernateCallback() {
1142: public Object doInHibernate(Session session)
1143: throws HibernateException {
1144: Query queryObject = session
1145: .createQuery(queryString);
1146: prepareQuery(queryObject);
1147: if (values != null) {
1148: for (int i = 0; i < values.length; i++) {
1149: queryObject.setParameter(i, values[i]);
1150: }
1151: }
1152: return new Integer(queryObject.executeUpdate());
1153: }
1154: }, true);
1155: return updateCount.intValue();
1156: }
1157:
1158: //-------------------------------------------------------------------------
1159: // Helper methods used by the operations above
1160: //-------------------------------------------------------------------------
1161:
1162: /**
1163: * Check whether write operations are allowed on the given Session.
1164: * <p>Default implementation throws an InvalidDataAccessApiUsageException in
1165: * case of <code>FlushMode.NEVER/MANUAL</code>. Can be overridden in subclasses.
1166: * @param session current Hibernate Session
1167: * @throws InvalidDataAccessApiUsageException if write operations are not allowed
1168: * @see #setCheckWriteOperations
1169: * @see #getFlushMode()
1170: * @see #FLUSH_EAGER
1171: * @see org.hibernate.Session#getFlushMode()
1172: * @see org.hibernate.FlushMode#NEVER
1173: * @see org.hibernate.FlushMode#MANUAL
1174: */
1175: protected void checkWriteOperationAllowed(Session session)
1176: throws InvalidDataAccessApiUsageException {
1177: if (isCheckWriteOperations() && getFlushMode() != FLUSH_EAGER
1178: && session.getFlushMode().lessThan(FlushMode.COMMIT)) {
1179: throw new InvalidDataAccessApiUsageException(
1180: "Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): "
1181: + "Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.");
1182: }
1183: }
1184:
1185: /**
1186: * Prepare the given Query object, applying cache settings and/or
1187: * a transaction timeout.
1188: * @param queryObject the Query object to prepare
1189: * @see #setCacheQueries
1190: * @see #setQueryCacheRegion
1191: * @see SessionFactoryUtils#applyTransactionTimeout
1192: */
1193: protected void prepareQuery(Query queryObject) {
1194: if (isCacheQueries()) {
1195: queryObject.setCacheable(true);
1196: if (getQueryCacheRegion() != null) {
1197: queryObject.setCacheRegion(getQueryCacheRegion());
1198: }
1199: }
1200: if (getFetchSize() > 0) {
1201: queryObject.setFetchSize(getFetchSize());
1202: }
1203: if (getMaxResults() > 0) {
1204: queryObject.setMaxResults(getMaxResults());
1205: }
1206: SessionFactoryUtils.applyTransactionTimeout(queryObject,
1207: getSessionFactory());
1208: }
1209:
1210: /**
1211: * Prepare the given Criteria object, applying cache settings and/or
1212: * a transaction timeout.
1213: * @param criteria the Criteria object to prepare
1214: * @see #setCacheQueries
1215: * @see #setQueryCacheRegion
1216: * @see SessionFactoryUtils#applyTransactionTimeout
1217: */
1218: protected void prepareCriteria(Criteria criteria) {
1219: if (isCacheQueries()) {
1220: criteria.setCacheable(true);
1221: if (getQueryCacheRegion() != null) {
1222: criteria.setCacheRegion(getQueryCacheRegion());
1223: }
1224: }
1225: if (getFetchSize() > 0) {
1226: criteria.setFetchSize(getFetchSize());
1227: }
1228: if (getMaxResults() > 0) {
1229: criteria.setMaxResults(getMaxResults());
1230: }
1231: SessionFactoryUtils.applyTransactionTimeout(criteria,
1232: getSessionFactory());
1233: }
1234:
1235: /**
1236: * Apply the given name parameter to the given Query object.
1237: * @param queryObject the Query object
1238: * @param paramName the name of the parameter
1239: * @param value the value of the parameter
1240: * @throws HibernateException if thrown by the Query object
1241: */
1242: protected void applyNamedParameterToQuery(Query queryObject,
1243: String paramName, Object value) throws HibernateException {
1244:
1245: if (value instanceof Collection) {
1246: queryObject.setParameterList(paramName, (Collection) value);
1247: } else if (value instanceof Object[]) {
1248: queryObject.setParameterList(paramName, (Object[]) value);
1249: } else {
1250: queryObject.setParameter(paramName, value);
1251: }
1252: }
1253:
1254: /**
1255: * Invocation handler that suppresses close calls on Hibernate Sessions.
1256: * Also prepares returned Query and Criteria objects.
1257: * @see org.hibernate.Session#close
1258: */
1259: private class CloseSuppressingInvocationHandler implements
1260: InvocationHandler {
1261:
1262: private final Session target;
1263:
1264: public CloseSuppressingInvocationHandler(Session target) {
1265: this .target = target;
1266: }
1267:
1268: public Object invoke(Object proxy, Method method, Object[] args)
1269: throws Throwable {
1270: // Invocation on Session interface coming in...
1271:
1272: if (method.getName().equals("equals")) {
1273: // Only consider equal when proxies are identical.
1274: return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
1275: } else if (method.getName().equals("hashCode")) {
1276: // Use hashCode of Session proxy.
1277: return new Integer(hashCode());
1278: } else if (method.getName().equals("close")) {
1279: // Handle close method: suppress, not valid.
1280: return null;
1281: }
1282:
1283: // Invoke method on target Session.
1284: try {
1285: Object retVal = method.invoke(this .target, args);
1286:
1287: // If return value is a Query or Criteria, apply transaction timeout.
1288: // Applies to createQuery, getNamedQuery, createCriteria.
1289: if (retVal instanceof Query) {
1290: prepareQuery(((Query) retVal));
1291: }
1292: if (retVal instanceof Criteria) {
1293: prepareCriteria(((Criteria) retVal));
1294: }
1295:
1296: return retVal;
1297: } catch (InvocationTargetException ex) {
1298: throw ex.getTargetException();
1299: }
1300: }
1301: }
1302:
1303: }
|