Java tutorial
/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.internal; import java.io.Serializable; import java.sql.Connection; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.transaction.SystemException; import org.hibernate.CacheMode; import org.hibernate.Criteria; import org.hibernate.EntityMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.ScrollMode; import org.hibernate.SessionException; import org.hibernate.StatelessSession; import org.hibernate.UnresolvableObjectException; import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.StatefulPersistenceContext; import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.query.spi.HQLQueryPlan; import org.hibernate.engine.query.spi.NativeSQLQueryPlan; import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.id.IdentifierGeneratorHelper; import org.hibernate.loader.criteria.CriteriaLoader; import org.hibernate.loader.custom.CustomLoader; import org.hibernate.loader.custom.CustomQuery; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.pretty.MessageHelper; import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.tuple.entity.EntityMetamodel; /** * @author Gavin King * @author Steve Ebersole */ public class StatelessSessionImpl extends AbstractSharedSessionContract implements StatelessSession { private static final CoreMessageLogger LOG = CoreLogging.messageLogger(StatelessSessionImpl.class); private static LoadQueryInfluencers NO_INFLUENCERS = new LoadQueryInfluencers(null) { @Override public String getInternalFetchProfile() { return null; } @Override public void setInternalFetchProfile(String internalFetchProfile) { } }; private final PersistenceContext temporaryPersistenceContext = new StatefulPersistenceContext(this); private final boolean connectionProvided; private final boolean allowBytecodeProxy; StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { super(factory, options); connectionProvided = options.getConnection() != null; allowBytecodeProxy = getFactory().getSessionFactoryOptions().isEnhancementAsProxyEnabled(); } @Override public boolean shouldAutoJoinTransaction() { return true; } // inserts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public Serializable insert(Object entity) { checkOpen(); return insert(null, entity); } @Override public Serializable insert(String entityName, Object entity) { checkOpen(); EntityPersister persister = getEntityPersister(entityName, entity); Serializable id = persister.getIdentifierGenerator().generate(this, entity); Object[] state = persister.getPropertyValues(entity); if (persister.isVersioned()) { boolean substitute = Versioning.seedVersion(state, persister.getVersionProperty(), persister.getVersionType(), this); if (substitute) { persister.setPropertyValues(entity, state); } } if (id == IdentifierGeneratorHelper.POST_INSERT_INDICATOR) { id = persister.insert(state, entity, this); } else { persister.insert(id, state, entity, this); } persister.setIdentifier(entity, id, this); return id; } // deletes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public void delete(Object entity) { checkOpen(); delete(null, entity); } @Override public void delete(String entityName, Object entity) { checkOpen(); EntityPersister persister = getEntityPersister(entityName, entity); Serializable id = persister.getIdentifier(entity, this); Object version = persister.getVersion(entity); persister.delete(id, version, entity, this); } // updates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public void update(Object entity) { checkOpen(); update(null, entity); } @Override public void update(String entityName, Object entity) { checkOpen(); EntityPersister persister = getEntityPersister(entityName, entity); Serializable id = persister.getIdentifier(entity, this); Object[] state = persister.getPropertyValues(entity); Object oldVersion; if (persister.isVersioned()) { oldVersion = persister.getVersion(entity); Object newVersion = Versioning.increment(oldVersion, persister.getVersionType(), this); Versioning.setVersion(state, newVersion, persister); persister.setPropertyValues(entity, state); } else { oldVersion = null; } persister.update(id, state, null, false, null, oldVersion, entity, null, this); } // loading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public Object get(Class entityClass, Serializable id) { return get(entityClass.getName(), id); } @Override public Object get(Class entityClass, Serializable id, LockMode lockMode) { return get(entityClass.getName(), id, lockMode); } @Override public Object get(String entityName, Serializable id) { return get(entityName, id, LockMode.NONE); } @Override public Object get(String entityName, Serializable id, LockMode lockMode) { checkOpen(); Object result = getFactory().getMetamodel().entityPersister(entityName).load(id, null, getNullSafeLockMode(lockMode), this); if (temporaryPersistenceContext.isLoadFinished()) { temporaryPersistenceContext.clear(); } return result; } @Override public void refresh(Object entity) { refresh(bestGuessEntityName(entity), entity, LockMode.NONE); } @Override public void refresh(String entityName, Object entity) { refresh(entityName, entity, LockMode.NONE); } @Override public void refresh(Object entity, LockMode lockMode) { refresh(bestGuessEntityName(entity), entity, lockMode); } @Override public void refresh(String entityName, Object entity, LockMode lockMode) { final EntityPersister persister = this.getEntityPersister(entityName, entity); final Serializable id = persister.getIdentifier(entity, this); if (LOG.isTraceEnabled()) { LOG.tracev("Refreshing transient {0}", MessageHelper.infoString(persister, id, this.getFactory())); } // TODO : can this ever happen??? // EntityKey key = new EntityKey( id, persister, source.getEntityMode() ); // if ( source.getPersistenceContext().getEntry( key ) != null ) { // throw new PersistentObjectException( // "attempted to refresh transient instance when persistent " + // "instance was already associated with the Session: " + // MessageHelper.infoString( persister, id, source.getFactory() ) // ); // } if (persister.canWriteToCache()) { final EntityDataAccess cacheAccess = persister.getCacheAccessStrategy(); if (cacheAccess != null) { final Object ck = cacheAccess.generateCacheKey(id, persister, getFactory(), getTenantIdentifier()); cacheAccess.evict(ck); } } String previousFetchProfile = this.getLoadQueryInfluencers().getInternalFetchProfile(); Object result = null; try { this.getLoadQueryInfluencers().setInternalFetchProfile("refresh"); result = persister.load(id, entity, getNullSafeLockMode(lockMode), this); } finally { this.getLoadQueryInfluencers().setInternalFetchProfile(previousFetchProfile); } UnresolvableObjectException.throwIfNull(result, id, persister.getEntityName()); if (temporaryPersistenceContext.isLoadFinished()) { temporaryPersistenceContext.clear(); } } @Override public Object immediateLoad(String entityName, Serializable id) throws HibernateException { if (getPersistenceContextInternal().isLoadFinished()) { throw new SessionException("proxies cannot be fetched by a stateless session"); } // unless we are still in the process of handling a top-level load return get(entityName, id); } @Override public void initializeCollection(PersistentCollection collection, boolean writing) throws HibernateException { throw new SessionException("collections cannot be fetched by a stateless session"); } @Override public Object instantiate(String entityName, Serializable id) throws HibernateException { checkOpen(); return getFactory().getMetamodel().entityPersister(entityName).instantiate(id, this); } @Override public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException { checkOpen(); final EntityPersister persister = getFactory().getMetamodel().entityPersister(entityName); final EntityKey entityKey = generateEntityKey(id, persister); // first, try to load it from the temp PC associated to this SS final PersistenceContext persistenceContext = getPersistenceContext(); Object loaded = persistenceContext.getEntity(entityKey); if (loaded != null) { // we found it in the temp PC. Should indicate we are in the midst of processing a result set // containing eager fetches via join fetch return loaded; } if (!eager) { // caller did not request forceful eager loading, see if we can create // some form of proxy // first, check to see if we can use "bytecode proxies" final EntityMetamodel entityMetamodel = persister.getEntityMetamodel(); final BytecodeEnhancementMetadata bytecodeEnhancementMetadata = entityMetamodel .getBytecodeEnhancementMetadata(); if (allowBytecodeProxy && bytecodeEnhancementMetadata.isEnhancedForLazyLoading()) { // if the entity defines a HibernateProxy factory, see if there is an // existing proxy associated with the PC - and if so, use it if (persister.getEntityMetamodel().getTuplizer().getProxyFactory() != null) { final Object proxy = persistenceContext.getProxy(entityKey); if (proxy != null) { if (LOG.isTraceEnabled()) { LOG.trace("Entity proxy found in session cache"); } if (LOG.isDebugEnabled() && ((HibernateProxy) proxy).getHibernateLazyInitializer().isUnwrap()) { LOG.debug("Ignoring NO_PROXY to honor laziness"); } return persistenceContext.narrowProxy(proxy, persister, entityKey, null); } // specialized handling for entities with subclasses with a HibernateProxy factory if (entityMetamodel.hasSubclasses()) { // entities with subclasses that define a ProxyFactory can create // a HibernateProxy. LOG.debugf( "Creating a HibernateProxy for to-one association with subclasses to honor laziness"); return createProxy(entityKey); } return bytecodeEnhancementMetadata.createEnhancedProxy(entityKey, false, this); } else if (!entityMetamodel.hasSubclasses()) { return bytecodeEnhancementMetadata.createEnhancedProxy(entityKey, false, this); } // If we get here, then the entity class has subclasses and there is no HibernateProxy factory. // The entity will get loaded below. } else { if (persister.hasProxy()) { final Object existingProxy = persistenceContext.getProxy(entityKey); if (existingProxy != null) { return persistenceContext.narrowProxy(existingProxy, persister, entityKey, null); } else { return createProxy(entityKey); } } } } // otherwise immediately materialize it // IMPLEMENTATION NOTE: increment/decrement the load count before/after getting the value // to ensure that #get does not clear the PersistenceContext. persistenceContext.beforeLoad(); try { return get(entityName, id); } finally { persistenceContext.afterLoad(); } } private Object createProxy(EntityKey entityKey) { final Object proxy = entityKey.getPersister().createProxy(entityKey.getIdentifier(), this); getPersistenceContext().addProxy(entityKey, proxy); return proxy; } @Override public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException { throw new UnsupportedOperationException(); } @Override public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters) throws HibernateException { throw new UnsupportedOperationException(); } @Override public List listFilter(Object collection, String filter, QueryParameters queryParameters) throws HibernateException { throw new UnsupportedOperationException(); } @Override public boolean isAutoCloseSessionEnabled() { return getFactory().getSessionFactoryOptions().isAutoCloseSessionEnabled(); } @Override public boolean shouldAutoClose() { return isAutoCloseSessionEnabled() && !isClosed(); } private boolean isFlushModeNever() { return false; } private void managedClose() { if (isClosed()) { throw new SessionException("Session was already closed!"); } close(); } private void managedFlush() { checkOpen(); getJdbcCoordinator().executeBatch(); } @Override public String bestGuessEntityName(Object object) { if (object instanceof HibernateProxy) { object = ((HibernateProxy) object).getHibernateLazyInitializer().getImplementation(); } return guessEntityName(object); } @Override public Connection connection() { checkOpen(); return getJdbcCoordinator().getLogicalConnection().getPhysicalConnection(); } @Override public int executeUpdate(String query, QueryParameters queryParameters) throws HibernateException { checkOpen(); queryParameters.validateParameters(); HQLQueryPlan plan = getQueryPlan(query, false); boolean success = false; int result = 0; try { result = plan.performExecuteUpdate(queryParameters, this); success = true; } finally { afterOperation(success); } temporaryPersistenceContext.clear(); return result; } @Override public CacheMode getCacheMode() { return CacheMode.IGNORE; } @Override public void setCacheMode(CacheMode cm) { throw new UnsupportedOperationException(); } @Override public void setFlushMode(FlushMode fm) { throw new UnsupportedOperationException(); } @Override public void setHibernateFlushMode(FlushMode flushMode) { throw new UnsupportedOperationException(); } @Override public int getDontFlushFromFind() { return 0; } @Override public Serializable getContextEntityIdentifier(Object object) { checkOpen(); return null; } public EntityMode getEntityMode() { return EntityMode.POJO; } @Override public String guessEntityName(Object entity) throws HibernateException { checkOpen(); return entity.getClass().getName(); } @Override public EntityPersister getEntityPersister(String entityName, Object object) throws HibernateException { checkOpen(); if (entityName == null) { return getFactory().getMetamodel().entityPersister(guessEntityName(object)); } else { return getFactory().getMetamodel().entityPersister(entityName).getSubclassEntityPersister(object, getFactory()); } } @Override public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException { checkOpen(); final PersistenceContext persistenceContext = getPersistenceContext(); final Object result = persistenceContext.getEntity(key); if (result != null) { return result; } final Object newObject = getInterceptor().getEntity(key.getEntityName(), key.getIdentifier()); if (newObject != null) { persistenceContext.addEntity(key, newObject); return newObject; } return null; } @Override public PersistenceContext getPersistenceContext() { return temporaryPersistenceContext; } @Override public void setAutoClear(boolean enabled) { throw new UnsupportedOperationException(); } @Override protected Object load(String entityName, Serializable identifier) { return null; } @Override public boolean isEventSource() { return false; } public boolean isDefaultReadOnly() { return false; } public void setDefaultReadOnly(boolean readOnly) throws HibernateException { if (readOnly) { throw new UnsupportedOperationException(); } } ///////////////////////////////////////////////////////////////////////////////////////////////////// //TODO: COPY/PASTE FROM SessionImpl, pull up! @Override public List list(String query, QueryParameters queryParameters) throws HibernateException { checkOpen(); queryParameters.validateParameters(); HQLQueryPlan plan = getQueryPlan(query, false); boolean success = false; List results = Collections.EMPTY_LIST; try { results = plan.performList(queryParameters, this); success = true; } finally { afterOperation(success); } temporaryPersistenceContext.clear(); return results; } public void afterOperation(boolean success) { if (!isTransactionInProgress()) { getJdbcCoordinator().afterTransaction(); } } @Override public Criteria createCriteria(Class persistentClass, String alias) { checkOpen(); return new CriteriaImpl(persistentClass.getName(), alias, this); } @Override public Criteria createCriteria(String entityName, String alias) { checkOpen(); return new CriteriaImpl(entityName, alias, this); } @Override public Criteria createCriteria(Class persistentClass) { checkOpen(); return new CriteriaImpl(persistentClass.getName(), this); } @Override public Criteria createCriteria(String entityName) { checkOpen(); return new CriteriaImpl(entityName, this); } @Override public ScrollableResultsImplementor scroll(Criteria criteria, ScrollMode scrollMode) { // TODO: Is this guaranteed to always be CriteriaImpl? CriteriaImpl criteriaImpl = (CriteriaImpl) criteria; checkOpen(); String entityName = criteriaImpl.getEntityOrClassName(); CriteriaLoader loader = new CriteriaLoader(getOuterJoinLoadable(entityName), getFactory(), criteriaImpl, entityName, getLoadQueryInfluencers()); return loader.scroll(this, scrollMode); } @Override @SuppressWarnings({ "unchecked" }) public List list(Criteria criteria) throws HibernateException { // TODO: Is this guaranteed to always be CriteriaImpl? CriteriaImpl criteriaImpl = (CriteriaImpl) criteria; checkOpen(); String[] implementors = getFactory().getMetamodel().getImplementors(criteriaImpl.getEntityOrClassName()); int size = implementors.length; CriteriaLoader[] loaders = new CriteriaLoader[size]; for (int i = 0; i < size; i++) { loaders[i] = new CriteriaLoader(getOuterJoinLoadable(implementors[i]), getFactory(), criteriaImpl, implementors[i], getLoadQueryInfluencers()); } List results = Collections.EMPTY_LIST; boolean success = false; try { for (int i = 0; i < size; i++) { final List currentResults = loaders[i].list(this); currentResults.addAll(results); results = currentResults; } success = true; } finally { afterOperation(success); } temporaryPersistenceContext.clear(); return results; } private OuterJoinLoadable getOuterJoinLoadable(String entityName) throws MappingException { EntityPersister persister = getFactory().getMetamodel().entityPersister(entityName); if (!(persister instanceof OuterJoinLoadable)) { throw new MappingException("class persister is not OuterJoinLoadable: " + entityName); } return (OuterJoinLoadable) persister; } @Override public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException { checkOpen(); CustomLoader loader = new CustomLoader(customQuery, getFactory()); boolean success = false; List results; try { results = loader.list(this, queryParameters); success = true; } finally { afterOperation(success); } temporaryPersistenceContext.clear(); return results; } @Override public ScrollableResultsImplementor scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException { checkOpen(); CustomLoader loader = new CustomLoader(customQuery, getFactory()); return loader.scroll(queryParameters, this); } @Override public ScrollableResultsImplementor scroll(String query, QueryParameters queryParameters) throws HibernateException { checkOpen(); HQLQueryPlan plan = getQueryPlan(query, false); return plan.performScroll(queryParameters, this); } @Override public void afterScrollOperation() { temporaryPersistenceContext.clear(); } @Override public void flush() { } @Override public LoadQueryInfluencers getLoadQueryInfluencers() { return NO_INFLUENCERS; } @Override public PersistenceContext getPersistenceContextInternal() { //In this case implemented the same as #getPersistenceContext return temporaryPersistenceContext; } @Override public int executeNativeUpdate(NativeSQLQuerySpecification nativeSQLQuerySpecification, QueryParameters queryParameters) throws HibernateException { checkOpen(); queryParameters.validateParameters(); NativeSQLQueryPlan plan = getNativeQueryPlan(nativeSQLQuerySpecification); boolean success = false; int result = 0; try { result = plan.performExecuteUpdate(queryParameters, this); success = true; } finally { afterOperation(success); } temporaryPersistenceContext.clear(); return result; } @Override public void afterTransactionBegin() { } @Override public void beforeTransactionCompletion() { flushBeforeTransactionCompletion(); } @Override public void afterTransactionCompletion(boolean successful, boolean delayed) { if (shouldAutoClose() && !isClosed()) { managedClose(); } } @Override public boolean isTransactionInProgress() { return connectionProvided || super.isTransactionInProgress(); } @Override public void flushBeforeTransactionCompletion() { boolean flush = false; try { flush = (!isClosed() && !isFlushModeNever() && !JtaStatusHelper.isRollback(getJtaPlatform().getCurrentStatus())); } catch (SystemException se) { throw new HibernateException("could not determine transaction status in beforeCompletion()", se); } if (flush) { managedFlush(); } } private JtaPlatform getJtaPlatform() { return getFactory().getServiceRegistry().getService(JtaPlatform.class); } private LockMode getNullSafeLockMode(LockMode lockMode) { return lockMode == null ? LockMode.NONE : lockMode; } }