blob: bc45c407f4a3a7d6a13f83c28f487b17591e0a12 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// 10/15/2010-2.2 Guy Pelletier
// - 322008: Improve usability of additional criteria applied to queries at the session/EM
// 06/30/2011-2.3.1 Guy Pelletier
// - 341940: Add disable/enable allowing native queries
// 09/09/2011-2.3.1 Guy Pelletier
// - 356197: Add new VPD type to MultitenantType
// 09/14/2011-2.3.1 Guy Pelletier
// - 357533: Allow DDL queries to execute even when Multitenant entities are part of the PU
// 05/14/2012-2.4 Guy Pelletier
// - 376603: Provide for table per tenant support for multitenant applications
// 08/11/2012-2.5 Guy Pelletier
// - 393867: Named queries do not work when using EM level Table Per Tenant Multitenancy.
// 11/29/2012-2.5 Guy Pelletier
// - 395406: Fix nightly static weave test errors
// 08/11/2014-2.5 Rick Curtis
// - 440594: Tolerate invalid NamedQuery at EntityManager creation.
// 09/03/2015 - Will Dazey
// - 456067: Added support for defining query timeout units
// 05/26/2016-2.7 Tomas Kraus
// - 494610: Session Properties map should be Map<String, Object>
package org.eclipse.persistence.internal.sessions;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.ReferenceMode;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorQueryManager;
import org.eclipse.persistence.descriptors.TablePerMultitenantPolicy;
import org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy;
import org.eclipse.persistence.descriptors.partitioning.PartitioningPolicy;
import org.eclipse.persistence.exceptions.ConcurrencyException;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.EclipseLinkException;
import org.eclipse.persistence.exceptions.ExceptionHandler;
import org.eclipse.persistence.exceptions.IntegrityChecker;
import org.eclipse.persistence.exceptions.IntegrityException;
import org.eclipse.persistence.exceptions.OptimisticLockException;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.history.AsOfClause;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.databaseaccess.Accessor;
import org.eclipse.persistence.internal.databaseaccess.Platform;
import org.eclipse.persistence.internal.descriptors.ObjectBuilder;
import org.eclipse.persistence.internal.helper.ConcurrencyManager;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.helper.QueryCounter;
import org.eclipse.persistence.internal.helper.linkedlist.ExposedNodeLinkedList;
import org.eclipse.persistence.internal.history.HistoricalSession;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.identitymaps.IdentityMapManager;
import org.eclipse.persistence.internal.indirection.DatabaseValueHolder;
import org.eclipse.persistence.internal.indirection.ProtectedValueHolder;
import org.eclipse.persistence.internal.indirection.ProxyIndirectionPolicy;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.queries.JoinedAttributeManager;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
import org.eclipse.persistence.internal.security.PrivilegedGetConstructorFor;
import org.eclipse.persistence.internal.security.PrivilegedInvokeConstructor;
import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass;
import org.eclipse.persistence.internal.sequencing.Sequencing;
import org.eclipse.persistence.internal.sessions.cdi.DisabledInjectionManager;
import org.eclipse.persistence.internal.sessions.cdi.InjectionManager;
import org.eclipse.persistence.logging.DefaultSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.logging.SessionLogEntry;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.platform.database.DatabasePlatform;
import org.eclipse.persistence.platform.server.ServerPlatform;
import org.eclipse.persistence.queries.AttributeGroup;
import org.eclipse.persistence.queries.Call;
import org.eclipse.persistence.queries.DataModifyQuery;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.DeleteObjectQuery;
import org.eclipse.persistence.queries.DoesExistQuery;
import org.eclipse.persistence.queries.InsertObjectQuery;
import org.eclipse.persistence.queries.JPAQueryBuilder;
import org.eclipse.persistence.queries.JPQLCall;
import org.eclipse.persistence.queries.ObjectBuildingQuery;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.queries.SQLCall;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.queries.WriteObjectQuery;
import org.eclipse.persistence.sessions.CopyGroup;
import org.eclipse.persistence.sessions.DatabaseLogin;
import org.eclipse.persistence.sessions.ExternalTransactionController;
import org.eclipse.persistence.sessions.Login;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.SessionEventManager;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.sessions.coordination.Command;
import org.eclipse.persistence.sessions.coordination.CommandManager;
import org.eclipse.persistence.sessions.coordination.CommandProcessor;
import org.eclipse.persistence.sessions.coordination.MetadataRefreshListener;
import org.eclipse.persistence.sessions.serializers.Serializer;
/**
* Implementation of org.eclipse.persistence.sessions.Session
* The public interface should be used.
* @see org.eclipse.persistence.sessions.Session
*
* <p>
* <b>Purpose</b>: Define the interface and common protocol of an EclipseLink compliant session.
* <p>
* <b>Description</b>: The session is the primary interface into EclipseLink,
* the application should do all of its reading and writing of objects through the session.
* The session also manages transactions and units of work. Normally the session
* is passed and used by the application controller objects. Controller objects normally
* sit behind the GUI and perform the business processes required for the application,
* they should perform all explicit database access and database access should be avoided from
* the domain object model. Do not use a globally accessible session instance, doing so does
* not allow for multiple sessions. Multiple sessions may required when performing things like
* data migration or multiple database access, as well the unit of work feature requires the usage
* of multiple session instances. Although session is abstract, any users of its subclasses
* should only cast the variables to Session to allow usage of any of its subclasses.
* <p>
* <b>Responsibilities</b>:
* <ul>
* <li> Connecting/disconnecting.
* <li> Reading and writing objects.
* <li> Transaction and unit of work support.
* <li> Identity maps and caching.
* </ul>
* @see DatabaseSessionImpl
*/
public abstract class AbstractSession extends CoreAbstractSession<ClassDescriptor, Login, Platform, Project, SessionEventManager> implements org.eclipse.persistence.sessions.Session, CommandProcessor, Serializable, Cloneable {
/** ExceptionHandler handles database exceptions. */
transient protected ExceptionHandler exceptionHandler;
/** IntegrityChecker catch all the descriptor Exceptions. */
transient protected IntegrityChecker integrityChecker;
/** The project stores configuration information, such as the descriptors and login. */
transient protected Project project;
/** Ensure mutual exclusion of the session's transaction state across multiple threads.*/
transient protected ConcurrencyManager transactionMutex;
/** Manages the live object cache.*/
protected IdentityMapAccessor identityMapAccessor;
/** If Transactions were externally started */
protected boolean wasJTSTransactionInternallyStarted;
/** The connection to the data store. */
transient protected Collection<Accessor> accessors;
/** Allow the datasource platform to be cached. */
transient protected Platform platform;
/** Stores predefine reusable queries.*/
transient protected Map<String, List<DatabaseQuery>> queries;
/**
* Stores predefined reusable AttributeGroups.
*/
protected Map<String, AttributeGroup> attributeGroups;
/** Stores predefined not yet parsed JPQL queries.*/
protected boolean jpaQueriesProcessed = false;
/** Resolves referential integrity on commits. */
transient protected CommitManager commitManager;
/** Tool that log performance information. */
transient protected SessionProfiler profiler;
/** Support being owned by a session broker. */
transient protected AbstractSession broker;
/** Used to identify a session when using the session broker. */
protected String name;
/** Keep track of active units of work. */
transient protected int numberOfActiveUnitsOfWork;
/**
* This collection will be used to store those objects that are currently locked
* for the clone process. It should be populated with an EclipseLinkIdentityHashMap
*/
protected Map objectsLockedForClone;
/** Destination for logged messages and SQL. */
transient protected SessionLog sessionLog;
/** When logging the name of the session is typed: class name + system hashcode. */
transient protected String logSessionString;
/** Stores the event listeners for this session. */
transient protected SessionEventManager eventManager;
/** Allow for user defined properties. */
protected Map<String, Object> properties;
/** Delegate that handles synchronizing a UnitOfWork with an external transaction. */
transient protected ExternalTransactionController externalTransactionController;
/** Last descriptor accessed, use to optimize descriptor lookup. */
transient protected ClassDescriptor lastDescriptorAccessed;
/** PERF: cache descriptors from project. */
transient protected Map<Class, ClassDescriptor> descriptors;
/** PERF: cache table per tenant descriptors needing to be initialized per EM */
transient protected List<ClassDescriptor> tablePerTenantDescriptors;
/** PERF: cache table per tenant queries needing to be initialized per EM */
transient protected List<DatabaseQuery> tablePerTenantQueries;
// bug 3078039: move EJBQL alias > descriptor map from Session to Project (MWN)
/** Used to determine If a session is in a Broker or not */
protected boolean isInBroker;
/**
* Used to connect this session to EclipseLink cluster for distributed command
*/
transient protected CommandManager commandManager;
/**
* PERF: Cache the write-lock check to avoid cost of checking in every register/clone.
*/
protected boolean shouldCheckWriteLock;
/**
* Determined whether changes should be propagated to an EclipseLink cluster
*/
protected boolean shouldPropagateChanges;
/** Used to determine If a session is in a profile or not */
protected boolean isInProfile;
/** PERF: Quick check if logging is OFF entirely. */
protected boolean isLoggingOff;
/** PERF: Allow for finalizers to be enabled, currently enables client-session finalize. */
protected boolean isFinalizersEnabled;
/** List of active command threads. */
transient protected ExposedNodeLinkedList activeCommandThreads;
/**
* Indicates whether the session is synchronized.
* In case external transaction controller is used isSynchronized==true means
* the session's jta connection will be freed during external transaction callback.
*/
protected boolean isSynchronized;
/**
* Stores the default reference mode that a UnitOfWork will use when referencing
* managed objects.
* @see org.eclipse.persistence.config.ReferenceMode
*/
protected ReferenceMode defaultReferenceMode;
/**
* Default pessimistic lock timeout value.
*/
protected Integer pessimisticLockTimeoutDefault;
protected TimeUnit pessimisticLockTimeoutUnitDefault;
protected int queryTimeoutDefault;
protected TimeUnit queryTimeoutUnitDefault;
/** Allow a session to enable concurrent processing. */
protected boolean isConcurrent;
/**
* This map will hold onto class to static metamodel class references from JPA.
*/
protected Map<String, String> staticMetamodelClasses;
/** temporarily holds a list of events that must be fired after the current operation completes.
* Initialy created for postClone events.
*/
protected List<DescriptorEvent> deferredEvents;
/** records that the UOW is executing deferred events. Events could cause operations to occur that may attempt to restart the event execution. This must be avoided*/
protected boolean isExecutingEvents;
/** Allow queries to be targeted at specific connection pools. */
protected PartitioningPolicy partitioningPolicy;
/** the MetadataRefreshListener is used with RCM to force a refresh of the metadata used within EntityManagerFactoryWrappers */
protected MetadataRefreshListener metadatalistener;
/** Stores the set of multitenant context properties this session requires **/
protected Set<String> multitenantContextProperties;
/** Store the query builder used to parse JPQL. */
transient protected JPAQueryBuilder queryBuilder;
/** Set the Serializer to use by default for serialization. */
transient protected Serializer serializer;
/** Allow CDI injection of entity listeners **/
transient protected InjectionManager injectionManager;
/**
* Indicates whether ObjectLevelReadQuery should by default use ResultSet Access optimization.
* Optimization specified by the session is ignored if incompatible with other query settings.
*/
protected boolean shouldOptimizeResultSetAccess;
/**
* Indicates whether Session creation should tolerate an invalid NamedQuery. If true, an exception
* will be thrown on .createNamedQuery(..) rather than at init time.
*/
protected boolean tolerateInvalidJPQL = false;
/**
* INTERNAL:
* Create and return a new session.
* This should only be called if the database login information is not know at the time of creation.
* Normally it is better to call the constructor that takes the login information as an argument
* so that the session can initialize itself to the platform information given in the login.
*/
protected AbstractSession() {
this.name = "";
this.queryTimeoutUnitDefault = DescriptorQueryManager.DefaultTimeoutUnit;
this.pessimisticLockTimeoutUnitDefault = DescriptorQueryManager.DefaultTimeoutUnit;
initializeIdentityMapAccessor();
// PERF - move to lazy init (3286091)
}
/**
* INTERNAL:
* Create a blank session, used for proxy session.
*/
protected AbstractSession(int nothing) {
}
/**
* PUBLIC:
* Create and return a new session.
* By giving the login information on creation this allows the session to initialize itself
* to the platform given in the login. This constructor does not return a connected session.
* To connect the session to the database login() must be sent to it. The login(userName, password)
* method may also be used to connect the session, this allows for the user name and password
* to be given at login but for the other database information to be provided when the session is created.
*/
protected AbstractSession(Login login) {
this(new org.eclipse.persistence.sessions.Project(login));
}
/**
* PUBLIC:
* Create and return a new session.
* This constructor does not return a connected session.
* To connect the session to the database login() must be sent to it. The login(userName, password)
* method may also be used to connect the session, this allows for the user name and password
* to be given at login but for the other database information to be provided when the session is created.
*/
protected AbstractSession(org.eclipse.persistence.sessions.Project project) {
this();
this.project = project;
if (project.getDatasourceLogin() == null) {
throw ValidationException.projectLoginIsNull(this);
}
// add the Project's queries as session queries
for (DatabaseQuery query : project.getQueries()) {
addQuery(query.getName(), query);
}
}
/**
* Return the Serializer to use by default for serialization.
*/
@Override
public Serializer getSerializer() {
return serializer;
}
/**
* Set the Serializer to use by default for serialization.
*/
@Override
public void setSerializer(Serializer serializer) {
this.serializer = serializer;
}
/**
* INTERNAL
* Return the query builder used to parser JPQL.
*/
public JPAQueryBuilder getQueryBuilder() {
if (this.queryBuilder == null) {
AbstractSession parent = getParent();
if (parent != null) {
this.queryBuilder = parent.getQueryBuilder();
} else {
this.queryBuilder = buildDefaultQueryBuilder();
}
}
return this.queryBuilder;
}
/**
* INTERNAL
* Set the query builder used to parser JPQL.
*/
public void setQueryBuilder(JPAQueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
}
/**
* INTERNAL
* Build the JPQL builder based on session properties.
*/
protected JPAQueryBuilder buildDefaultQueryBuilder() {
String queryBuilderClassName = (String)getProperty(PersistenceUnitProperties.JPQL_PARSER);
if (queryBuilderClassName == null) {
queryBuilderClassName = "org.eclipse.persistence.internal.jpa.jpql.HermesParser";
//queryBuilderClassName = "org.eclipse.persistence.queries.ANTLRQueryBuilder";
}
String validation = (String)getProperty(PersistenceUnitProperties.JPQL_VALIDATION);
JPAQueryBuilder builder = null;
try {
Class parserClass = null;
// use class.forName() to avoid loading parser classes for JAXB
// Use Class.forName not thread class loader to avoid class loader issues.
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
parserClass = AccessController.doPrivileged(new PrivilegedClassForName(queryBuilderClassName));
builder = (JPAQueryBuilder)AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(parserClass));
} else {
parserClass = PrivilegedAccessHelper.getClassForName(queryBuilderClassName);
builder = (JPAQueryBuilder)PrivilegedAccessHelper.newInstanceFromClass(parserClass);
}
} catch (Exception e) {
throw new IllegalStateException("Could not load the JPQL parser class." /* TODO: Localize string */, e);
}
if (validation != null) {
builder.setValidationLevel(validation);
}
return builder;
}
/**
* INTERNAL:
* PERF: Used for quick turning logging ON/OFF entirely.
* @param loggingOff Logging is turned off when <code>true</code>
* and turned on when <code>false</code>.
*/
public void setLoggingOff(final boolean loggingOff) {
isLoggingOff = loggingOff;
}
/**
* INTERNAL:
* PERF: Used for quick check if logging is OFF entirely.
*/
public boolean isLoggingOff() {
return isLoggingOff;
}
/**
* INTERNAL:
* Called by a sessions queries to obtain individual query ids.
* CR #2698903
*/
public long getNextQueryId() {
return QueryCounter.getCount();
}
/**
* INTERNAL:
* Return a unit of work for this session not registered with the JTS transaction.
*/
public UnitOfWorkImpl acquireNonSynchronizedUnitOfWork() {
return acquireNonSynchronizedUnitOfWork(null);
}
/**
* INTERNAL:
* Return a unit of work for this session not registered with the JTS transaction.
*/
public UnitOfWorkImpl acquireNonSynchronizedUnitOfWork(ReferenceMode referenceMode) {
setNumberOfActiveUnitsOfWork(getNumberOfActiveUnitsOfWork() + 1);
UnitOfWorkImpl unitOfWork = new UnitOfWorkImpl(this, referenceMode);
if (shouldLog(SessionLog.FINER, SessionLog.TRANSACTION)) {
log(SessionLog.FINER, SessionLog.TRANSACTION, "acquire_unit_of_work_with_argument", String.valueOf(System.identityHashCode(unitOfWork)));
}
return unitOfWork;
}
/**
* INTERNAL:
* Constructs a HistoricalSession given a valid AsOfClause.
*/
@Override
public org.eclipse.persistence.sessions.Session acquireHistoricalSession(AsOfClause clause) throws ValidationException {
if ((clause == null) || (clause.getValue() == null)) {
throw ValidationException.cannotAcquireHistoricalSession();
}
if (!getProject().hasGenericHistorySupport() && !hasBroker() && ((getPlatform() == null) || !getPlatform().isOracle())) {
throw ValidationException.historicalSessionOnlySupportedOnOracle();
}
return new HistoricalSession(this, clause);
}
/**
* PUBLIC:
* Return a unit of work for this session.
* The unit of work is an object level transaction that allows
* a group of changes to be applied as a unit.
*
* @see UnitOfWorkImpl
*/
@Override
public UnitOfWorkImpl acquireUnitOfWork() {
UnitOfWorkImpl unitOfWork = acquireNonSynchronizedUnitOfWork(getDefaultReferenceMode());
unitOfWork.registerWithTransactionIfRequired();
return unitOfWork;
}
/**
* PUBLIC:
* Return a repeatable write unit of work for this session.
* A repeatable write unit of work allows multiple writeChanges (flushes).
*
* @see RepeatableWriteUnitOfWork
*/
public RepeatableWriteUnitOfWork acquireRepeatableWriteUnitOfWork(ReferenceMode referenceMode) {
return new RepeatableWriteUnitOfWork(this, referenceMode);
}
/**
* PUBLIC:
* Return a unit of work for this session.
* The unit of work is an object level transaction that allows
* a group of changes to be applied as a unit.
*
* @see UnitOfWorkImpl
* @param referenceMode The reference type the UOW should use internally when
* referencing Working clones. Setting this to WEAK means the UOW will use
* weak references to reference clones that support active object change
* tracking and hard references for deferred change tracked objects.
* Setting to FORCE_WEAK means that all objects will be referenced by weak
* references and if the application no longer references the clone the
* clone may be garbage collected. If the clone
* has uncommitted changes then those changes will be lost.
*/
@Override
public UnitOfWorkImpl acquireUnitOfWork(ReferenceMode referenceMode) {
UnitOfWorkImpl unitOfWork = acquireNonSynchronizedUnitOfWork(referenceMode);
unitOfWork.registerWithTransactionIfRequired();
return unitOfWork;
}
/**
* PUBLIC:
* Add an alias for the descriptor
*/
public void addAlias(String alias, ClassDescriptor descriptor) {
project.addAlias(alias, descriptor);
}
/**
* INTERNAL:
* Return all pre-defined not yet parsed EJBQL queries.
*/
@Override
public void addJPAQuery(DatabaseQuery query) {
getProject().addJPAQuery(query);
}
/**
* INTERNAL:
* Return all pre-defined not yet parsed EJBQL multitenant queries.
*/
public void addJPATablePerTenantQuery(DatabaseQuery query) {
getProject().addJPATablePerTenantQuery(query);
}
/**
* PUBLIC:
* Return a set of multitenant context properties this session
*/
public void addMultitenantContextProperty(String contextProperty) {
getMultitenantContextProperties().add(contextProperty);
}
/**
* INTERNAL:
* Add the query to the session queries.
*/
protected synchronized void addQuery(DatabaseQuery query, boolean nameMustBeUnique) {
Vector queriesByName = (Vector)getQueries().get(query.getName());
if (queriesByName == null) {
// lazily create Vector in Hashtable.
queriesByName = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance();
getQueries().put(query.getName(), queriesByName);
}
if (nameMustBeUnique){ // JPA addNamedQuery
if (queriesByName.size() <= 1){
queriesByName.clear();
}else{
throw new IllegalStateException(ExceptionLocalization.buildMessage("argument_keyed_named_query_with_JPA", new Object[]{query.getName()}));
}
}else{
// Check that we do not already have a query that matched it
for (Iterator enumtr = queriesByName.iterator(); enumtr.hasNext();) {
DatabaseQuery existingQuery = (DatabaseQuery)enumtr.next();
if (Helper.areTypesAssignable(query.getArgumentTypes(), existingQuery.getArgumentTypes())) {
throw ValidationException.existingQueryTypeConflict(query, existingQuery);
}
}
}
queriesByName.add(query);
}
/**
* PUBLIC:
* Add the query to the session queries with the given name.
* This allows for common queries to be pre-defined, reused and executed by name.
*/
@Override
public void addQuery(String name, DatabaseQuery query) {
query.setName(name);
addQuery(query, false);
}
/**
* PUBLIC:
* Add the query to the session queries with the given name.
* This allows for common queries to be pre-defined, reused and executed by name.
*/
public void addQuery(String name, DatabaseQuery query, boolean replace) {
query.setName(name);
addQuery(query, replace);
}
/**
* INTERNAL:
* Add a metamodel class to model class reference.
*/
public void addStaticMetamodelClass(String modelClassName, String metamodelClassName) {
if (staticMetamodelClasses == null) {
staticMetamodelClasses = new HashMap<>();
}
staticMetamodelClasses.put(modelClassName, metamodelClassName);
}
/**
* INTERNAL:
* Add a descriptor that is uses a table per tenant multitenant policy.
*/
protected void addTablePerTenantDescriptor(ClassDescriptor descriptor) {
getTablePerTenantDescriptors().add(descriptor);
}
/**
* INTERNAL:
* Add a query that queries a table per tenant entity
*/
protected void addTablePerTenantQuery(DatabaseQuery query) {
getTablePerTenantQueries().add(query);
}
/**
* INTERNAL:
* Called by beginTransaction() to start a transaction.
* This starts a real database transaction.
* Allows retry if the connection is dead.
*/
protected void basicBeginTransaction() throws DatabaseException {
Collection<Accessor> accessors = getAccessors();
if (accessors == null) {
return;
}
Accessor failedAccessor = null;
try {
for (Accessor accessor : accessors) {
failedAccessor = accessor;
basicBeginTransaction(accessor);
}
} catch (RuntimeException exception) {
// If begin failed, rollback ones already started.
for (Accessor accessor : accessors) {
if (accessor == failedAccessor) {
break;
}
try {
accessor.rollbackTransaction(this);
} catch (RuntimeException ignore) { }
}
throw exception;
}
}
/**
* INTERNAL:
* Called by beginTransaction() to start a transaction.
* This starts a real database transaction.
* Allows retry if the connection is dead.
*/
protected void basicBeginTransaction(Accessor accessor) throws DatabaseException {
try {
accessor.beginTransaction(this);
} catch (DatabaseException databaseException) {
// Retry if the failure was communication based? (i.e. timeout, database down, can no longer ping)
if ((!getDatasourceLogin().shouldUseExternalTransactionController()) && databaseException.isCommunicationFailure()) {
DatabaseException exceptionToThrow = databaseException;
Object[] args = new Object[1];
args[0] = databaseException;
log(SessionLog.INFO, SessionLog.TRANSACTION, "communication_failure_attempting_begintransaction_retry", args, null);
// Attempt to reconnect connection.
exceptionToThrow = retryTransaction(accessor, databaseException, 0, this);
if (exceptionToThrow == null) {
// Retry was a success.
return;
}
handleException(exceptionToThrow);
} else {
handleException(databaseException);
}
} catch (RuntimeException exception) {
handleException(exception);
}
}
/**
* INTERNAL:
* A begin transaction failed.
* Re-connect and retry the begin transaction.
*/
public DatabaseException retryTransaction(Accessor accessor, DatabaseException databaseException, int retryCount, AbstractSession executionSession) {
DatabaseException exceptionToThrow = databaseException;
// Attempt to reconnect connection.
int count = getLogin().getQueryRetryAttemptCount();
while (retryCount < count) {
try {
// if database session then re-establish
// connection
// else the session will just get a new
// connection from the pool
accessor.reestablishConnection(this);
accessor.beginTransaction(this);
return null;
} catch (DatabaseException newException) {
// Need to use last exception, as may not be a communication exception.
exceptionToThrow = newException;
// failed to get connection because of
// database error.
++retryCount;
try {
// Give the failover time to recover.
Thread.sleep(getLogin().getDelayBetweenConnectionAttempts());
Object[] args = new Object[1];
args[0] = exceptionToThrow;
log(SessionLog.INFO, SessionLog.TRANSACTION, "communication_failure_attempting_begintransaction_retry", args, null);
} catch (InterruptedException intEx) {
break;
}
}
}
return exceptionToThrow;
}
/**
* INTERNAL:
* Called in the end of beforeCompletion of external transaction synchronization listener.
* Close the managed sql connection corresponding to the external transaction,
* if applicable releases accessor.
*/
public void releaseJTSConnection() {
}
/**
* INTERNAL:
* Called by commitTransaction() to commit a transaction.
* This commits the active transaction.
*/
protected void basicCommitTransaction() throws DatabaseException {
Collection<Accessor> accessors = getAccessors();
if (accessors == null) {
return;
}
try {
for (Accessor accessor : accessors) {
accessor.commitTransaction(this);
}
} catch (RuntimeException exception) {
// Leave transaction uncommitted as rollback should be called.
handleException(exception);
}
}
/**
* INTERNAL:
* Called by rollbackTransaction() to rollback a transaction.
* This rolls back the active transaction.
*/
protected void basicRollbackTransaction() throws DatabaseException {
Collection<Accessor> accessors = getAccessors();
if (accessors == null) {
return;
}
RuntimeException exception = null;
for (Accessor accessor : accessors) {
try {
accessor.rollbackTransaction(this);
} catch (RuntimeException failure) {
exception = failure;
}
}
if (exception != null) {
handleException(exception);
}
}
/**
* INTERNAL:
* Attempts to begin an external transaction.
* Returns true only in one case -
* external transaction has been internally started during this method call:
* wasJTSTransactionInternallyStarted()==false in the beginning of this method and
* wasJTSTransactionInternallyStarted()==true in the end of this method.
*/
public boolean beginExternalTransaction() {
boolean externalTransactionHasBegun = false;
if (hasExternalTransactionController() && !wasJTSTransactionInternallyStarted()) {
try {
getExternalTransactionController().beginTransaction(this);
} catch (RuntimeException exception) {
handleException(exception);
}
if (wasJTSTransactionInternallyStarted()) {
externalTransactionHasBegun = true;
log(SessionLog.FINER, SessionLog.TRANSACTION, "external_transaction_has_begun_internally");
}
}
return externalTransactionHasBegun;
}
/**
* PUBLIC:
* Begin a transaction on the database.
* This allows a group of database modification to be committed or rolled back as a unit.
* All writes/deletes will be sent to the database be will not be visible to other users until commit.
* Although databases do not allow nested transaction,
* EclipseLink supports nesting through only committing to the database on the outer commit.
*
* @exception DatabaseException if the database connection is lost or the begin is rejected.
* @exception ConcurrencyException if this session's transaction is acquired by another thread and a timeout occurs.
*
* @see #isInTransaction()
*/
public void beginTransaction() throws DatabaseException, ConcurrencyException {
ConcurrencyManager mutex = getTransactionMutex();
// If there is no db transaction in progress
// beginExternalTransaction() starts an external transaction -
// provided externalTransactionController is used, and there is
// no active external transaction - so we have to start one internally.
if (!mutex.isAcquired()) {
beginExternalTransaction();
}
// For unit of work and client session multi threading is allowed as they are a context,
// this is required for JTS/RMI/CORBA/EJB stuff where the server thread can be different across calls.
if (isClientSession()) {
mutex.setActiveThread(Thread.currentThread());
}
// Ensure mutual exclusion and call subclass specific begin.
mutex.acquire();
if (!mutex.isNested()) {
if (this.eventManager != null) {
this.eventManager.preBeginTransaction();
}
basicBeginTransaction();
if (this.eventManager != null) {
this.eventManager.postBeginTransaction();
}
}
}
/**
* Check to see if the descriptor of a superclass can be used to describe this class
*
* @return ClassDescriptor
*/
protected ClassDescriptor checkHierarchyForDescriptor(Class theClass){
return getDescriptor(theClass.getSuperclass());
}
/**
* allow the entity listener injection manager to clean itself up.
*/
public void cleanUpInjectionManager(){
if (injectionManager != null){
injectionManager.cleanUp(this);
}
}
/**
* PUBLIC:
* clear the integrityChecker. IntegrityChecker holds all the ClassDescriptor Exceptions.
*/
@Override
public void clearIntegrityChecker() {
setIntegrityChecker(null);
}
/**
* INTERNAL:
* Clear the the lastDescriptorAccessed cache.
*/
public void clearLastDescriptorAccessed() {
this.lastDescriptorAccessed = null;
}
/**
* INTERNAL:
* Clear the the descriptors cache.
*/
public void clearDescriptors() {
this.descriptors = null;
}
/**
* PUBLIC:
* Clear the profiler, this will end the current profile operation.
*/
@Override
public void clearProfile() {
setProfiler(null);
}
/**
* INTERNAL:
* Clones the descriptor
*/
@Override
public Object clone() {
// An alternative to this process should be found
try {
return super.clone();
} catch (Exception exception) {
throw new AssertionError(exception);
}
}
/**
* INTERNAL:
* Attempts to commit the running internally started external transaction.
* Returns true only in one case -
* external transaction has been internally committed during this method call:
* wasJTSTransactionInternallyStarted()==true in the beginning of this method and
* wasJTSTransactionInternallyStarted()==false in the end of this method.
*/
public boolean commitExternalTransaction() {
boolean externalTransactionHasCommitted = false;
if (hasExternalTransactionController() && wasJTSTransactionInternallyStarted()) {
try {
getExternalTransactionController().commitTransaction(this);
} catch (RuntimeException exception) {
handleException(exception);
}
if (!wasJTSTransactionInternallyStarted()) {
externalTransactionHasCommitted = true;
log(SessionLog.FINER, SessionLog.TRANSACTION, "external_transaction_has_committed_internally");
}
}
return externalTransactionHasCommitted;
}
/**
* PUBLIC:
* Commit the active database transaction.
* This allows a group of database modification to be committed or rolled back as a unit.
* All writes/deletes will be sent to the database be will not be visible to other users until commit.
* Although databases do not allow nested transaction,
* EclipseLink supports nesting through only committing to the database on the outer commit.
*
* @exception DatabaseException most databases validate changes as they are done,
* normally errors do not occur on commit unless the disk fails or the connection is lost.
* @exception ConcurrencyException if this session is not within a transaction.
*/
public void commitTransaction() throws DatabaseException, ConcurrencyException {
ConcurrencyManager mutex = getTransactionMutex();
// Release mutex and call subclass specific commit.
if (!mutex.isNested()) {
if (this.eventManager != null) {
this.eventManager.preCommitTransaction();
}
basicCommitTransaction();
if (this.eventManager != null) {
this.eventManager.postCommitTransaction();
}
}
// This MUST not be in a try catch or finally as if the commit failed the transaction is still open.
mutex.release();
// If there is no db transaction in progress
// if there is an active external transaction
// which was started internally - it should be committed internally, too.
if (!mutex.isAcquired()) {
commitExternalTransaction();
}
}
/**
* INTERNAL:
* Return if the two object match completely.
* This checks the objects attributes and their private parts.
*/
public boolean compareObjects(Object firstObject, Object secondObject) {
if ((firstObject == null) && (secondObject == null)) {
return true;
}
if ((firstObject == null) || (secondObject == null)) {
return false;
}
if (!(firstObject.getClass().equals(secondObject.getClass()))) {
return false;
}
ObjectBuilder builder = getDescriptor(firstObject.getClass()).getObjectBuilder();
return builder.compareObjects(builder.unwrapObject(firstObject, this), builder.unwrapObject(secondObject, this), this);
}
/**
* TESTING:
* Return true if the object do not match.
* This checks the objects attributes and their private parts.
*/
public boolean compareObjectsDontMatch(Object firstObject, Object secondObject) {
return !this.compareObjects(firstObject, secondObject);
}
/**
* PUBLIC:
* Return true if the pre-defined query is defined on the session.
*/
@Override
public boolean containsQuery(String queryName) {
return getQueries().containsKey(queryName);
}
/**
* PUBLIC:
* Return a complete copy of the object or of collection of objects.
* In case of collection all members should be either entities of the same type
* or have a common inheritance hierarchy mapped root class.
* This can be used to obtain a scratch copy of an object,
* or for templatizing an existing object into another new object.
* The object and all of its privately owned parts will be copied.
*
* @see #copy(Object, AttributeGroup)
*/
@Override
public Object copy(Object originalObjectOrObjects) {
return copy(originalObjectOrObjects, new CopyGroup());
}
/**
* PUBLIC:
* Return a complete copy of the object or of collection of objects.
* In case of collection all members should be either entities of the same type
* or have a common inheritance hierarchy mapped root class.
* This can be used to obtain a scratch copy of an object,
* or for templatizing an existing object into another new object.
* If there are no attributes in the group
* then the object and all of its privately owned parts will be copied.
* Otherwise only the attributes included into the group will be copied.
*/
@Override
public Object copy(Object originalObjectOrObjects, AttributeGroup group) {
if (originalObjectOrObjects == null) {
return null;
}
CopyGroup copyGroup = group.toCopyGroup();
copyGroup.setSession(this);
if(originalObjectOrObjects instanceof Collection) {
// it's a collection - make sure all elements use the same instance of CopyGroup.
Collection originalCollection = (Collection)originalObjectOrObjects;
Collection copies;
if(originalCollection instanceof List) {
copies = new ArrayList();
} else {
copies = new HashSet();
}
Iterator it = originalCollection.iterator();
while(it.hasNext()) {
copies.add(copyInternal(it.next(), copyGroup));
}
return copies;
}
// it's not a collection
return copyInternal(originalObjectOrObjects, copyGroup);
}
/**
* INTERNAL:
*/
public Object copyInternal(Object originalObject, CopyGroup copyGroup) {
if (originalObject == null) {
return null;
}
ClassDescriptor descriptor = getDescriptor(originalObject);
if (descriptor == null) {
return originalObject;
}
return descriptor.getObjectBuilder().copyObject(originalObject, copyGroup);
}
/**
* INTERNAL:
* Copy the read only classes from the unit of work
*
* Added Nov 8, 2000 JED for Patch 2.5.1.8
* Ref: Prs 24502
*/
public Vector copyReadOnlyClasses() {
return getDefaultReadOnlyClasses();
}
public DatabaseValueHolder createCloneQueryValueHolder(ValueHolderInterface attributeValue, Object clone, AbstractRecord row, ForeignReferenceMapping mapping) {
return new ProtectedValueHolder(attributeValue, mapping, this);
}
public DatabaseValueHolder createCloneTransformationValueHolder(ValueHolderInterface attributeValue, Object original, Object clone, AbstractTransformationMapping mapping) {
return new ProtectedValueHolder(attributeValue, mapping, this);
}
public <T> InjectionManager<T> createInjectionManager(Object beanManager){
try{
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
Class elim = AccessController.doPrivileged(new PrivilegedClassForName(InjectionManager.DEFAULT_CDI_INJECTION_MANAGER, true, getLoader()));
Constructor constructor = AccessController.doPrivileged(new PrivilegedGetConstructorFor(elim, new Class[] {String.class}, false));
return (InjectionManager<T>) AccessController.doPrivileged(new PrivilegedInvokeConstructor(constructor, new Object[] {beanManager}));
} else {
Class elim = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(InjectionManager.DEFAULT_CDI_INJECTION_MANAGER, true, getLoader());
Constructor constructor = PrivilegedAccessHelper.getConstructorFor(elim, new Class[] {Object.class}, false);
return (InjectionManager<T>) PrivilegedAccessHelper.invokeConstructor(constructor, new Object[] {beanManager});
}
} catch (Exception e){
logThrowable(SessionLog.FINEST, SessionLog.JPA, e);
}
return new DisabledInjectionManager<>();
}
/**
* INTERNAL:
* This method is similar to getAndCloneCacheKeyFromParent. It purpose is to get protected cache data from the shared cache and
* build/return a protected instance.
*/
public Object createProtectedInstanceFromCachedData(Object cached, Integer refreshCascade, ClassDescriptor descriptor){
CacheKey localCacheKey = getIdentityMapAccessorInstance().getCacheKeyForObject(cached);
if (localCacheKey != null && localCacheKey.getObject() != null){
return localCacheKey.getObject();
}
boolean identityMapLocked = this.shouldCheckWriteLock && getParent().getIdentityMapAccessorInstance().acquireWriteLock();
boolean rootOfCloneRecursion = false;
CacheKey cacheKey = getParent().getIdentityMapAccessorInstance().getCacheKeyForObject(cached);
try{
Object key = null;
Object lockValue = null;
long readTime = 0;
if (cacheKey != null){
if (identityMapLocked) {
checkAndRefreshInvalidObject(cached, cacheKey, descriptor);
} else {
// Check if we have locked all required objects already.
if (this.objectsLockedForClone == null) {
// PERF: If a simple object just acquire a simple read-lock.
if (descriptor.shouldAcquireCascadedLocks()) {
this.objectsLockedForClone = getParent().getIdentityMapAccessorInstance().getWriteLockManager().acquireLocksForClone(cached, descriptor, cacheKey, this);
} else {
checkAndRefreshInvalidObject(cached, cacheKey, descriptor);
cacheKey.acquireReadLock();
}
rootOfCloneRecursion = true;
}
}
key = cacheKey.getKey();
lockValue = cacheKey.getWriteLockValue();
readTime = cacheKey.getReadTime();
}
if (descriptor.hasInheritance()){
descriptor = this.getClassDescriptor(cached.getClass());
}
ObjectBuilder builder = descriptor.getObjectBuilder();
Object workingClone = builder.instantiateWorkingCopyClone(cached, this);
// PERF: Cache the primary key if implements PersistenceEntity.
builder.populateAttributesForClone(cached, cacheKey, workingClone, refreshCascade, this);
getIdentityMapAccessorInstance().putInIdentityMap(workingClone, key, lockValue, readTime, descriptor);
return workingClone;
}finally{
if (rootOfCloneRecursion){
if (this.objectsLockedForClone == null && cacheKey != null) {
cacheKey.releaseReadLock();
} else {
for (Iterator iterator = this.objectsLockedForClone.values().iterator(); iterator.hasNext();) {
((CacheKey)iterator.next()).releaseReadLock();
}
this.objectsLockedForClone = null;
}
executeDeferredEvents();
}
}
}
/**
* INTERNAL:
* Check if the object is invalid and refresh it.
* This is used to ensure that no invalid objects are registered.
*/
public void checkAndRefreshInvalidObject(Object object, CacheKey cacheKey, ClassDescriptor descriptor) {
if (isConsideredInvalid(object, cacheKey, descriptor)) {
ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(object.getClass());
query.setSelectionId(cacheKey.getKey());
query.refreshIdentityMapResult();
query.setIsExecutionClone(true);
this.executeQuery(query);
}
}
/**
* INTERNAL:
* Check if the object is invalid and *should* be refreshed.
* This is used to ensure that no invalid objects are cloned.
*/
public boolean isConsideredInvalid(Object object, CacheKey cacheKey, ClassDescriptor descriptor) {
if (cacheKey.getObject() != null) {
CacheInvalidationPolicy cachePolicy = descriptor.getCacheInvalidationPolicy();
// BUG#6671556 refresh invalid objects when accessed in the unit of work.
return (cachePolicy.shouldRefreshInvalidObjectsOnClone() && cachePolicy.isInvalidated(cacheKey));
}
return false;
}
/**
* INTERNAL:
* Add an event to the deferred list. Events will be fired after the operation completes
*/
public void deferEvent(DescriptorEvent event){
if (this.deferredEvents == null){
this.deferredEvents = new ArrayList<>();
}
this.deferredEvents.add(event);
}
/**
* PUBLIC:
* delete all of the objects and all of their privately owned parts in the database.
* The allows for a group of objects to be deleted as a unit.
* The objects will be deleted through a single transactions.
*
* @exception DatabaseException if an error occurs on the database,
* these include constraint violations, security violations and general database errors.
* @exception OptimisticLockException if the object's descriptor is using optimistic locking and
* the object has been updated or deleted by another user since it was last read.
*/
public void deleteAllObjects(Collection domainObjects) throws DatabaseException, OptimisticLockException {
for (Iterator objectsEnum = domainObjects.iterator(); objectsEnum.hasNext();) {
deleteObject(objectsEnum.next());
}
}
/**
* PUBLIC:
* Delete the object and all of its privately owned parts from the database.
* The delete operation can be customized through using a delete query.
*
* @exception DatabaseException if an error occurs on the database,
* these include constraint violations, security violations and general database errors.
* An database error is not raised if the object is already deleted or no rows are effected.
* @exception OptimisticLockException if the object's descriptor is using optimistic locking and
* the object has been updated or deleted by another user since it was last read.
*
* @see DeleteObjectQuery
*/
public Object deleteObject(Object domainObject) throws DatabaseException, OptimisticLockException {
DeleteObjectQuery query = new DeleteObjectQuery();
query.setObject(domainObject);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* PUBLIC:
* Return if the object exists on the database or not.
* This always checks existence on the database.
*/
@Override
public boolean doesObjectExist(Object object) throws DatabaseException {
DoesExistQuery query = new DoesExistQuery();
query.setObject(object);
query.checkDatabaseForDoesExist();
query.setIsExecutionClone(true);
return (Boolean) executeQuery(query);
}
/**
* PUBLIC:
* Turn off logging
*/
@Override
public void dontLogMessages() {
setLogLevel(SessionLog.OFF);
}
/**
* INTERNAL:
* End the operation timing.
*/
@Override
public void endOperationProfile(String operationName) {
if (this.isInProfile) {
getProfiler().endOperationProfile(operationName);
}
}
/**
* INTERNAL:
* End the operation timing.
*/
public void endOperationProfile(String operationName, DatabaseQuery query, int weight) {
if (this.isInProfile) {
getProfiler().endOperationProfile(operationName, query, weight);
}
}
/**
* INTERNAL:
* Updates the value of SessionProfiler state
*/
@Override
public void updateProfile(String operationName, Object value) {
if (this.isInProfile) {
getProfiler().update(operationName, value);
}
}
/**
* INTERNAL:
* Set the table per tenant. This should be called per client session after
* the start of a transaction. From JPA this method is called on the entity
* manager by setting the multitenant table per tenant property.
*/
public void updateTablePerTenantDescriptors(String property, Object value) {
// When all the table per tenant descriptors are set, we should initialize them.
boolean shouldInitializeDescriptors = hasTablePerTenantDescriptors();
for (ClassDescriptor descriptor : getTablePerTenantDescriptors()) {
TablePerMultitenantPolicy policy = (TablePerMultitenantPolicy) descriptor.getMultitenantPolicy();
if ((! policy.hasContextTenant()) && policy.usesContextProperty(property)) {
policy.setContextTenant((String) value);
}
shouldInitializeDescriptors = shouldInitializeDescriptors && policy.hasContextTenant();
}
if (shouldInitializeDescriptors) {
// Now that the correct tables are set on all table per tenant
// descriptors, we can go through the initialization phases safely.
try {
// First initialize basic properties (things that do not depend on anything else)
for (ClassDescriptor descriptor : tablePerTenantDescriptors) {
descriptor.preInitialize(this);
}
// Second initialize basic mappings
for (ClassDescriptor descriptor : tablePerTenantDescriptors) {
descriptor.initialize(this);
}
// Third initialize child dependencies
for (ClassDescriptor descriptor : tablePerTenantDescriptors) {
descriptor.postInitialize(this);
}
if (getIntegrityChecker().hasErrors()) {
handleSevere(new IntegrityException(getIntegrityChecker()));
}
} finally {
clearIntegrityChecker();
}
getCommitManager().initializeCommitOrder();
// If we have table per tenant queries, initialize and add them now
// once all the descriptors have been initialized.
if (hasTablePerTenantQueries()) {
for (DatabaseQuery query : getTablePerTenantQueries()) {
processJPAQuery(query);
}
}
}
}
/**
* INTERNAL:
* Updates the count of SessionProfiler event
*/
@Override
public void incrementProfile(String operationName) {
if (this.isInProfile) {
getProfiler().occurred(operationName, this);
}
}
/**
* INTERNAL:
* Updates the count of SessionProfiler event
*/
public void incrementProfile(String operationName, DatabaseQuery query) {
if (this.isInProfile) {
getProfiler().occurred(operationName, query, this);
}
}
/**
* INTERNAL:
* Causes any deferred events to be fired. Called after operation completes
*/
public void executeDeferredEvents(){
if (!this.isExecutingEvents && this.deferredEvents != null) {
this.isExecutingEvents = true;
try {
for (int i = 0; i < this.deferredEvents.size(); ++i) {
// the size is checked every time here because the list may grow
DescriptorEvent event = this.deferredEvents.get(i);
event.getDescriptor().getEventManager().executeEvent(event);
}
this.deferredEvents.clear();
} finally {
this.isExecutingEvents = false;
}
}
}
/**
* INTERNAL:
* Overridden by subclasses that do more than just execute the call.
* Executes the call directly on this session and does not check which
* session it should have executed on.
*/
public Object executeCall(Call call, AbstractRecord translationRow, DatabaseQuery query) throws DatabaseException {
if (query.getAccessors() == null) {
query.setAccessors(getAccessors());
}
try {
return basicExecuteCall(call, translationRow, query);
} finally {
if (call.isFinished()) {
query.setAccessors(null);
}
}
}
/**
* INTERNAL:
* Release (if required) connection after call.
*/
public void releaseConnectionAfterCall(DatabaseQuery query) {
}
/**
* PUBLIC:
* Execute the call on the database.
* The row count is returned.
* The call can be a stored procedure call, SQL call or other type of call.
* <p>Example:
* <p>session.executeNonSelectingCall(new SQLCall("Delete from Employee");
*
* @see #executeSelectingCall(Call)
*/
@Override
public int executeNonSelectingCall(Call call) throws DatabaseException {
DataModifyQuery query = new DataModifyQuery();
query.setIsExecutionClone(true);
query.setCall(call);
Integer value = (Integer)executeQuery(query);
if (value == null) {
return 0;
} else {
return value;
}
}
/**
* PUBLIC:
* Execute the sql on the database.
* <p>Example:
* <p>session.executeNonSelectingSQL("Delete from Employee");
* @see #executeNonSelectingCall(Call)
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
*/
@Override
public void executeNonSelectingSQL(String sqlString) throws DatabaseException {
executeNonSelectingCall(new SQLCall(sqlString));
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName) throws DatabaseException {
DatabaseQuery query = getQuery(queryName);
if (query == null) {
throw QueryException.queryNotDefined(queryName);
}
return executeQuery(query);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass) throws DatabaseException {
ClassDescriptor descriptor = getDescriptor(domainClass);
if (descriptor == null) {
throw QueryException.descriptorIsMissingForNamedQuery(domainClass, queryName);
}
DatabaseQuery query = descriptor.getQueryManager().getQuery(queryName);
if (query == null) {
throw QueryException.queryNotDefined(queryName, domainClass);
}
return executeQuery(query);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass, Object arg1) throws DatabaseException {
Vector argumentValues = new Vector();
argumentValues.addElement(arg1);
return executeQuery(queryName, domainClass, argumentValues);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass, Object arg1, Object arg2) throws DatabaseException {
Vector argumentValues = new Vector();
argumentValues.addElement(arg1);
argumentValues.addElement(arg2);
return executeQuery(queryName, domainClass, argumentValues);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass, Object arg1, Object arg2, Object arg3) throws DatabaseException {
Vector argumentValues = new Vector();
argumentValues.addElement(arg1);
argumentValues.addElement(arg2);
argumentValues.addElement(arg3);
return executeQuery(queryName, domainClass, argumentValues);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass, List argumentValues) throws DatabaseException {
if (argumentValues instanceof Vector) {
return executeQuery(queryName, domainClass, (Vector)argumentValues);
} else {
return executeQuery(queryName, domainClass, new Vector(argumentValues));
}
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
public Object executeQuery(String queryName, Class domainClass, Vector argumentValues) throws DatabaseException {
ClassDescriptor descriptor = getDescriptor(domainClass);
if (descriptor == null) {
throw QueryException.descriptorIsMissingForNamedQuery(domainClass, queryName);
}
DatabaseQuery query = descriptor.getQueryManager().getQuery(queryName, argumentValues);
if (query == null) {
throw QueryException.queryNotDefined(queryName, domainClass);
}
return executeQuery(query, argumentValues);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Object arg1) throws DatabaseException {
Vector argumentValues = new Vector();
argumentValues.addElement(arg1);
return executeQuery(queryName, argumentValues);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Object arg1, Object arg2) throws DatabaseException {
Vector argumentValues = new Vector();
argumentValues.addElement(arg1);
argumentValues.addElement(arg2);
return executeQuery(queryName, argumentValues);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Object arg1, Object arg2, Object arg3) throws DatabaseException {
Vector argumentValues = new Vector();
argumentValues.addElement(arg1);
argumentValues.addElement(arg2);
argumentValues.addElement(arg3);
return executeQuery(queryName, argumentValues);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, List argumentValues) throws DatabaseException {
if (argumentValues instanceof Vector) {
return executeQuery(queryName, (Vector)argumentValues);
} else {
return executeQuery(queryName, new Vector(argumentValues));
}
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
*
* @see #addQuery(String, DatabaseQuery)
*/
public Object executeQuery(String queryName, Vector argumentValues) throws DatabaseException {
DatabaseQuery query = getQuery(queryName, argumentValues);
if (query == null) {
throw QueryException.queryNotDefined(queryName);
}
return executeQuery(query, argumentValues);
}
/**
* PUBLIC:
* Execute the database query.
* A query is a database operation such as reading or writing.
* The query allows for the operation to be customized for such things as,
* performance, depth, caching, etc.
*
* @see DatabaseQuery
*/
@Override
public Object executeQuery(DatabaseQuery query) throws DatabaseException {
return executeQuery(query, EmptyRecord.getEmptyRecord());
}
/**
* PUBLIC:
* Return the results from executing the database query.
* The query arguments are passed in as a List of argument values in the same order as the query arguments.
*/
@Override
public Object executeQuery(DatabaseQuery query, List argumentValues) throws DatabaseException {
if (query == null) {
throw QueryException.queryNotDefined();
}
AbstractRecord row = query.rowFromArguments(argumentValues, this);
return executeQuery(query, row);
}
/**
* INTERNAL:
* Return the results from executing the database query.
* the arguments should be a database row with raw data values.
*/
public Object executeQuery(DatabaseQuery query, AbstractRecord row) throws DatabaseException {
if (hasBroker()) {
if (!((query.isDataModifyQuery() || query.isDataReadQuery()) && (query.getSessionName() == null))) {
return getBroker().executeQuery(query, row);
}
}
if (query == null) {
throw QueryException.queryNotDefined();
}
// Check for disabled native queries.
if (query.isUserDefinedSQLCall() && query.isSQLCallQuery() && ! query.isJPQLCallQuery()) {
if (! query.shouldAllowNativeSQLQuery(getProject().allowNativeSQLQueries())) {
// If the session/project says no to SQL queries and the database
// query doesn't ask to bypass this decision then throw an exception.
throw QueryException.nativeSQLQueriesAreDisabled(query);
}
}
//CR#2272
log(SessionLog.FINEST, SessionLog.QUERY, "execute_query", query);
//Make a call to the internal method with a retry count of 0. This will
//initiate a retry call stack if required and supported. The separation between the
//calling stack and the target method is made because the target method may call itself
//recursively.
return this.executeQuery(query, row, 0);
}
/**
* INTERNAL:
* Return the results from executing the database query.
* the arguments should be a database row with raw data values.
*/
public Object executeQuery(DatabaseQuery query, AbstractRecord row, int retryCount) throws DatabaseException {
try {
if (this.eventManager != null) {
this.eventManager.preExecuteQuery(query);
}
Object result;
if (isInProfile()) {
result = getProfiler().profileExecutionOfQuery(query, row, this);
} else {
result = internalExecuteQuery(query, row);
}
if (this.eventManager != null) {
this.eventManager.postExecuteQuery(query, result);
}
return result;
} catch (RuntimeException exception) {
if (exception instanceof QueryException) {
QueryException queryException = (QueryException)exception;
if (queryException.getQuery() == null) {
queryException.setQuery(query);
}
if (queryException.getQueryArgumentsRecord() == null) {
queryException.setQueryArguments(row);
}
if (queryException.getSession() == null) {
queryException.setSession(this);
}
} else if (exception instanceof DatabaseException) {
DatabaseException databaseException = (DatabaseException)exception;
if (databaseException.getQuery() == null) {
databaseException.setQuery(query);
}
if (databaseException.getQueryArgumentsRecord() == null) {
databaseException.setQueryArguments(row);
}
if (databaseException.getSession() == null) {
databaseException.setSession(this);
}
//if this query is a read query outside of a transaction then we may be able to retry the query
if (!isInTransaction() && query.isReadQuery() && getDatasourceLogin() instanceof DatabaseLogin) {
final int count = getLogin().getQueryRetryAttemptCount();
//was the failure communication based? (ie timeout)
if (databaseException.isCommunicationFailure() && retryCount < count) {
Object[] args = new Object[1];
args[0] = databaseException;
log(SessionLog.INFO, SessionLog.QUERY, "communication_failure_attempting_query_retry", args, null);
Object result = retryQuery(query, row, databaseException, retryCount, this);
if (result instanceof DatabaseException) {
exception = (DatabaseException)result;
} else {
return result;
}
}
}
}
return handleException(exception);
}
}
/**
* INTERNAL:
* A query execution failed due to an invalid query.
* Re-connect and retry the query.
*/
public Object retryQuery(DatabaseQuery query, AbstractRecord row, DatabaseException databaseException, int retryCount, AbstractSession executionSession) {
DatabaseException exception = databaseException;
//retry
if (retryCount <= getLogin().getQueryRetryAttemptCount()) {
try {
// attempt to reconnect for a certain number of times.
// servers may take some time to recover.
++retryCount;
try {
//passing the retry count will prevent a runaway retry where
// we can acquire connections but are unable to execute any queries
if (retryCount > 1) {
// We are retrying more than once lets wait to give connection time to restart.
//Give the failover time to recover.
Thread.sleep(getLogin().getDelayBetweenConnectionAttempts());
}
return executionSession.executeQuery(query, row, retryCount);
} catch (DatabaseException newException){
//replace original exception with last exception thrown
//this exception could be a data based exception as opposed
//to a connection exception that needs to go back to the customer.
exception = newException;
}
} catch (InterruptedException ex) {
//Ignore interrupted exception.
}
}
return exception;
}
/**
* PUBLIC:
* Execute the call on the database and return the result.
* The call must return a value, if no value is return executeNonSelectCall must be used.
* The call can be a stored procedure call, SQL call or other type of call.
* A vector of database rows is returned, database row implements Java 2 Map which should be used to access the data.
* <p>Example:
* <p>session.executeSelectingCall(new SQLCall("Select * from Employee");
*
* @see #executeNonSelectingCall(Call)
*/
@Override
public Vector executeSelectingCall(Call call) throws DatabaseException {
DataReadQuery query = new DataReadQuery();
query.setCall(call);
query.setIsExecutionClone(true);
return (Vector)executeQuery(query);
}
/**
* PUBLIC:
* Execute the sql on the database and return the result.
* It must return a value, if no value is return executeNonSelectingSQL must be used.
* A vector of database rows is returned, database row implements Java 2 Map which should be used to access the data.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
* <p>Example:
* <p>session.executeSelectingCall("Select * from Employee");
*
* @see #executeSelectingCall(Call)
*/
@Override
public Vector executeSQL(String sqlString) throws DatabaseException {
return executeSelectingCall(new SQLCall(sqlString));
}
/**
* INTERNAL:
* This should normally not be used, most sessions do not have a single accessor.
* ServerSession has a set of connection pools.
* ClientSession only has an accessor during a transaction.
* SessionBroker has multiple accessors.
* getAccessors() should be used to support partitioning.
* To maintain backward compatibility, and to support certain cases that required a default accessor,
* this returns the first accessor.
*/
public Accessor getAccessor() {
Collection<Accessor> accessors = getAccessors();
if ((accessors == null) || accessors.isEmpty()) {
return null;
}
if (accessors instanceof List) {
return ((List<Accessor>)accessors).get(0);
}
return accessors.iterator().next();
}
/**
* INTERNAL:
* This should normally not be used, most sessions do not have specific accessors.
* ServerSession has a set of connection pools.
* ClientSession only has an accessor during a transaction.
* SessionBroker has multiple accessors.
* getAccessors() is used to support partitioning.
* If the accessor is null, this lazy initializes one for backwardcompatibility with DatabaseSession.
*/
public Collection<Accessor> getAccessors() {
if ((this.accessors == null) && (this.project != null) && (this.project.getDatasourceLogin() != null)) {
synchronized (this) {
if ((this.accessors == null) && (this.project != null) && (this.project.getDatasourceLogin() != null)) {
// PERF: lazy init, not always required.
List<Accessor> newAccessors = new ArrayList(1);
newAccessors.add(this.project.getDatasourceLogin().buildAccessor());
this.accessors = newAccessors;
}
}
}
return this.accessors;
}
/**
* INTERNAL:
* Return the connections to use for the query execution.
*/
public Collection<Accessor> getAccessors(Call call, AbstractRecord translationRow, DatabaseQuery query) {
// Check for partitioning.
Collection<Accessor> accessors = null;
if (query.getPartitioningPolicy() != null) {
accessors = query.getPartitioningPolicy().getConnectionsForQuery(this, query, translationRow);
if (accessors != null) {
return accessors;
}
}
ClassDescriptor descriptor = query.getDescriptor();
if ((descriptor != null) && (descriptor.getPartitioningPolicy() != null)) {
accessors = descriptor.getPartitioningPolicy().getConnectionsForQuery(this, query, translationRow);
if (accessors != null) {
return accessors;
}
}
if (this.partitioningPolicy != null) {
accessors = this.partitioningPolicy.getConnectionsForQuery(this, query, translationRow);
if (accessors != null) {
return accessors;
}
}
return accessors;
}
/**
* INTERNAL:
* Execute the call on each accessors and merge the results.
*/
public Object basicExecuteCall(Call call, AbstractRecord translationRow, DatabaseQuery query) throws DatabaseException {
Object result = null;
if (this.eventManager != null) {
this.eventManager.preExecuteCall(call);
}
try {
if (query.getAccessors().size() == 1) {
result = query.getAccessor().executeCall(call, translationRow, this);
} else {
RuntimeException exception = null;
// Replication or partitioning may require execution on multiple connections.
for (Accessor accessor : query.getAccessors()) {
Object object = null;
try {
object = accessor.executeCall(call, translationRow, this);
} catch (RuntimeException failed) {
// Catch any exceptions to allow execution on each connections.
// This is used to have DDL run on every database even if one db fails because table already exists.
if (exception == null) {
exception = failed;
}
}
if (call.isOneRowReturned()) {
// If one row is desired, then break on first hit.
if (object != null) {
result = object;
break;
}
} else if (call.isNothingReturned()) {
// If no return ensure row count is consistent, 0 if any 0, otherwise first number.
if (result == null) {
result = object;
} else {
if (object instanceof Integer) {
if ((Integer) result != 0) {
if ((Integer) object != 0) {
result = object;
}
}
}
}
} else {
// Either a set of rows (union), or cursor (return).
if (result == null) {
result = object;
} else {
if (object instanceof List) {
((List)result).addAll((List)object);
} else {
break; // Not sure what to do, so break (if a cursor, don't only want to open one cursor.
}
}
}
}
if (exception != null) {
throw exception;
}
}
} finally {
if (this.eventManager != null) {
this.eventManager.postExecuteCall(call, result);
}
}
return result;
}
/**
* INTERNAL:
*/
public ExposedNodeLinkedList getActiveCommandThreads() {
if (activeCommandThreads == null) {
activeCommandThreads = new ExposedNodeLinkedList();
}
return activeCommandThreads;
}
/**
* PUBLIC:
* Return the active session for the current active external (JTS) transaction.
* This should only be used with JTS and will return the session if no external transaction exists.
*/
@Override
public org.eclipse.persistence.sessions.Session getActiveSession() {
org.eclipse.persistence.sessions.Session activeSession = getActiveUnitOfWork();
if (activeSession == null) {
activeSession = this;
}
return activeSession;
}
/**
* PUBLIC:
* Return the active unit of work for the current active external (JTS) transaction.
* This should only be used with JTS and will return null if no external transaction exists.
*/
@Override
public org.eclipse.persistence.sessions.UnitOfWork getActiveUnitOfWork() {
if (hasExternalTransactionController()) {
return getExternalTransactionController().getActiveUnitOfWork();
}
/* Steven Vo: CR# 2517
Get from the server session since the external transaction controller could be
null out from the client session by TL WebLogic 5.1 to provide non-jts transaction
operations
*/
if (isClientSession()) {
return ((org.eclipse.persistence.sessions.server.ClientSession)this).getParent().getActiveUnitOfWork();
}
return null;
}
/**
* INTERNAL:
* Returns the alias descriptors Map.
*/
public Map getAliasDescriptors() {
return project.getAliasDescriptors();
}
/**
* ADVANCED:
* Answers the past time this session is as of. Indicates whether or not this
* is a special historical session where all objects are read relative to a
* particular point in time.
* @return An immutable object representation of the past time.
* <code>null</code> if no clause set, or this a regular session.
* @see #acquireHistoricalSession(org.eclipse.persistence.history.AsOfClause)
*/
@Override
public AsOfClause getAsOfClause() {
return null;
}
/**
* INTERNAL:
* Allow the session to be used from a session broker.
*/
public AbstractSession getBroker() {
return broker;
}
/**
* INTERNAL:
* The session that this query is executed against when not in transaction.
* The session containing the shared identity map.
* <p>
* In most cases this is the root ServerSession or DatabaseSession.
* <p>
* In cases where objects are not to be cached in the global identity map
* an alternate session may be returned:
* <ul>
* <li>A ClientSession if in transaction
* <li>An isolated ClientSession or HistoricalSession
* <li>A registered session of a root SessionBroker
* </ul>
*/
public AbstractSession getRootSession(DatabaseQuery query) {
ClassDescriptor descriptor = null;
if (query != null){
descriptor = query.getDescriptor();
}
return getParentIdentityMapSession(descriptor, true, true);
}
/**
* INTERNAL:
* Gets the parent session.
*/
public AbstractSession getParent() {
return null;
}
/**
* INTERNAL:
* Gets the next link in the chain of sessions followed by a query's check
* early return, the chain of sessions with identity maps all the way up to
* the root session.
*/
public AbstractSession getParentIdentityMapSession(DatabaseQuery query) {
ClassDescriptor descriptor = null;
if (query != null){
descriptor = query.getDescriptor();
}
return getParentIdentityMapSession(descriptor, false, false);
}
/**
* INTERNAL:
* Gets the next link in the chain of sessions followed by a query's check
* early return, the chain of sessions with identity maps all the way up to
* the root session.
* <p>
* Used for session broker which delegates to registered sessions, or UnitOfWork
* which checks parent identity map also.
* @param canReturnSelf true when method calls itself. If the path
* starting at <code>this</code> is acceptable. Sometimes true if want to
* move to the first valid session, i.e. executing on ClientSession when really
* should be on ServerSession.
* @param terminalOnly return the session we will execute the call on, not
* the next step towards it.
* @return this if there is no next link in the chain
*/
public AbstractSession getParentIdentityMapSession(DatabaseQuery query, boolean canReturnSelf, boolean terminalOnly) {
ClassDescriptor descriptor = null;
if (query != null){
descriptor = query.getDescriptor();
}
return getParentIdentityMapSession(descriptor, canReturnSelf, terminalOnly);
}
/**
* INTERNAL:
* Returns the appropriate IdentityMap session for this descriptor. Sessions can be
* chained and each session can have its own Cache/IdentityMap. Entities can be stored
* at different levels based on Cache Isolation. This method will return the correct Session
* for a particular Entity class based on the Isolation Level and the attributes provided.
*
* @param canReturnSelf true when method calls itself. If the path
* starting at <code>this</code> is acceptable. Sometimes true if want to
* move to the first valid session, i.e. executing on ClientSession when really
* should be on ServerSession.
* @param terminalOnly return the last session in the chain where the Enitity is stored.
* @return Session with the required IdentityMap
*/
public AbstractSession getParentIdentityMapSession(ClassDescriptor descriptor, boolean canReturnSelf, boolean terminalOnly) {
return this;
}
/**
* INTERNAL:
* Returns the default pessimistic lock timeout value.
*/
public Integer getPessimisticLockTimeoutDefault() {
return pessimisticLockTimeoutDefault;
}
public TimeUnit getPessimisticLockTimeoutUnitDefault() {
return pessimisticLockTimeoutUnitDefault;
}
/**
* PUBLIC:
* Return the default query timeout for this session.
* This timeout will apply to any queries that do not have a timeout set,
* and that do not have a default timeout defined in their descriptor.
*/
public int getQueryTimeoutDefault() {
return queryTimeoutDefault;
}
public TimeUnit getQueryTimeoutUnitDefault() {
return queryTimeoutUnitDefault;
}
@SuppressWarnings("unchecked")
public <T> InjectionManager<T> getInjectionManager() {
if (injectionManager == null){
injectionManager = createInjectionManager(this.getProperty(PersistenceUnitProperties.CDI_BEANMANAGER));
}
return injectionManager;
}
/**
* INTERNAL:
* Gets the session which this query will be executed on.
* Generally will be called immediately before the call is translated,
* which is immediately before session.executeCall.
* <p>
* Since the execution session also knows the correct datasource platform
* to execute on, it is often used in the mappings where the platform is
* needed for type conversion, or where calls are translated.
* <p>
* Is also the session with the accessor. Will return a ClientSession if
* it is in transaction and has a write connection.
* @return a session with a live accessor
* @param query may store session name or reference class for brokers case
*/
public AbstractSession getExecutionSession(DatabaseQuery query) {
return this;
}
/**
* INTERNAL:
* Return if the commit manager has been set.
*/
public boolean hasCommitManager() {
return commitManager != null;
}
/**
* INTERNAL:
* The commit manager is used to resolve referential integrity on commits of multiple objects.
* All brokered sessions share the same commit manager.
*/
public CommitManager getCommitManager() {
if (hasBroker()) {
return getBroker().getCommitManager();
}
// PERF: lazy init, not always required, not required for client sessions
if (commitManager == null) {
commitManager = new CommitManager(this);
}
return commitManager;
}
/**
* INTERNAL:
* Returns the set of read-only classes that gets assigned to each newly created UnitOfWork.
*
* @see org.eclipse.persistence.sessions.Project#setDefaultReadOnlyClasses(Collection)
*/
public Vector getDefaultReadOnlyClasses() {
//Bug#3911318 All brokered sessions share the same DefaultReadOnlyClasses.
if (hasBroker()) {
return getBroker().getDefaultReadOnlyClasses();
}
return getProject().getDefaultReadOnlyClasses();
}
/**
* ADVANCED:
* Return the descriptor specified for the class.
* If the class does not have a descriptor but implements an interface that is also implemented
* by one of the classes stored in the map, that descriptor will be stored under the
* new class. If a descriptor does not exist for the Class parameter, null is returned.
* If the passed Class parameter is null, then null will be returned.
*/
@Override
public ClassDescriptor getClassDescriptor(Class theClass) {
if (theClass == null) {
return null;
}
return getDescriptor(theClass);
}
/**
* ADVANCED:
* Return the descriptor specified for the object's class.
* If a descriptor does not exist for the Object parameter, null is returned.
* If the passed Object parameter is null, then null will be returned.
*/
@Override
public ClassDescriptor getClassDescriptor(Object domainObject) {
if (domainObject == null) {
return null;
}
return getDescriptor(domainObject);
}
/**
* PUBLIC:
* Return the descriptor for the alias.
* UnitOfWork delegates this to the parent
*/
@Override
public ClassDescriptor getClassDescriptorForAlias(String alias) {
return project.getDescriptorForAlias(alias);
}
/**
* ADVANCED:
* Return the descriptor specified for the class.
* If the class does not have a descriptor but implements an interface that is also implemented
* by one of the classes stored in the map, that descriptor will be stored under the
* new class. If the passed Class is null, null will be returned.
*/
@Override
public ClassDescriptor getDescriptor(Class theClass) {
if (theClass == null) {
return null;
}
// Optimize descriptor lookup through caching the last one accessed.
ClassDescriptor descriptor = this.lastDescriptorAccessed;
if ((descriptor != null) && (descriptor.getJavaClass() == theClass)) {
return descriptor;
}
if (this.descriptors != null) {
descriptor = this.descriptors.get(theClass);
} else {
descriptor = this.project.getDescriptors().get(theClass);
}
if (descriptor == null) {
if (hasBroker()) {
// Also check the broker
descriptor = getBroker().getDescriptor(theClass);
}
if (descriptor == null) {
// Allow for an event listener to lazy register the descriptor for a class.
if (this.eventManager != null) {
this.eventManager.missingDescriptor(theClass);
}
descriptor = getDescriptors().get(theClass);
if (descriptor == null) {
// This allows for the correct descriptor to be found if the class implements an interface,
// or extends a class that a descriptor is registered for.
// This is used by EJB to find the descriptor for a stub and remote to unwrap it,
// and by inheritance to allow for subclasses that have no additional state to not require a descriptor.
if (!theClass.isInterface()) {
Class[] interfaces = theClass.getInterfaces();
for (int index = 0; index < interfaces.length; ++index) {
Class interfaceClass = interfaces[index];
descriptor = getDescriptor(interfaceClass);
if (descriptor != null) {
getDescriptors().put(interfaceClass, descriptor);
break;
}
}
if (descriptor == null ) {
descriptor = checkHierarchyForDescriptor(theClass);
}
}
}
}
}
// Cache for optimization.
this.lastDescriptorAccessed = descriptor;
return descriptor;
}
/**
* ADVANCED:
* Return the descriptor specified for the object's class.
* If the passed Object is null, null will be returned.
*/
@Override
public ClassDescriptor getDescriptor(Object domainObject) {
if (domainObject == null) {
return null;
}
//Bug#3947714 Check and trigger the proxy here
if (this.project.hasProxyIndirection()) {
return getDescriptor(ProxyIndirectionPolicy.getValueFromProxy(domainObject).getClass());
}
return getDescriptor(domainObject.getClass());
}
/**
* PUBLIC:
* Return the descriptor for the alias.
* @param alias The descriptor alias.
* @return The descriptor for the alias or {@code null} if no descriptor was found.
*/
@Override
public ClassDescriptor getDescriptorForAlias(final String alias) {
// If we have a descriptors list return our sessions descriptor and
// not that of the project since we may be dealing with a multitenant
// descriptor which will have been initialized locally on the session.
// The project descriptor will be not initialized.
final ClassDescriptor desc = project.getDescriptorForAlias(alias);
if (desc != null && desc.hasMultitenantPolicy() && this.descriptors != null) {
return this.descriptors.get(desc.getJavaClass());
} else {
return desc;
}
}
/**
* ADVANCED:
* Return all registered descriptors.
*/
@Override
public Map<Class, ClassDescriptor> getDescriptors() {
return this.project.getDescriptors();
}
/**
* INTERNAL:
* Return if an event manager has been set.
*/
public boolean hasEventManager() {
return eventManager != null;
}
/**
* PUBLIC:
* Return the event manager.
* The event manager can be used to register for various session events.
*/
@Override
public SessionEventManager getEventManager() {
if (eventManager == null) {
synchronized (this) {
if (eventManager == null) {
// PERF: lazy init.
eventManager = new SessionEventManager(this);
}
}
}
return eventManager;
}
/**
* INTERNAL:
* Return a string which represents my ExceptionHandler's class
* Added for F2104: Properties.xml
* - gn
*/
public String getExceptionHandlerClass() {
String className = null;
try {
className = getExceptionHandler().getClass().getName();
} catch (Exception exception) {
return null;
}
return className;
}
/**
* PUBLIC:
* Return the ExceptionHandler.Exception handler can catch errors that occur on queries or during database access.
*/
@Override
public ExceptionHandler getExceptionHandler() {
return exceptionHandler;
}
/**
* PUBLIC:
* Used for JTS integration. If your application requires to have JTS control transactions instead of EclipseLink an
* external transaction controller must be specified.
* EclipseLink provides JTS controllers for several JTS implementations including JTS 1.0, Weblogic 5.1 and WebSphere 3.0.
*
* @see org.eclipse.persistence.transaction.JTATransactionController
*/
@Override
public ExternalTransactionController getExternalTransactionController() {
return externalTransactionController;
}
/**
* PUBLIC:
* The IdentityMapAccessor is the preferred way of accessing IdentityMap funcitons
* This will return an object which implements an interface which exposes all public
* IdentityMap functions.
*/
@Override
public org.eclipse.persistence.sessions.IdentityMapAccessor getIdentityMapAccessor() {
return identityMapAccessor;
}
/**
* INTERNAL:
* Return the internally available IdentityMapAccessor instance.
*/
public org.eclipse.persistence.internal.sessions.IdentityMapAccessor getIdentityMapAccessorInstance() {
return identityMapAccessor;
}
/**
* PUBLIC:
* Returns the integrityChecker.IntegrityChecker holds all the ClassDescriptor Exceptions.
*/
@Override
public IntegrityChecker getIntegrityChecker() {
// BUG# 2700595 - Lazily create an IntegrityChecker if one has not already been created.
if (integrityChecker == null) {
integrityChecker = new IntegrityChecker();
}
return integrityChecker;
}
/**
* ADVANCED:
* Return all pre-defined not yet parsed JPQL queries.
*/
@Override
public List<DatabaseQuery> getJPAQueries() {
return getProject().getJPAQueries();
}
/**
* ADVANCED:
* Return all pre-defined not yet parsed JPQL queries.
*/
public List<DatabaseQuery> getJPATablePerTenantQueries() {
return getProject().getJPATablePerTenantQueries();
}
/**
* PUBLIC:
* Return the writer to which an accessor writes logged messages and SQL.
* If not set, this reference defaults to a writer on System.out.
* To enable logging, logMessages must be turned on.
*
* @see #setLoggingOff(boolean)
*/
@Override
public Writer getLog() {
return getSessionLog().getWriter();
}
/**
* INTERNAL:
* Return the name of the session: class name + system hashcode.
* <p>
* This should be the implementation of toString(), and also the
* value should be calculated in the constructor for it is used all the
* time. However everything is lazily initialized now and the value is
* transient for the system hashcode could vary?
*/
public String getLogSessionString() {
if (logSessionString == null) {
StringWriter writer = new StringWriter();
writer.write(getSessionTypeString());
writer.write("(");
writer.write(String.valueOf(System.identityHashCode(this)));
writer.write(")");
logSessionString = writer.toString();
}
return logSessionString;
}
/**
* INTERNAL:
* Returns the type of session, its class.
* <p>
* Override to hide from the user when they are using an internal subclass
* of a known class.
* <p>
* A user does not need to know that their UnitOfWork is a
* non-deferred UnitOfWork, or that their ClientSession is an
* IsolatedClientSession.
*/
public String getSessionTypeString() {
return Helper.getShortClassName(getClass());
}
/**
* INTERNAL:
* Return the static metamodel class associated with the given model class
* if available. Callers must handle null.
*/
public String getStaticMetamodelClass(String modelClassName) {
if (staticMetamodelClasses != null) {
return staticMetamodelClasses.get(modelClassName);
}
return null;
}
/**
* OBSOLETE:
* Return the login, the login holds any database connection information given.
* This has been replaced by getDatasourceLogin to make use of the Login interface
* to support non-relational datasources,
* if DatabaseLogin API is required it will need to be cast.
*/
@Override
public DatabaseLogin getLogin() {
try {
return (DatabaseLogin)getDatasourceLogin();
} catch (ClassCastException wrongType) {
throw ValidationException.notSupportedForDatasource();
}
}
/**
* PUBLIC:
* Return the login, the login holds any database connection information given.
* This return the Login interface and may need to be cast to the datasource specific implementation.
*/
@Override
public Login getDatasourceLogin() {
if (this.project == null) {
return null;
}
return this.project.getDatasourceLogin();
}
/**
* INTERNAL:
* Return the mapped superclass descriptor if one exists for the given
* class name. Must check any broker as well.
*/
public ClassDescriptor getMappedSuperclass(String className) {
ClassDescriptor desc = getProject().getMappedSuperclass(className);
if (desc == null && hasBroker()) {
getBroker().getMappedSuperclass(className);
}
return desc;
}
/**
* PUBLIC:
* Return a set of multitenant context properties this session
*/
public Set<String> getMultitenantContextProperties() {
if (this.multitenantContextProperties == null) {
this.multitenantContextProperties = new HashSet<>();
}
return this.multitenantContextProperties;
}
/**
* PUBLIC:
* Return the name of the session.
* This is used with the session broker, or to give the session a more meaningful name.
*/
@Override
public String getName() {
return name;
}
/**
* ADVANCED:
* Return the sequnce number from the database
*/
@Override
public Number getNextSequenceNumberValue(Class domainClass) {
return (Number)getSequencing().getNextValue(domainClass);
}
/**
* INTERNAL:
* Return the number of units of work connected.
*/
public int getNumberOfActiveUnitsOfWork() {
return numberOfActiveUnitsOfWork;
}
/**
* INTERNAL:
* For use within the merge process this method will get an object from the shared
* cache using a readlock. If a readlock is unavailable then the merge manager will be
* transitioned to deferred locks and a deferred lock will be used.
*/
protected CacheKey getCacheKeyFromTargetSessionForMerge(Object implementation, ObjectBuilder builder, ClassDescriptor descriptor, MergeManager mergeManager){
Object original = null;
Object primaryKey = builder.extractPrimaryKeyFromObject(implementation, this, true);
if (primaryKey == null) {
return null;
}
CacheKey cacheKey = null;
if (mergeManager == null){
cacheKey = getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, implementation.getClass(), descriptor, true);
if (cacheKey != null){
cacheKey.checkReadLock();
}
return cacheKey;
}
cacheKey = getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, implementation.getClass(), descriptor, true);
if (cacheKey != null) {
if (cacheKey.acquireReadLockNoWait()) {
original = cacheKey.getObject();
cacheKey.releaseReadLock();
} else {
if (!mergeManager.isTransitionedToDeferredLocks()) {
getIdentityMapAccessorInstance().getWriteLockManager().transitionToDeferredLocks(mergeManager);
}
cacheKey.acquireDeferredLock();
original = cacheKey.getObject();
if (original == null) {
synchronized (cacheKey) {
if (cacheKey.isAcquired()) {
try {
cacheKey.wait();
} catch (InterruptedException e) {
//ignore and return
}
}
original = cacheKey.getObject();
}
}
cacheKey.releaseDeferredLock();
}
}
return cacheKey;
}
/**
* INTERNAL:
* Return the database platform currently connected to.
* The platform is used for database specific behavior.
* NOTE: this must only be used for relational specific usage,
* it will fail for non-relational datasources.
*/
@Override
public DatabasePlatform getPlatform() {
// PERF: Cache the platform.
if (platform == null) {
platform = getDatasourceLogin().getPlatform();
}
return (DatabasePlatform)platform;
}
/**
* INTERNAL:
* Return the class loader for the session's application.
* This loader should be able to load any application or EclipseLink class.
*/
public ClassLoader getLoader() {
return getDatasourcePlatform().getConversionManager().getLoader();
}
/**
* INTERNAL:
* Return the database platform currently connected to.
* The platform is used for database specific behavior.
*/
@Override
public Platform getDatasourcePlatform() {
// PERF: Cache the platform.
if (platform == null) {
platform = getDatasourceLogin().getDatasourcePlatform();
}
return platform;
}
/**
* INTERNAL:
* Marked internal as this is not customer API but helper methods for
* accessing the server platform from within EclipseLink's other sessions types
* (ie not DatabaseSession)
*/
@Override
public ServerPlatform getServerPlatform() {
return null;
}
/**
* INTERNAL:
* Return the database platform currently connected to
* for specified class.
* The platform is used for database specific behavior.
*/
@Override
public Platform getPlatform(Class domainClass) {
// PERF: Cache the platform.
if (platform == null) {
platform = getDatasourcePlatform();
}
return platform;
}
/**
* PUBLIC:
* Return the profiler.
* The profiler is a tool that can be used to determine performance bottlenecks.
* The profiler can be queries to print summaries and configure for logging purposes.
*/
@Override
public SessionProfiler getProfiler() {
return profiler;
}
/**
* PUBLIC:
* Return the project, the project holds configuartion information including the descriptors.
*/
@Override
public org.eclipse.persistence.sessions.Project getProject() {
return project;
}
/**
* ADVANCED:
* Allow for user defined properties.
*/
@Override
public Map<String, Object> getProperties() {
if (properties == null) {
properties = new HashMap(5);
}
return properties;
}
/**
* INTERNAL:
* Allow to check for user defined properties.
*/
public boolean hasProperties() {
return ((properties != null) && !properties.isEmpty());
}
/**
* INTERNAL:
* Return list of table per tenant multitenant descriptors.
*/
public boolean hasTablePerTenantDescriptors() {
return (tablePerTenantDescriptors != null && ! tablePerTenantDescriptors.isEmpty());
}
/**
* INTERNAL:
* Return a list of table per tenant multitenant queries.
*/
public boolean hasTablePerTenantQueries() {
return (tablePerTenantQueries != null && ! tablePerTenantQueries.isEmpty());
}
/**
* ADVANCED:
* Returns the user defined property.
*/
@Override
public Object getProperty(String name) {
if(this.properties==null){
return null;
}
return getProperties().get(name);
}
/**
* ADVANCED:
* Return all pre-defined queries.
*/
@Override
public Map<String, List<DatabaseQuery>> getQueries() {
// PERF: lazy init, not normally required.
if (queries == null) {
queries = new HashMap(5);
}
return queries;
}
/**
* ADVANCED:
* Return an attribute group of a particular name.
*/
/**
* ADVANCED
* Return all predefined attribute groups
*/
public Map<String, AttributeGroup> getAttributeGroups(){
if (this.attributeGroups == null){
this.attributeGroups = new HashMap<>(5);
}
return this.attributeGroups;
}
/**
* INTERNAL:
* Return the pre-defined queries in this session.
* A single vector containing all the queries is returned.
*
* @see #getQueries()
*/
public List<DatabaseQuery> getAllQueries() {
Vector allQueries = new Vector();
for (Iterator vectors = getQueries().values().iterator(); vectors.hasNext();) {
allQueries.addAll((Vector)vectors.next());
}
return allQueries;
}
/**
* PUBLIC:
* Return the query from the session pre-defined queries with the given name.
* This allows for common queries to be pre-defined, reused and executed by name.
*/
@Override
public DatabaseQuery getQuery(String name) {
return getQuery(name, null);
}
/**
* PUBLIC:
* Return the query from the session pre-defined queries with the given name and argument types.
* This allows for common queries to be pre-defined, reused and executed by name.
* This method should be used if the Session has multiple queries with the same name but
* different arguments.
*
* @see #getQuery(String)
*/
@Override
public DatabaseQuery getQuery(String name, List arguments) {
if (arguments instanceof Vector) {
return getQuery(name, (Vector)arguments);
} else {
return getQuery(name, new Vector(arguments));
}
}
/**
* PUBLIC:
* Return the query from the session pre-defined queries with the given name and argument types.
* This allows for common queries to be pre-defined, reused and executed by name.
* This method should be used if the Session has multiple queries with the same name but
* different arguments.
*
* @see #getQuery(String)
*/
public DatabaseQuery getQuery(String name, Vector arguments) {
return getQuery(name, arguments, true);
}
/**
* INTERNAL:
* Return the query from the session pre-defined queries with the given name and argument types.
* This allows for common queries to be pre-defined, reused and executed by name.
* This method should be used if the Session has multiple queries with the same name but
* different arguments.
*
* @param shouldSearchParent indicates whether parent should be searched if query not found.
* @see #getQuery(String, List)
*/
public DatabaseQuery getQuery(String name, Vector arguments, boolean shouldSearchParent) {
Vector queries = (Vector)getQueries().get(name);
if ((queries != null) && !queries.isEmpty()) {
// Short circuit the simple, most common case of only one query.
if (queries.size() == 1) {
return (DatabaseQuery)queries.firstElement();
}
// CR#3754; Predrag; mar 19/2002;
// We allow multiple named queries with the same name but
// different argument set; we can have only one query with
// no arguments; Vector queries is not sorted;
// When asked for the query with no parameters the
// old version did return the first query - wrong:
// return (DatabaseQuery) queries.firstElement();
int argumentTypesSize = 0;
if (arguments != null) {
argumentTypesSize = arguments.size();
}
Vector argumentTypes = new Vector(argumentTypesSize);
for (int i = 0; i < argumentTypesSize; i++) {
argumentTypes.addElement(arguments.elementAt(i).getClass());
}
for (Enumeration queriesEnum = queries.elements(); queriesEnum.hasMoreElements();) {
DatabaseQuery query = (DatabaseQuery)queriesEnum.nextElement();
if (Helper.areTypesAssignable(argumentTypes, query.getArgumentTypes())) {
return query;
}
}
}
if(shouldSearchParent) {
AbstractSession parent = getParent();
if(parent != null) {
return parent.getQuery(name, arguments, true);
}
}
return null;
}
/**
* Returns an AttributeGroup by name
*/
/**
* INTERNAL:
* Return the Sequencing object used by the session.
*/
public Sequencing getSequencing() {
return null;
}
/**
* INTERNAL:
* Return the session to be used for the class.
* Used for compatibility with the session broker.
*/
public AbstractSession getSessionForClass(Class domainClass) {
if (hasBroker()) {
return getBroker().getSessionForClass(domainClass);
}
return this;
}
/**
* INTERNAL:
* Return the session by name.
* Used for compatibility with the session broker.
*/
public AbstractSession getSessionForName(String name) throws ValidationException {
if (hasBroker()) {
return getBroker().getSessionForName(name);
}
return this;
}
/**
* PUBLIC:
* Return the session log to which an accessor logs messages and SQL.
* If not set, this will default to a session log on a writer on System.out.
* To enable logging, logMessages must be turned on.
*
* @see #setLoggingOff(boolean)
*/
@Override
public SessionLog getSessionLog() {
if (sessionLog == null) {
setSessionLog(new DefaultSessionLog());
}
return sessionLog;
}
/**
* INTERNAL:
* Return list of table per tenant multitenant descriptors.
*/
public List<ClassDescriptor> getTablePerTenantDescriptors() {
if (tablePerTenantDescriptors == null) {
tablePerTenantDescriptors = new ArrayList<>();
}
return tablePerTenantDescriptors;
}
/**
* INTERNAL:
* Return list of table per tenant multitenant descriptors.
*/
public List<DatabaseQuery> getTablePerTenantQueries() {
if (tablePerTenantQueries == null) {
tablePerTenantQueries = new ArrayList<>();
}
return tablePerTenantQueries;
}
/**
* INTERNAL:
* The transaction mutex ensure mutual exclusion on transaction across multiple threads.
*/
public ConcurrencyManager getTransactionMutex() {
// PERF: not always required, defer.
if (transactionMutex == null) {
synchronized (this) {
if (transactionMutex == null) {
transactionMutex = new ConcurrencyManager();
}
}
}
return transactionMutex;
}
/**
* PUBLIC:
* Allow any WARNING level exceptions that occur within EclipseLink to be logged and handled by the exception handler.
*/
@Override
public Object handleException(RuntimeException exception) throws RuntimeException {
if ((exception instanceof EclipseLinkException)) {
EclipseLinkException eclipseLinkException = (EclipseLinkException)exception;
if (eclipseLinkException.getSession() == null) {
eclipseLinkException.setSession(this);
}
//Bug#3559280 Avoid logging an exception twice
if (!eclipseLinkException.hasBeenLogged()) {
logThrowable(SessionLog.WARNING, null, exception);
eclipseLinkException.setHasBeenLogged(true);
}
} else {
logThrowable(SessionLog.WARNING, null, exception);
}
if (hasExceptionHandler()) {
if (this.broker != null && this.broker.hasExceptionHandler()) {
try {
return getExceptionHandler().handleException(exception);
} catch (RuntimeException ex) {
// handle the original exception
return this.broker.getExceptionHandler().handleException(exception);
}
} else {
return getExceptionHandler().handleException(exception);
}
} else {
if (this.broker != null && this.broker.hasExceptionHandler()) {
return this.broker.getExceptionHandler().handleException(exception);
} else {
throw exception;
}
}
}
/**
* INTERNAL:
* Allow the session to be used from a session broker.
*/
public boolean hasBroker() {
return broker != null;
}
/**
* ADVANCED:
* Return true if a descriptor exists for the given class.
*/
@Override
public boolean hasDescriptor(Class theClass) {
if (theClass == null) {
return false;
}
return getDescriptors().get(theClass) != null;
}
/**
* PUBLIC:
* Return if an exception handler is present.
*/
@Override
public boolean hasExceptionHandler() {
if (exceptionHandler == null) {
return false;
}
return true;
}
/**
* PUBLIC:
* Used for JTA integration. If your application requires to have JTA control transactions instead of EclipseLink an
* external transaction controler must be specified. EclipseLink provides JTA controlers for JTA 1.0 and application
* servers.
* @see org.eclipse.persistence.transaction.JTATransactionController
*/
@Override
public boolean hasExternalTransactionController() {
return externalTransactionController != null;
}
/**
* INTERNAL:
* Set up the IdentityMapManager. This method allows subclasses of Session to override
* the default IdentityMapManager functionality.
*/
public void initializeIdentityMapAccessor() {
this.identityMapAccessor = new org.eclipse.persistence.internal.sessions.IdentityMapAccessor(this, new IdentityMapManager(this));
}
/**
* PUBLIC:
* Insert the object and all of its privately owned parts into the database.
* Insert should only be used if the application knows that the object is new,
* otherwise writeObject should be used.
* The insert operation can be customized through using an insert query.
*
* @exception DatabaseException if an error occurs on the database,
* these include constraint violations, security violations and general database errors.
*
* @see InsertObjectQuery
* @see #writeObject(Object)
*/
public Object insertObject(Object domainObject) throws DatabaseException {
InsertObjectQuery query = new InsertObjectQuery();
query.setObject(domainObject);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* INTERNAL:
* Return the results from exeucting the database query.
* The arguments should be a database row with raw data values.
* This method is provided to allow subclasses to change the default querying behavior.
* All querying goes through this method.
*/
public Object internalExecuteQuery(DatabaseQuery query, AbstractRecord databaseRow) throws DatabaseException {
return query.execute(this, databaseRow);
}
/**
* INTERNAL:
* Returns true if the session is a session Broker.
*/
public boolean isBroker() {
return false;
}
/**
* INTERNAL:
* Returns true if the session is in a session Broker.
*/
public boolean isInBroker() {
return isInBroker;
}
/**
* PUBLIC:
* Return if the class is defined as read-only.
*/
public boolean isClassReadOnly(Class theClass) {
ClassDescriptor descriptor = getDescriptor(theClass);
return isClassReadOnly(theClass, descriptor);
}
/**
* INTERNAL:
* Return if the class is defined as read-only.
* PERF: Pass descriptor to avoid re-lookup.
*/
public boolean isClassReadOnly(Class theClass, ClassDescriptor descriptor) {
if ((descriptor != null) && descriptor.shouldBeReadOnly()) {
return true;
}
if (theClass != null) {
return getDefaultReadOnlyClasses().contains(theClass);
}
return false;
}
/**
* PUBLIC:
* Return if this session is a client session.
*/
@Override
public boolean isClientSession() {
return false;
}
/**
* PUBLIC:
* Return if this session is an isolated client session.
*/
public boolean isIsolatedClientSession() {
return false;
}
/**
* PUBLIC:
* Return if this session is an exclusive isolated client session.
*/
public boolean isExclusiveIsolatedClientSession() {
return false;
}
/**
* PUBLIC:
* Return if this session is connected to the database.
*/
@Override
public boolean isConnected() {
if (getAccessor() == null) {
return false;
}
return getAccessor().isConnected();
}
/**
* PUBLIC:
* Return if this session is a database session.
*/
@Override
public boolean isDatabaseSession() {
return false;
}
/**
* PUBLIC:
* Return if this session is a distributed session.
*/
@Override
public boolean isDistributedSession() {
return false;
}
/**
* PUBLIC:
* Return if a profiler is being used.
*/
@Override
public boolean isInProfile() {
return isInProfile;
}
/**
* PUBLIC:
* Allow for user deactive a profiler
*/
public void setIsInProfile(boolean inProfile) {
this.isInProfile = inProfile;
}
/**
* INTERNAL:
* Set if this session is contained in a SessionBroker.
*/
public void setIsInBroker(boolean isInBroker) {
this.isInBroker = isInBroker;
}
/**
* PUBLIC:
* Return if this session's decendants should use finalizers.
* The allows certain finalizers such as in ClientSession to be enabled.
* These are disable by default for performance reasons.
*/
@Override
public boolean isFinalizersEnabled() {
return isFinalizersEnabled;
}
/**
* INTERNAL:
* Register a finalizer to release this session.
*/
public void registerFinalizer() {
// Ensure the finalizer is referenced until the session is gc'd.
setProperty("finalizer", new SessionFinalizer(this));
}
/**
* INTERNAL:
* Return if this session is a historical session.
*/
public boolean isHistoricalSession() {
return false;
}
/**
* PUBLIC:
* Set if this session's decendants should use finalizers.
* The allows certain finalizers such as in ClientSession to be enabled.
* These are disable by default for performance reasons.
*/
@Override
public void setIsFinalizersEnabled(boolean isFinalizersEnabled) {
this.isFinalizersEnabled = isFinalizersEnabled;
}
/**
* PUBLIC:
* Return if the session is currently in the progress of a database transaction.
* Because nested transactions are allowed check if the transaction mutex has been aquired.
*/
public boolean isInTransaction() {
return this.transactionMutex != null && this.transactionMutex.isAcquired();
}
/**
* INTERNAL:
* used to see if JPA Queries have been processed during initialization
*/
public boolean isJPAQueriesProcessed(){
return this.jpaQueriesProcessed;
}
/**
* PUBLIC:
* Returns true if Protected Entities should be built within this session
*/
public boolean isProtectedSession(){
return true;
}
/**
* PUBLIC:
* Return if this session is remote.
*/
@Override
public boolean isRemoteSession() {
return false;
}
/**
* PUBLIC:
* Return if this session is a unit of work.
*/
@Override
public boolean isRemoteUnitOfWork() {
return false;
}
/**
* PUBLIC:
* Return if this session is a server session.
*/
@Override
public boolean isServerSession() {
return false;
}
/**
* PUBLIC:
* Return if this session is a session broker.
*/
@Override
public boolean isSessionBroker() {
return false;
}
/**
* INTERNAL:
* Return if this session is synchronized.
*/
public boolean isSynchronized() {
return isSynchronized;
}
/**
* PUBLIC:
* Return if this session is a unit of work.
*/
@Override
public boolean isUnitOfWork() {
return false;
}
/**
* ADVANCED:
* Extract and return the primary key from the object.
*/
@Override
public Object getId(Object domainObject) throws ValidationException {
ClassDescriptor descriptor = getDescriptor(domainObject);
return keyFromObject(domainObject, descriptor);
}
/**
* ADVANCED:
* Extract and return the primary key from the object.
*/
public Object keyFromObject(Object domainObject, ClassDescriptor descriptor) throws ValidationException {
if (descriptor == null) {
throw ValidationException.missingDescriptor(domainObject.getClass().getName());
}
Object implemention = descriptor.getObjectBuilder().unwrapObject(domainObject, this);
if (implemention == null) {
return null;
}
return descriptor.getObjectBuilder().extractPrimaryKeyFromObject(implemention, this);
}
/**
* PUBLIC:
* Log the log entry.
*/
@Override
public void log(SessionLogEntry entry) {
if (this.isLoggingOff) {
return;
}
if (shouldLog(entry.getLevel(), entry.getNameSpace())) {
if (entry.getSession() == null) {// Used for proxy session.
entry.setSession(this);
}
getSessionLog().log(entry);
}
}
/**
* Log a untranslated message to the EclipseLink log at FINER level.
*/
@Override
public void logMessage(String message) {
if (this.isLoggingOff) {
return;
}
log(SessionLog.FINER, SessionLog.MISC, message, null, null, false);
}
/**
* INTERNAL:
* A call back to do session specific preparation of a query.
* <p>
* The call back occurs soon before we clone the query for execution,
* meaning that if this method needs to clone the query then the caller will
* determine that it doesn't need to clone the query itself.
*/
public DatabaseQuery prepareDatabaseQuery(DatabaseQuery query) {
if (!isUnitOfWork() && query.isObjectLevelReadQuery()) {
return ((ObjectLevelReadQuery)query).prepareOutsideUnitOfWork(this);
} else {
return query;
}
}
/**
* PUBLIC:
* Read all of the instances of the class from the database.
* This operation can be customized through using a ReadAllQuery,
* or through also passing in a selection criteria.
*
* @see ReadAllQuery
* @see #readAllObjects(Class, Expression)
*/
@Override
public Vector readAllObjects(Class domainClass) throws DatabaseException {
ReadAllQuery query = new ReadAllQuery();
query.setIsExecutionClone(true);
query.setReferenceClass(domainClass);
return (Vector)executeQuery(query);
}
/**
* PUBLIC:
* Read all of the instances of the class from the database return through execution the SQL string.
* The SQL string must be a valid SQL select statement or selecting stored procedure call.
* This operation can be customized through using a ReadAllQuery.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
*
* @see ReadAllQuery
*/
public Vector readAllObjects(Class domainClass, String sqlString) throws DatabaseException {
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(domainClass);
query.setSQLString(sqlString);
query.setIsExecutionClone(true);
return (Vector)executeQuery(query);
}
/**
* PUBLIC:
* Read all the instances of the class from the database returned through execution the Call string.
* The Call can be an SQLCall or JPQLCall.
*
* example: session.readAllObjects(Employee.class, new SQLCall("SELECT * FROM EMPLOYEE"));
* @see Call
*/
@Override
public Vector readAllObjects(Class referenceClass, Call aCall) throws DatabaseException {
ReadAllQuery raq = new ReadAllQuery();
raq.setReferenceClass(referenceClass);
raq.setCall(aCall);
raq.setIsExecutionClone(true);
return (Vector)executeQuery(raq);
}
/**
* PUBLIC:
* Read all of the instances of the class from the database matching the given expression.
* This operation can be customized through using a ReadAllQuery.
*
* @see ReadAllQuery
*/
@Override
public Vector readAllObjects(Class domainClass, Expression expression) throws DatabaseException {
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(domainClass);
query.setSelectionCriteria(expression);
query.setIsExecutionClone(true);
return (Vector)executeQuery(query);
}
/**
* PUBLIC:
* Read the first instance of the class from the database.
* This operation can be customized through using a ReadObjectQuery,
* or through also passing in a selection criteria.
*
* @see ReadObjectQuery
* @see #readAllObjects(Class, Expression)
*/
@Override
public Object readObject(Class domainClass) throws DatabaseException {
ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(domainClass);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* PUBLIC:
* Read the first instance of the class from the database return through execution the SQL string.
* The SQL string must be a valid SQL select statement or selecting stored procedure call.
* This operation can be customized through using a ReadObjectQuery.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
*
* @see ReadObjectQuery
*/
public Object readObject(Class domainClass, String sqlString) throws DatabaseException {
ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(domainClass);
query.setSQLString(sqlString);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* PUBLIC:
* Read the first instance of the class from the database returned through execution the Call string.
* The Call can be an SQLCall or JPQLCall.
*
* example: session.readObject(Employee.class, new SQLCall("SELECT * FROM EMPLOYEE"));
* @see SQLCall
* @see JPQLCall
*/
@Override
public Object readObject(Class domainClass, Call aCall) throws DatabaseException {
ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(domainClass);
query.setCall(aCall);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* PUBLIC:
* Read the first instance of the class from the database matching the given expression.
* This operation can be customized through using a ReadObjectQuery.
*
* @see ReadObjectQuery
*/
@Override
public Object readObject(Class domainClass, Expression expression) throws DatabaseException {
ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(domainClass);
query.setSelectionCriteria(expression);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* PUBLIC:
* Use the example object to consruct a read object query by the objects primary key.
* This will read the object from the database with the same primary key as the object
* or null if no object is found.
*/
@Override
public Object readObject(Object object) throws DatabaseException {
ReadObjectQuery query = new ReadObjectQuery();
query.setSelectionObject(object);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* PUBLIC:
* Refresh the attributes of the object and of all of its private parts from the database.
* The object will be pessimisticly locked on the database for the duration of the transaction.
* If the object is already locked this method will wait until the lock is released.
* A no wait option is available through setting the lock mode.
* @see #refreshAndLockObject(Object, short)
*/
public Object refreshAndLockObject(Object object) throws DatabaseException {
return refreshAndLockObject(object, ObjectBuildingQuery.LOCK);
}
/**
* PUBLIC:
* Refresh the attributes of the object and of all of its private parts from the database.
* The object will be pessimisticly locked on the database for the duration of the transaction.
* <p>Lock Modes: ObjectBuildingQuery.NO_LOCK, LOCK, LOCK_NOWAIT
*/
public Object refreshAndLockObject(Object object, short lockMode) throws DatabaseException {
ReadObjectQuery query = new ReadObjectQuery();
query.setSelectionObject(object);
query.refreshIdentityMapResult();
query.cascadePrivateParts();
query.setLockMode(lockMode);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* PUBLIC:
* Refresh the attributes of the object and of all of its private parts from the database.
* This can be used to ensure the object is up to date with the database.
* Caution should be used when using this to make sure the application has no un commited
* changes to the object.
*/
@Override
public Object refreshObject(Object object) throws DatabaseException {
return refreshAndLockObject(object, ObjectBuildingQuery.NO_LOCK);
}
/**
* PUBLIC:
* Release the session.
* This does nothing by default, but allows for other sessions such as the ClientSession to do something.
*/
@Override
public void release() {
}
/**
* INTERNAL:
* Release the unit of work, if lazy release the connection.
*/
public void releaseUnitOfWork(UnitOfWorkImpl unitOfWork) {
// Nothing is required by default, allow subclasses to do cleanup.
setNumberOfActiveUnitsOfWork(getNumberOfActiveUnitsOfWork() - 1);
}
/**
* PUBLIC:
* Remove the user defined property.
*/
@Override
public void removeProperty(String property) {
getProperties().remove(property);
}
/**
* PUBLIC:
* Remove all queries with the given queryName regardless of the argument types.
*
* @see #removeQuery(String, Vector)
*/
@Override
public void removeQuery(String queryName) {
getQueries().remove(queryName);
}
/**
* PUBLIC:
* Remove the specific query with the given queryName and argumentTypes.
*/
public void removeQuery(String queryName, Vector argumentTypes) {
Vector queries = (Vector)getQueries().get(queryName);
if (queries == null) {
return;
} else {
DatabaseQuery query = null;
for (Enumeration enumtr = queries.elements(); enumtr.hasMoreElements();) {
query = (DatabaseQuery)enumtr.nextElement();
if (Helper.areTypesAssignable(argumentTypes, query.getArgumentTypes())) {
break;
}
}
if (query != null) {
queries.remove(query);
}
}
}
/**
* PROTECTED:
* Attempts to rollback the running internally started external transaction.
* Returns true only in one case -
* extenal transaction has been internally rolled back during this method call:
* wasJTSTransactionInternallyStarted()==true in the beginning of this method and
* wasJTSTransactionInternallyStarted()==false in the end of this method.
*/
protected boolean rollbackExternalTransaction() {
boolean externalTransactionHasRolledBack = false;
if (hasExternalTransactionController() && wasJTSTransactionInternallyStarted()) {
try {
getExternalTransactionController().rollbackTransaction(this);
} catch (RuntimeException exception) {
handleException(exception);
}
if (!wasJTSTransactionInternallyStarted()) {
externalTransactionHasRolledBack = true;
log(SessionLog.FINER, SessionLog.TRANSACTION, "external_transaction_has_rolled_back_internally");
}
}
return externalTransactionHasRolledBack;
}
/**
* PUBLIC:
* Rollback the active database transaction.
* This allows a group of database modification to be commited or rolledback as a unit.
* All writes/deletes will be sent to the database be will not be visible to other users until commit.
* Although databases do not allow nested transaction,
* EclipseLink supports nesting through only committing to the database on the outer commit.
*
* @exception DatabaseException if the database connection is lost or the rollback fails.
* @exception ConcurrencyException if this session is not within a transaction.
*/
public void rollbackTransaction() throws DatabaseException, ConcurrencyException {
ConcurrencyManager mutex = getTransactionMutex();
// Ensure release of mutex and call subclass specific release.
try {
if (!mutex.isNested()) {
if (this.eventManager != null) {
this.eventManager.preRollbackTransaction();
}
basicRollbackTransaction();
if (this.eventManager != null) {
this.eventManager.postRollbackTransaction();
}
}
} finally {
mutex.release();
// If there is no db transaction in progress
// if there is an active external transaction
// which was started internally - it should be rolled back internally, too.
if (!mutex.isAcquired()) {
rollbackExternalTransaction();
}
}
}
/**
* INTERNAL:
* Set the accessor.
*/
public void setAccessor(Accessor accessor) {
this.accessors = new ArrayList(1);
this.accessors.add(accessor);
}
/**
* INTERNAL:
* Allow the session to be used from a session broker.
*/
public void setBroker(AbstractSession broker) {
this.broker = broker;
}
/**
* INTERNAL:
* The commit manager is used to resolve referncial integrity on commits of multiple objects.
*/
public void setCommitManager(CommitManager commitManager) {
this.commitManager = commitManager;
}
public void setInjectionManager(
InjectionManager injectionManager) {
this.injectionManager = injectionManager;
}
/**
* INTERNAL:
* Set the event manager.
* The event manager can be used to register for various session events.
*/
public void setEventManager(SessionEventManager eventManager) {
this.eventManager = eventManager;
if (eventManager != null) {
this.eventManager.setSession(this);
}
}
/**
* PUBLIC:
* Set the exceptionHandler.
* Exception handler can catch errors that occur on queries or during database access.
*/
@Override
public void setExceptionHandler(ExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
/**
* Used for JTS integration internally by ServerPlatform.
*/
@Override
public void setExternalTransactionController(ExternalTransactionController externalTransactionController) {
this.externalTransactionController = externalTransactionController;
if (externalTransactionController == null) {
return;
}
if (!hasBroker()) {
externalTransactionController.setSession(this);
}
}
/**
* PUBLIC:
* set the integrityChecker. IntegrityChecker holds all the ClassDescriptor Exceptions.
*/
@Override
public void setIntegrityChecker(IntegrityChecker integrityChecker) {
this.integrityChecker = integrityChecker;
}
/**
* INTERNAL:
* used to set if JPA Queries have been processed during initialization
*/
public void setJPAQueriesProcessed(boolean jpaQueriesProcessed){
this.jpaQueriesProcessed = jpaQueriesProcessed;
}
/**
* PUBLIC:
* Set the writer to which an accessor writes logged messages and SQL.
* If not set, this reference defaults to a writer on System.out.
* To enable logging logMessages() is used.
*
* @see #setLoggingOff(boolean)
*/
@Override
public void setLog(Writer log) {
getSessionLog().setWriter(log);
}
/**
* PUBLIC:
* Set the login.
*/
public void setLogin(DatabaseLogin login) {
setDatasourceLogin(login);
}
/**
* PUBLIC:
* Set the login.
*/
public void setLogin(Login login) {
setDatasourceLogin(login);
}
/**
* PUBLIC:
* Set the login.
*/
public void setDatasourceLogin(Login login) {
getProject().setDatasourceLogin(login);
this.platform = null;
}
/**
* PUBLIC:
* Set the name of the session.
* This is used with the session broker.
*/
@Override
public void setName(String name) {
this.name = name;
}
protected void setNumberOfActiveUnitsOfWork(int numberOfActiveUnitsOfWork) {
this.numberOfActiveUnitsOfWork = numberOfActiveUnitsOfWork;
}
/**
* PUBLIC:
* Set the default pessimistic lock timeout value. This value will be used
* to set the WAIT clause of a SQL SELECT FOR UPDATE statement. It defines
* how long EcliseLink should wait for a lock on the database row before
* aborting.
*/
public void setPessimisticLockTimeoutDefault(Integer pessimisticLockTimeoutDefault) {
this.pessimisticLockTimeoutDefault = pessimisticLockTimeoutDefault;
}
public void setPessimisticLockTimeoutUnitDefault(TimeUnit pessimisticLockTimeoutUnitDefault) {
this.pessimisticLockTimeoutUnitDefault = pessimisticLockTimeoutUnitDefault;
}
/**
* PUBLIC:
* Set the default query timeout for this session.
* This timeout will apply to any queries that do not have a timeout set,
* and that do not have a default timeout defined in their descriptor.
*/
@Override
public void setQueryTimeoutDefault(int queryTimeoutDefault) {
this.queryTimeoutDefault = queryTimeoutDefault;
}
@Override
public void setQueryTimeoutUnitDefault(TimeUnit queryTimeoutUnitDefault) {
this.queryTimeoutUnitDefault = queryTimeoutUnitDefault;
}
/**
* PUBLIC:
* Set the profiler for the session.
* This allows for performance operations to be profiled.
*/
@Override
public void setProfiler(SessionProfiler profiler) {
this.profiler = profiler;
if (profiler != null) {
profiler.setSession(this);
setIsInProfile(getProfiler().getProfileWeight() != SessionProfiler.NONE);
// Clear cached flag that bypasses the profiler check.
getIdentityMapAccessorInstance().getIdentityMapManager().checkIsCacheAccessPreCheckRequired();
} else {
setIsInProfile(false);
}
}
/**
* INTERNAL:
* Set the project, the project holds configuration information including the descriptors.
*/
public void setProject(org.eclipse.persistence.sessions.Project project) {
this.project = project;
}
/**
* INTERNAL:
* Set the user defined properties by shallow copying the propertiesMap.
*/
public void setProperties(Map<String, Object> propertiesMap) {
if (null == propertiesMap) {
// Keep current behavior and set properties map to null
properties = null;
} else {
/*
* Bug# 219097 Clone as (HashMap) possible immutable maps to avoid
* an UnsupportedOperationException on a later put() We do not know
* the key:value type values at design time. putAll() is not
* synchronized. We clone all maps whether immutable or not.
*/
properties = new HashMap();
// Shallow copy all internal key:value pairs - a null propertiesMap will throw a NPE
properties.putAll(propertiesMap);
}
}
/**
* PUBLIC: Allow for user defined properties.
*/
@Override
public void setProperty(String propertyName, Object propertyValue) {
getProperties().put(propertyName, propertyValue);
}
/**
* INTERNAL:
* Set the named queries.
*/
public void setQueries(Map<String, List<DatabaseQuery>> queries) {
this.queries = queries;
}
/**
* PUBLIC:
* Set the session log to which an accessor logs messages and SQL.
* If not set, this will default to a session log on a writer on System.out.
* To enable logging, log level can not be OFF.
* Also set a backpointer to this session in SessionLog.
*
* @see #logMessage(String)
*/
@Override
public void setSessionLog(SessionLog sessionLog) {
this.isLoggingOff = false;
this.sessionLog = sessionLog;
if ((sessionLog != null) && (sessionLog.getSession() == null)) {
sessionLog.setSession(this);
}
}
/**
* INTERNAL:
* Set isSynchronized flag to indicate that this session is synchronized.
* This method should only be called by setSynchronized methods of derived classes.
*/
public void setSynchronized(boolean synched) {
isSynchronized = synched;
}
protected void setTransactionMutex(ConcurrencyManager transactionMutex) {
this.transactionMutex = transactionMutex;
}
/**
* INTERNAL:
* Return if a JTS transaction was started by the session.
* The session will start a JTS transaction if a unit of work or transaction is begun without a JTS transaction present.
*/
public void setWasJTSTransactionInternallyStarted(boolean wasJTSTransactionInternallyStarted) {
this.wasJTSTransactionInternallyStarted = wasJTSTransactionInternallyStarted;
}
/**
* PUBLIC:
* Return if logging is enabled (false if log level is OFF)
*/
@Override
public boolean shouldLogMessages() {
if (this.isLoggingOff) {
return false;
}
if (getLogLevel(null) == SessionLog.OFF) {
return false;
} else {
return true;
}
}
/**
* INTERNAL:
* Start the operation timing.
*/
@Override
public void startOperationProfile(String operationName) {
if (this.isInProfile) {
getProfiler().startOperationProfile(operationName);
}
}
/**
* INTERNAL:
* Start the operation timing.
*/
public void startOperationProfile(String operationName, DatabaseQuery query, int weight) {
if (this.isInProfile) {
getProfiler().startOperationProfile(operationName, query, weight);
}
}
/**
* Print the connection status with the session.
*/
@Override
public String toString() {
StringWriter writer = new StringWriter();
writer.write(getSessionTypeString() + "(" + Helper.cr() + "\t" + getAccessor() + Helper.cr() + "\t" + getDatasourcePlatform() + ")");
return writer.toString();
}
/**
* INTERNAL:
* Unwrap the object if required.
* This is used for the wrapper policy support and EJB.
*/
public Object unwrapObject(Object proxy) {
return getDescriptor(proxy).getObjectBuilder().unwrapObject(proxy, this);
}
/**
* PUBLIC:
* Update the object and all of its privately owned parts in the database.
* Update should only be used if the application knows that the object is new,
* otherwise writeObject should be used.
* The update operation can be customized through using an update query.
*
* @exception DatabaseException if an error occurs on the database,
* these include constraint violations, security violations and general database errors.
* @exception OptimisticLockException if the object's descriptor is using optimistic locking and
* the object has been updated or deleted by another user since it was last read.
*
* @see UpdateObjectQuery
* @see #writeObject(Object)
*/
public Object updateObject(Object domainObject) throws DatabaseException, OptimisticLockException {
UpdateObjectQuery query = new UpdateObjectQuery();
query.setObject(domainObject);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* ADVANCED:
* This can be used to help debugging an object identity problem.
* An object identity problem is when an object in the cache references an object not in the cache.
* This method will validate that all cached objects are in a correct state.
*/
@Override
public void validateCache() {
getIdentityMapAccessorInstance().validateCache();
}
/**
* INTERNAL:
* This method will be used to update the query with any settings required
* For this session. It can also be used to validate execution.
*/
public void validateQuery(DatabaseQuery query) {
// a no-op for this class
}
/**
* TESTING:
* This is used by testing code to ensure that a deletion was successful.
*/
public boolean verifyDelete(Object domainObject) {
ObjectBuilder builder = getDescriptor(domainObject).getObjectBuilder();
Object implementation = builder.unwrapObject(domainObject, this);
return builder.verifyDelete(implementation, this);
}
/**
* INTERNAL:
* Return if a JTS transaction was started by the session.
* The session will start a JTS transaction if a unit of work or transaction is begun without a JTS transaction present.
*/
public boolean wasJTSTransactionInternallyStarted() {
return wasJTSTransactionInternallyStarted;
}
/**
* INTERNAL:
* Wrap the object if required.
* This is used for the wrapper policy support and EJB.
*/
public Object wrapObject(Object implementation) {
return getDescriptor(implementation).getObjectBuilder().wrapObject(implementation, this);
}
/**
* INTERNAL:
* Write all of the objects and all of their privately owned parts in the database.
* The allows for a group of new objects to be commited as a unit.
* The objects will be commited through a single transactions and any
* foreign keys/circular references between the objects will be resolved.
*/
protected void writeAllObjectsWithChangeSet(UnitOfWorkChangeSet uowChangeSet) throws DatabaseException, OptimisticLockException {
getCommitManager().commitAllObjectsWithChangeSet(uowChangeSet);
}
/**
* PUBLIC:
* Write the object and all of its privately owned parts in the database.
* Write will determine if an insert or an update should be done,
* it may go to the database to determine this (by default will check the identity map).
* The write operation can be customized through using an write query.
*
* @exception DatabaseException if an error occurs on the database,
* these include constraint violations, security violations and general database errors.
* @exception OptimisticLockException if the object's descriptor is using optimistic locking and
* the object has been updated or deleted by another user since it was last read.
*
* @see WriteObjectQuery
* @see #insertObject(Object)
* @see #updateObject(Object)
*/
public Object writeObject(Object domainObject) throws DatabaseException, OptimisticLockException {
WriteObjectQuery query = new WriteObjectQuery();
query.setObject(domainObject);
query.setIsExecutionClone(true);
return executeQuery(query);
}
/**
* INTERNAL:
* This method notifies the accessor that a particular sets of writes has
* completed. This notification can be used for such thing as flushing the
* batch mechanism
*/
public void writesCompleted() {
if (getAccessors() == null) {
return;
}
for (Accessor accessor : getAccessors()) {
accessor.writesCompleted(this);
}
}
/**
* INTERNAL:
* RemoteCommandManager method. This is a required method in order
* to implement the CommandProcessor interface.
* Process the remote command from the RCM. The command may have come from
* any remote session or application. Since this is a EclipseLink session we can
* always assume that the object that we receive here will be a Command object.
*/
@Override
public void processCommand(Object command) {
((Command)command).executeWithSession(this);
}
/**
* INTERNAL:
* Process the JPA named queries into EclipseLink Session queries. This
* method is called after descriptor initialization.
* Temporarily made public for ODI. Should not be used elsewhere.
*/
public void processJPAQueries() {
if (! jpaQueriesProcessed) {
// Process the JPA queries that do not query table per tenant entities.
for (DatabaseQuery jpaQuery : getJPAQueries()) {
processJPAQuery(jpaQuery);
}
// Process the JPA queries that query table per tenant entities. At
// the EMF level, these queries will be initialized and added right
// away. At the EM level we must defer their initialization to each
// individual client session.
for (DatabaseQuery jpaQuery : getJPATablePerTenantQueries()) {
boolean processQuery = true;
for (ClassDescriptor descriptor : jpaQuery.getDescriptors()) {
// If the descriptor is not fully initialized then we can
// not initialize the query and must isolate it to be
// initialized and stored per client session (EM).
if (! descriptor.isFullyInitialized()) {
processQuery = false;
break;
}
}
if (processQuery) {
processJPAQuery(jpaQuery);
} else {
addTablePerTenantQuery(jpaQuery);
}
}
jpaQueriesProcessed = true;
}
}
/**
* INTERNAL:
* Process the JPA named query into an EclipseLink Session query. This
* method is called after descriptor initialization.
*/
protected void processJPAQuery(DatabaseQuery jpaQuery) {
// This is a hack, to allow the core Session to initialize JPA queries, without have a dependency on JPA.
// They need to be initialized after login, as the database platform must be known.
try {
jpaQuery.prepareInternal(this);
} catch (RuntimeException re) {
// If jpql-tolerate-error==true, any problems will be ignored at query prep time and the runtime will
// continue chugging along. The invalid query will be left in place so that an exception
// will be thrown at runtime if a user attempts to use it.
if (!tolerateInvalidJPQL) {
throw re;
}
}
DatabaseQuery databaseQuery = (DatabaseQuery) jpaQuery.getProperty("databasequery");
databaseQuery = (databaseQuery == null) ? jpaQuery : databaseQuery;
addQuery(databaseQuery, false); // this should be true but for backward compatibility it
// is set to false.
}
/**
* PUBLIC:
* Return the CommandManager that allows this session to act as a
* CommandProcessor and receive or propagate commands from/to the
* EclipseLink cluster.
*
* @see CommandManager
* @return The CommandManager instance that controls the remote command
* service for this session
*/
@Override
public CommandManager getCommandManager() {
return commandManager;
}
/**
* ADVANCED:
* Set the CommandManager that allows this session to act as a
* CommandProcessor and receive or propagate commands from/to the
* EclipseLink cluster.
*
* @see CommandManager
* @param mgr The CommandManager instance to control the remote command
* service for this session
*/
@Override
public void setCommandManager(CommandManager mgr) {
commandManager = mgr;
}
/**
* PUBLIC:
* Return whether changes should be propagated to other sessions or applications
* in a EclipseLink cluster through the Remote Command Manager mechanism. In order for
* this to occur the CommandManager must be set.
*
* @see #setCommandManager(CommandManager)
* @return True if propagation is set to occur, false if not
*/
public boolean shouldPropagateChanges() {
return shouldPropagateChanges;
}
/**
* ADVANCED:
* Set whether changes should be propagated to other sessions or applications
* in a EclipseLink cluster through the Remote Command Manager mechanism. In order for
* this to occur the CommandManager must be set.
*
* @see #setCommandManager(CommandManager)
* @param choice If true (and the CommandManager is set) then propagation will occur
*/
public void setShouldPropagateChanges(boolean choice) {
shouldPropagateChanges = choice;
}
/**
* INTERNAL:
* RemoteCommandManager method. This is a required method in order
* to implement the CommandProcessor interface.
* Return true if a message at the specified log level would be logged given the
* log level setting of this session. This can be used by the CommandManager to
* know whether it should even bother to create the localized strings and call
* the logMessage method, or if it would only find that the message would not be
* logged because the session level does not permit logging. The log level passed
* in will be one of the constants LOG_ERROR, LOG_WARNING, LOG_INFO, and LOG_DEBUG
* defined in the CommandProcessor interface.
*/
@Override
public boolean shouldLogMessages(int logLevel) {
if (this.isLoggingOff) {
return false;
}
if (LOG_ERROR == logLevel) {
return getLogLevel(SessionLog.PROPAGATION) <= SessionLog.SEVERE;
}
if (LOG_WARNING == logLevel) {
return getLogLevel(SessionLog.PROPAGATION) <= SessionLog.WARNING;
}
if (LOG_INFO == logLevel) {
return getLogLevel(SessionLog.PROPAGATION) <= SessionLog.FINER;
}
if (LOG_DEBUG == logLevel) {
return getLogLevel(SessionLog.PROPAGATION) <= SessionLog.FINEST;
}
return false;
}
/**
* INTERNAL:
* RemoteCommandManager method. This is a required method in order
* to implement the CommandProcessor interface.
* Log the specified message string at the specified level if it should be logged
* given the log level setting in this session. The log level passed in will be one
* of the constants LOG_ERROR, LOG_WARNING, LOG_INFO, and LOG_DEBUG defined in the
* CommandProcessor interface.
*/
@Override
public void logMessage(int logLevel, String message) {
if (this.isLoggingOff) {
return;
}
if (shouldLogMessages(logLevel)) {
int level;
switch (logLevel) {
case CommandProcessor.LOG_ERROR:
level = SessionLog.SEVERE;
break;
case CommandProcessor.LOG_WARNING:
level = SessionLog.WARNING;
break;
case CommandProcessor.LOG_INFO:
level = SessionLog.FINER;
break;
case CommandProcessor.LOG_DEBUG:
level = SessionLog.FINEST;
break;
default:
level = SessionLog.ALL;
}
log(level, SessionLog.PROPAGATION, message, null, null, false);
}
}
/**
* PUBLIC:
* <p>
* Return the log level
* </p>
*
* @return the log level
* @param category the string representation of a EclipseLink category, e.g. "sql", "transaction" ...
*/
@Override
public int getLogLevel(String category) {
return getSessionLog().getLevel(category);
}
/**
* PUBLIC:
* <p>
* Return the log level
* </p>
* @return the log level
*/
@Override
public int getLogLevel() {
return getSessionLog().getLevel();
}
/**
* PUBLIC:
* <p>
* Set the log level
* </p>
*
* @param level the new log level
*/
@Override
public void setLogLevel(int level) {
this.isLoggingOff = false;
getSessionLog().setLevel(level);
}
/**
* PUBLIC:
* Return true if SQL logging should log visible bind parameters. If the
* shouldDisplayData is not set, check the session log level and return
* true for a level greater than CONFIG.
*/
public boolean shouldDisplayData() {
return getSessionLog().shouldDisplayData();
}
/**
* PUBLIC:
* <p>
* Check if a message of the given level would actually be logged.</p>
*
* @return true if the given message level will be logged
* @param level the log request level
* @param category the string representation of a EclipseLink category
*/
@Override
public boolean shouldLog(int level, String category) {
if (this.isLoggingOff) {
return false;
}
return getSessionLog().shouldLog(level, category);
}
/**
* PUBLIC:
* <p>
* Log a message with level and category that needs to be translated.
* </p>
*
* @param level the log request level value
* @param message the string message
* @param category the string representation of a EclipseLink category.
*/
public void log(int level, String category, String message) {
if (this.isLoggingOff) {
return;
}
if (!shouldLog(level, category)) {
return;
}
log(level, category, message, null);
}
/**
* PUBLIC:
* <p>
* Log a message with level, category and a parameter that needs to be translated.
* </p>
*
* @param level the log request level value
* @param message the string message
* @param category the string representation of a EclipseLink category.
* @param param a parameter of the message
*/
public void log(int level, String category, String message, Object param) {
if (this.isLoggingOff) {
return;
}
if (!shouldLog(level, category)) {
return;
}
log(level, category, message, new Object[] { param });
}
/**
* PUBLIC:
* <p>
* Log a message with level, category and two parameters that needs to be translated.
* </p>
*
* @param level the log request level value
* @param message the string message
* @param category the string representation of a EclipseLink category.
* @param param1 a parameter of the message
* @param param2 second parameter of the message
*/
public void log(int level, String category, String message, Object param1, Object param2) {
if (this.isLoggingOff) {
return;
}
if (!shouldLog(level, category)) {
return;
}
log(level, category, message, new Object[] { param1, param2 });
}
/**
* PUBLIC:
* <p>
* Log a message with level, category and three parameters that needs to be translated.
* </p>
*
* @param level the log request level value
* @param message the string message
* @param category the string representation of a EclipseLink category.
* @param param1 a parameter of the message
* @param param2 second parameter of the message
* @param param3 third parameter of the message
*/
public void log(int level, String category, String message, Object param1, Object param2, Object param3) {
if (this.isLoggingOff) {
return;
}
if (!shouldLog(level, category)) {
return;
}
log(level, category, message, new Object[] { param1, param2, param3 });
}
/**
* PUBLIC:
* <p>
* Log a message with level, category and an array of parameters that needs to be translated.
* </p>
*
* @param level the log request level value
* @param message the string message
* @param category the string representation of a EclipseLink category.
* @param params array of parameters to the message
*/
public void log(int level, String category, String message, Object[] params) {
if (this.isLoggingOff) {
return;
}
log(level, category, message, params, null);
}
/**
* PUBLIC:
* <p>
* Log a message with level, category, parameters and accessor that needs to be translated.
* </p>
*
* @param level the log request level value
* @param message the string message
* @param params array of parameters to the message
* @param accessor the connection that generated the log entry
* @param category the string representation of a EclipseLink category.
*/
public void log(int level, String category, String message, Object[] params, Accessor accessor) {
if (this.isLoggingOff) {
return;
}
log(level, category, message, params, accessor, true);
}
/**
* PUBLIC:
* <p>
* Log a message with level, category, parameters and accessor. shouldTranslate determines if the message needs to be translated.
* </p>
*
* @param level the log request level value
* @param message the string message
* @param params array of parameters to the message
* @param accessor the connection that generated the log entry
* @param category the string representation of a EclipseLink category.
* @param shouldTranslate true if the message needs to be translated.
*/
public void log(int level, String category, String message, Object[] params, Accessor accessor, boolean shouldTranslate) {
if (this.isLoggingOff) {
return;
}
if (shouldLog(level, category)) {
startOperationProfile(SessionProfiler.Logging);
log(new SessionLogEntry(level, category, this, message, params, accessor, shouldTranslate));
endOperationProfile(SessionProfiler.Logging);
}
}
/**
* PUBLIC:
* <p>
* Log a throwable with level and category.
* </p>
*
* @param level the log request level value
* @param category the string representation of a EclipseLink category.
* @param throwable a Throwable
*/
public void logThrowable(int level, String category, Throwable throwable) {
if (this.isLoggingOff) {
return;
}
// Must not create the log if not logging as is a performance issue.
if (shouldLog(level, category)) {
startOperationProfile(SessionProfiler.Logging);
log(new SessionLogEntry(this, level, category, throwable));
endOperationProfile(SessionProfiler.Logging);
}
}
/**
* PUBLIC:
* <p>
* This method is called when a severe level message needs to be logged.
* The message will be translated
* </p>
*
* @param message the message key
*/
public void severe(String message, String category) {
if (this.isLoggingOff) {
return;
}
log(SessionLog.SEVERE, category, message);
}
/**
* PUBLIC:
* <p>
* This method is called when a warning level message needs to be logged.
* The message will be translated
* </p>
*
* @param message the message key
*/
public void warning(String message, String category) {
if (this.isLoggingOff) {
return;
}
log(SessionLog.WARNING, category, message);
}
/**
* PUBLIC:
* <p>
* This method is called when a info level message needs to be logged.
* The message will be translated
* </p>
*
* @param message the message key
*/
public void info(String message, String category) {
if (this.isLoggingOff) {
return;
}
log(SessionLog.INFO, category, message);
}
/**
* PUBLIC:
* <p>
* This method is called when a config level message needs to be logged.
* The message will be translated
* </p>
*
* @param message the message key
*/
public void config(String message, String category) {
if (this.isLoggingOff) {
return;
}
log(SessionLog.CONFIG, category, message);
}
/**
* PUBLIC:
* <p>
* This method is called when a fine level message needs to be logged.
* The message will be translated
* </p>
*
* @param message the message key
*/
public void fine(String message, String category) {
if (this.isLoggingOff) {
return;
}
log(SessionLog.FINE, category, message);
}
/**
* PUBLIC:
* <p>
* This method is called when a finer level message needs to be logged.
* The message will be translated
* </p>
*
* @param message the message key
*/
public void finer(String message, String category) {
if (this.isLoggingOff) {
return;
}
log(SessionLog.FINER, category, message);
}
/**
* PUBLIC:
* <p>
* This method is called when a finest level message needs to be logged.
* The message will be translated
* </p>
*
* @param message the message key
*/
public void finest(String message, String category) {
if (this.isLoggingOff) {
return;
}
log(SessionLog.FINEST, category, message);
}
/**
* PUBLIC:
* Allow any SEVERE level exceptions that occur within EclipseLink to be logged and handled by the exception handler.
*/
@Override
public Object handleSevere(RuntimeException exception) throws RuntimeException {
logThrowable(SessionLog.SEVERE, null, exception);
if (hasExceptionHandler()) {
return getExceptionHandler().handleException(exception);
} else {
throw exception;
}
}
/**
* INTERNAL:
*/
public void releaseReadConnection(Accessor connection) {
//bug 4668234 -- used to only release connections on server sessions but should always release
//do nothing -- overidden in UnitOfWork,ClientSession and ServerSession
}
/**
* INTERNAL:
* Copies descriptors cached on the Project.
* Used after Project.descriptors has been reset by addDescriptor(s) when the session is connected.
*/
public void copyDescriptorsFromProject() {
this.descriptors = getDescriptors();
}
/**
* INTERNAL:
* This method will be used to copy all EclipseLink named queries defined in descriptors into the session.
* @param allowSameQueryNameDiffArgsCopyToSession if the value is true, it allow
* multiple queries of the same name but different arguments to be copied to the session.
*/
public void copyDescriptorNamedQueries(boolean allowSameQueryNameDiffArgsCopyToSession) {
for (ClassDescriptor descriptor : getProject().getOrderedDescriptors()) {
Map queries = descriptor.getQueryManager().getQueries();
if ((queries != null) && (queries.size() > 0)) {
for (Iterator keyValueItr = queries.entrySet().iterator(); keyValueItr.hasNext();){
Map.Entry entry = (Map.Entry) keyValueItr.next();
Vector thisQueries = (Vector)entry.getValue();
if ((thisQueries != null) && (thisQueries.size() > 0)){
for( Iterator thisQueriesItr=thisQueries.iterator();thisQueriesItr.hasNext();){
DatabaseQuery queryToBeAdded = (DatabaseQuery)thisQueriesItr.next();
if (allowSameQueryNameDiffArgsCopyToSession){
addQuery(queryToBeAdded, false);
} else {
if (getQuery(queryToBeAdded.getName()) == null){
addQuery(queryToBeAdded, false);
} else {
log(SessionLog.WARNING, SessionLog.PROPERTIES, "descriptor_named_query_cannot_be_added", new Object[]{queryToBeAdded,queryToBeAdded.getName(),queryToBeAdded.getArgumentTypes()});
}
}
}
}
}
}
}
}
/**
* INTERNAL:
* This method rises appropriate for the session event(s)
* right after connection is acquired.
*/
public void postAcquireConnection(Accessor accessor) {
if (getProject().hasVPDIdentifier(this)) {
if (getPlatform().supportsVPD()) {
DatabaseQuery query = getPlatform().getVPDSetIdentifierQuery(getProject().getVPDIdentifier());
List argValues = new ArrayList();
query.addArgument(getProject().getVPDIdentifier());
argValues.add(getProperty(getProject().getVPDIdentifier()));
executeQuery(query, argValues);
} else {
throw ValidationException.vpdNotSupported(getPlatform().getClass().getName());
}
}
if (this.eventManager != null) {
this.eventManager.postAcquireConnection(accessor);
}
}
/**
* INTERNAL:
* This method rises appropriate for the session event(s)
* right before the connection is released.
*/
public void preReleaseConnection(Accessor accessor) {
if (getProject().hasVPDIdentifier(this)) {
if (getPlatform().supportsVPD()) {
DatabaseQuery query = getPlatform().getVPDClearIdentifierQuery(getProject().getVPDIdentifier());
List argValues = new ArrayList();
query.addArgument(getProject().getVPDIdentifier());
argValues.add(getProperty(getProject().getVPDIdentifier()));
executeQuery(query, argValues);
} else {
throw ValidationException.vpdNotSupported(getPlatform().getClass().getName());
}
}
if (this.eventManager != null) {
this.eventManager.preReleaseConnection(accessor);
}
}
/**
* INTERNAL:
* Execute the call on the database. Calling this method will bypass a
* global setting to disallow native SQL queries. (set by default when
* one Entity is marked as multitenant)
*
* The row count is returned.
*
* The call can be a stored procedure call, SQL call or other type of call.
*
* <p>Example:
* <p>session.executeNonSelectingCall(new SQLCall("Delete from Employee"), true);
*
* @see #priviledgedExecuteSelectingCall(Call)
*/
public int priviledgedExecuteNonSelectingCall(Call call) throws DatabaseException {
DataModifyQuery query = new DataModifyQuery();
query.setAllowNativeSQLQuery(true);
query.setIsExecutionClone(true);
query.setCall(call);
Integer value = (Integer)executeQuery(query);
if (value == null) {
return 0;
} else {
return value;
}
}
/**
* INTERNAL:
* Execute the call on the database and return the result. Calling this
* method will bypass a global setting to disallow native SQL queries. (set
* by default when one Entity is marked as multitenant)
*
* The call must return a value, if no value is return executeNonSelectCall
* must be used.
*
* The call can be a stored procedure call, SQL call or other type of call.
*
* A vector of database rows is returned, database row implements Java 2 Map
* which should be used to access the data.
*
* <p>Example:
* <p>session.executeSelectingCall(new SQLCall("Select * from Employee");
*
* @see #priviledgedExecuteNonSelectingCall(Call)
*/
public Vector priviledgedExecuteSelectingCall(Call call) throws DatabaseException {
DataReadQuery query = new DataReadQuery();
query.setAllowNativeSQLQuery(true);
query.setCall(call);
query.setIsExecutionClone(true);
return (Vector)executeQuery(query);
}
/**
* INTERNAL:
* This method is called in case externalConnectionPooling is used.
* If returns true, accessor used by the session keeps its
* connection open until released by the session.
*/
public boolean isExclusiveConnectionRequired() {
return false;
}
/**
* Stores the default Session wide reference mode that a UnitOfWork will use when referencing
* managed objects.
* @see org.eclipse.persistence.config.ReferenceMode
*/
@Override
public ReferenceMode getDefaultReferenceMode() {
return defaultReferenceMode;
}
/**
* Stores the default Session wide reference mode that a UnitOfWork will use when referencing
* managed objects.
* @see org.eclipse.persistence.config.ReferenceMode
*/
@Override
public void setDefaultReferenceMode(ReferenceMode defaultReferenceMode) {
this.defaultReferenceMode = defaultReferenceMode;
}
/**
* This method will load the passed object or collection of objects using the passed AttributeGroup.
* In case of collection all members should be either objects of the same mapped type
* or have a common inheritance hierarchy mapped root class.
* The AttributeGroup should correspond to the object type.
*
*/
public void load(Object objectOrCollection, AttributeGroup group) {
if (objectOrCollection == null || group == null) {
return;
}
if (objectOrCollection instanceof Collection) {
Iterator iterator = ((Collection)objectOrCollection).iterator();
while (iterator.hasNext()) {
Object object = iterator.next();
load(object, group, getClassDescriptor(object.getClass()), false);
}
} else {
ClassDescriptor concreteDescriptor = getClassDescriptor(objectOrCollection.getClass());
load(objectOrCollection, group, concreteDescriptor, false);
}
}
/**
* This method will load the passed object or collection of objects using the passed AttributeGroup.
* In case of collection all members should be either objects of the same mapped type
* or have a common inheritance hierarchy mapped root class.
* The AttributeGroup should correspond to the object type.
*
*/
public void load(Object objectOrCollection, AttributeGroup group, ClassDescriptor referenceDescriptor, boolean fromFetchGroup) {
if (objectOrCollection == null || group == null) {
return;
}
if (objectOrCollection instanceof Collection) {
Iterator iterator = ((Collection)objectOrCollection).iterator();
while (iterator.hasNext()) {
load(iterator.next(), group, referenceDescriptor, fromFetchGroup);
}
} else {
ClassDescriptor concreteDescriptor = referenceDescriptor;
if (concreteDescriptor.hasInheritance() && !objectOrCollection.getClass().equals(concreteDescriptor.getJavaClass())){
concreteDescriptor = concreteDescriptor.getInheritancePolicy().getDescriptor(objectOrCollection.getClass());
}
AttributeGroup concreteGroup = group.findGroup(concreteDescriptor);
concreteDescriptor.getObjectBuilder().load(objectOrCollection, concreteGroup, this, fromFetchGroup);
}
}
public CacheKey retrieveCacheKey(Object primaryKey, ClassDescriptor concreteDescriptor, JoinedAttributeManager joinManager, ObjectBuildingQuery query){
CacheKey cacheKey;
//lock the object in the IM
// PERF: Only use deferred locking if required.
// CR#3876308 If joining is used, deferred locks are still required.
if (query.requiresDeferredLocks()) {
cacheKey = this.getIdentityMapAccessorInstance().acquireDeferredLock(primaryKey, concreteDescriptor.getJavaClass(), concreteDescriptor, query.isCacheCheckComplete() || query.shouldRetrieveBypassCache());
if (cacheKey.getActiveThread() != Thread.currentThread()) {
int counter = 0;
while ((cacheKey.getObject() == null) && (counter < 1000)) {
//must release lock here to prevent acquiring multiple deferred locks but only
//releasing one at the end of the build object call.
//bug 5156075
cacheKey.releaseDeferredLock();
//sleep and try again if we are not the owner of the lock for CR 2317
// prevents us from modifying a cache key that another thread has locked.
try {
Thread.sleep(10);
} catch (InterruptedException exception) {
}
cacheKey = this.getIdentityMapAccessorInstance().acquireDeferredLock(primaryKey, concreteDescriptor.getJavaClass(), concreteDescriptor, query.isCacheCheckComplete() || query.shouldRetrieveBypassCache());
if (cacheKey.getActiveThread() == Thread.currentThread()) {
break;
}
counter++;
}
if (counter == 1000) {
throw ConcurrencyException.maxTriesLockOnBuildObjectExceded(cacheKey.getActiveThread(), Thread.currentThread());
}
}
} else {
cacheKey = this.getIdentityMapAccessorInstance().acquireLock(primaryKey, concreteDescriptor.getJavaClass(), concreteDescriptor, query.isCacheCheckComplete() || query.shouldRetrieveBypassCache());
}
return cacheKey;
}
/**
* PUBLIC:
* Return the session's partitioning policy.
*/
@Override
public PartitioningPolicy getPartitioningPolicy() {
return partitioningPolicy;
}
/**
* PUBLIC:
* Set the session's partitioning policy.
* A PartitioningPolicy is used to partition, load-balance or replicate data across multiple difference databases
* or across a database cluster such as Oracle RAC.
* Partitioning can provide improved scalability by allowing multiple database machines to service requests.
*/
@Override
public void setPartitioningPolicy(PartitioningPolicy partitioningPolicy) {
this.partitioningPolicy = partitioningPolicy;
}
/**
* INTERNAL:
* This currently only used by JPA with RCM to force a refresh of the metadata used within EntityManagerFactoryWrappers
*/
public MetadataRefreshListener getRefreshMetadataListener() {
return metadatalistener;
}
public void setRefreshMetadataListener(MetadataRefreshListener metadatalistener) {
this.metadatalistener = metadatalistener;
}
/**
* ADVANCED:
* Return if the session enables concurrent processing.
* Concurrent processing allow certain processing to be done on seperate threads.
* This can result in improved performance.
* This will use the session's server platform's thread pool.
*/
public boolean isConcurrent() {
return this.isConcurrent;
}
/**
* ADVANCED:
* Set if the session enables concurrent processing.
* Concurrent processing allow certain processing to be done on seperate threads.
* This can result in improved performance.
* This will use the session's server platform's thread pool.
*/
public void setIsConcurrent(boolean isConcurrent) {
this.isConcurrent = isConcurrent;
}
/**
* ADVANCED:
* Set to indicate whether ObjectLevelReadQuery should by default use ResultSet Access optimization.
* If not set then parent's flag is used, is none set then ObjectLevelReadQuery.isResultSetAccessOptimizedQueryDefault is used.
* If the optimization specified by the session is ignored if incompatible with other query settings.
*/
public void setShouldOptimizeResultSetAccess(boolean shouldOptimizeResultSetAccess) {
this.shouldOptimizeResultSetAccess = shouldOptimizeResultSetAccess;
}
/**
* ADVANCED:
* Indicates whether ObjectLevelReadQuery should by default use ResultSet Access optimization.
* Optimization specified by the session is ignored if incompatible with other query settings.
*/
public boolean shouldOptimizeResultSetAccess() {
return this.shouldOptimizeResultSetAccess;
}
/**
* ADVANCED: Indicates whether an invalid NamedQuery will be tolerated at init time.
*
* Default is false.
*/
public void setTolerateInvalidJPQL(boolean b) {
this.tolerateInvalidJPQL = b;
}
/**
* ADVANCED: Indicates whether an invalid NamedQuery will be tolerated at init time.
*
* Default is false.
*/
public boolean shouldTolerateInvalidJPQL() {
return this.tolerateInvalidJPQL;
}
}