org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 2011, 2021 Oracle and/or its affiliates. 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:
// tware - initial implementation as part of extensibility feature
// 01/11/2013-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support
// 01/16/2013-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support
// 02/04/2013-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support
// 05/26/2016-2.7 Tomas Kraus
// - 494610: Session Properties map should be Map
package org.eclipse.persistence.internal.jpa;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import jakarta.persistence.Cache;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.Query;
import jakarta.persistence.SynchronizationType;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.metamodel.Metamodel;
import org.eclipse.persistence.config.ReferenceMode;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.PersistenceUnitLoadingException;
import org.eclipse.persistence.internal.indirection.IndirectionPolicy;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.DatabaseSessionImpl;
import org.eclipse.persistence.internal.sessions.coordination.MetadataRefreshCommand;
import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.queries.AttributeGroup;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.FetchGroupTracker;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.UnitOfWork.CommitOrderType;
import org.eclipse.persistence.sessions.broker.SessionBroker;
import org.eclipse.persistence.sessions.coordination.CommandManager;
import org.eclipse.persistence.sessions.factories.SessionManager;
import org.eclipse.persistence.sessions.server.Server;
import org.eclipse.persistence.sessions.server.ServerSession;
/**
* Wraps our implementation of EntityManagerFactory
* Most operations are forwarded to the delegate. This wrapper is used to enable
* the refreshMetadata functionality which allows you to switch the underlying metadata for
* an EMF after deploy time.
* @author tware
*
*/
public class EntityManagerFactoryImpl implements EntityManagerFactory, PersistenceUnitUtil, JpaEntityManagerFactory {
protected EntityManagerFactoryDelegate delegate;
/**
* Returns the id of the entity. A generated id is not guaranteed to be
* available until after the database insert has occurred. Returns null if
* the entity does not yet have an id
*
* @return id of the entity
* @throws IllegalArgumentException
* if the entity is found not to be an entity.
*/
public static Object getIdentifier(Object entity, AbstractSession session) {
ClassDescriptor descriptor = session.getDescriptor(entity);
if (descriptor == null) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("jpa_persistence_util_non_persistent_class", new Object[] { entity }));
}
if (descriptor.getCMPPolicy() != null) {
return descriptor.getCMPPolicy().createPrimaryKeyInstance(entity, session);
} else {
// 308950: Alternatively, CacheImpl.getId(entity) handles a null CMPPolicy case for weaved and unweaved domain object
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("jpa_persistence_util_non_persistent_class", new Object[] { entity }));
}
}
/**
* Determine the load state of an entity belonging to the persistence unit.
* This method can be used to determine the load state of an entity passed
* as a reference. An entity is considered loaded if all attributes for
* which FetchType EAGER has been specified have been loaded. The
* isLoaded(Object, String) method should be used to determine the load
* state of an attribute. Not doing so might lead to unintended loading of
* state.
*
* @param entity
* whose load state is to be determined
* @return false if the entity has not been loaded, else true.
*/
public static Boolean isLoaded(Object entity, AbstractSession session) {
ClassDescriptor descriptor = session.getDescriptor(entity);
if (descriptor == null) {
return null;
}
List mappings = descriptor.getMappings();
Iterator i = mappings.iterator();
while (i.hasNext()) {
DatabaseMapping mapping = i.next();
if (!mapping.isLazy() && !isLoaded(entity, mapping.getAttributeName(), mapping)) {
return false;
}
}
return true;
}
/**
* Determine the load state of a given persistent attribute of an entity
* belonging to the persistence unit.
*
* @param entity
* containing the attribute
* @param attributeName
* name of attribute whose load state is to be determined
* @return false if entity's state has not been loaded or if the attribute
* state has not been loaded, otherwise true
*/
public static Boolean isLoaded(Object entity, String attributeName, AbstractSession session) {
ClassDescriptor descriptor = session.getDescriptor(entity);
if (descriptor == null) {
return null;
}
if (descriptor.hasFetchGroupManager()){
if (!descriptor.getFetchGroupManager().isAttributeFetched(entity, attributeName)){
return false;
}
}
DatabaseMapping mapping = descriptor.getMappingForAttributeName(attributeName);
if (mapping == null) {
return null;
}
return isLoaded(entity, attributeName, mapping);
}
/**
* Check whether a named attribute on a given entity with a given mapping
* has been loaded.
*
* This method will check the valueholder or indirect collection for LAZY
* ForeignReferenceMappings to see if has been instantiated and otherwise
* check the fetch group.
*
*/
public static boolean isLoaded(Object entity, String attributeName, DatabaseMapping mapping) {
if (mapping.isForeignReferenceMapping()) {
if (mapping.isLazy()) {
Object value = mapping.getAttributeValueFromObject(entity);
IndirectionPolicy policy = ((ForeignReferenceMapping) mapping).getIndirectionPolicy();
return policy.objectIsInstantiated(value);
}
}
if (entity instanceof FetchGroupTracker) {
return ((FetchGroupTracker) entity)._persistence_isAttributeFetched(attributeName);
} else {
return true;
}
}
/**
* Will return an instance of the Factory. Should only be called by
* EclipseLink.
*
*/
public EntityManagerFactoryImpl(AbstractSession serverSession) {
delegate = new EntityManagerFactoryDelegate(serverSession, this);
}
public EntityManagerFactoryImpl(EntityManagerSetupImpl setupImpl, Map properties) {
delegate = new EntityManagerFactoryDelegate(setupImpl, properties, this);
}
/**
* Create a dynamic persistence unit which does not use the persistence.xml.
* Instead all configuration is driven from the provided persistence unit
* properties and descriptors.
*/
public EntityManagerFactoryImpl(String persistenceUnitName, Map properties, List descriptors) {
delegate = new EntityManagerFactoryDelegate(persistenceUnitName, properties, descriptors, this);
}
/**
* ADVANCED:
* Re-bootstrap this factory. This method will rebuild the EntityManagerFactory. It should be used
* in conjunction with a MetadataSource to allow mappings to be changed in a running system. All existing
* EntityMangers will continue to function with the old metadata, but new factories will use the new metadata.
*/
@Override
public void refreshMetadata(Map properties){
EntityManagerSetupImpl setupImpl = delegate.getSetupImpl();
if (setupImpl == null){
throw PersistenceUnitLoadingException.cannotRefreshEntityManagerFactoryCreatedFromSession(delegate.getServerSession().getName());
}
String sessionName = setupImpl.getSessionName();
Map existingProperties = delegate.getProperties();
Map deployProperties = new HashMap<>();
deployProperties.putAll(existingProperties);
if (properties != null){
deployProperties.putAll(properties);
}
EntityManagerSetupImpl newSetupImpl = setupImpl.refreshMetadata(properties);
EntityManagerFactoryDelegate oldDelegate = delegate;
delegate = new EntityManagerFactoryDelegate(newSetupImpl, deployProperties, this);
// This code has been added to allow validation to occur without actually calling createEntityManager
// RCM refresh command requires the DEPLOY_ON_STARTUP property is set to true so the listener can be added to the session.
try{
if (newSetupImpl.shouldGetSessionOnCreateFactory(deployProperties)) {
ServerSession session = getServerSession();
CommandManager rcm = session.getCommandManager();
if (rcm != null && newSetupImpl.shouldSendMetadataRefreshCommand(deployProperties)) {
MetadataRefreshCommand command = new MetadataRefreshCommand(properties);
rcm.propagateCommand(command);
}
session.setRefreshMetadataListener(newSetupImpl);
}
} catch (RuntimeException ex) {
if(delegate != null) {
delegate.close();
} else {
newSetupImpl.undeploy();
}
synchronized(EntityManagerFactoryProvider.emSetupImpls){
// bring back the old emSetupImpl and session
EntityManagerFactoryProvider.emSetupImpls.put(sessionName, setupImpl);
SessionManager.getManager().getSessions().put(sessionName, setupImpl.getSession());
setupImpl.setIsMetadataExpired(false);
}
delegate = oldDelegate;
throw ex;
}
}
/**
* INTERNAL: Returns the SessionBroker that the Factory will be using and
* initializes it if it is not available. This method makes use of the
* partially constructed session stored in our setupImpl and completes its
* construction
* TODO: should throw IllegalStateException if not SessionBroker
*/
@Override
public SessionBroker getSessionBroker() {
return delegate.getSessionBroker();
}
/**
* INTERNAL: Returns the ServerSession that the Factory will be using and
* initializes it if it is not available. This method makes use of the
* partially constructed session stored in our setupImpl and completes its
* construction
*/
@Override
public ServerSession getServerSession() {
return delegate.getServerSession();
}
/**
* Closes this factory, releasing any resources that might be held by this
* factory. After invoking this method, all methods on the instance will
* throw an {@link IllegalStateException}, except for {@link #isOpen}, which
* will return false
.
*/
@Override
public synchronized void close() {
delegate.close();
}
/**
* Indicates whether or not this factory is open. Returns true
* until a call to {@link #close} is made.
*/
@Override
public boolean isOpen() {
return delegate.isOpen();
}
/**
* PUBLIC: Returns an EntityManager for this deployment.
*/
@Override
public EntityManager createEntityManager() {
return createEntityManagerImpl(null, null);
}
/**
* PUBLIC: Returns an EntityManager for this deployment.
*/
@Override
public EntityManager createEntityManager(Map properties) {
return createEntityManagerImpl(properties, null);
}
@Override
public EntityManager createEntityManager(SynchronizationType synchronizationType) {
return createEntityManagerImpl(null, synchronizationType);
}
@Override
public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) {
return createEntityManagerImpl(map, synchronizationType);
}
protected EntityManagerImpl createEntityManagerImpl(Map properties, SynchronizationType syncType) {
EntityManagerSetupImpl setupImpl = delegate.getSetupImpl();
if (setupImpl != null) {
if (setupImpl.isMetadataExpired()) {
String sessionName = setupImpl.getSessionName();
EntityManagerSetupImpl storedImpl = null;
synchronized (EntityManagerFactoryProvider.emSetupImpls){
storedImpl = EntityManagerFactoryProvider.emSetupImpls.get(sessionName);
}
if (storedImpl != null) {
delegate = new EntityManagerFactoryDelegate(storedImpl, delegate.getProperties(), this);
}
}
}
return delegate.createEntityManagerImpl(properties, syncType);
}
/**
* Gets the underlying implementation of the EntityManagerFactory.
* This method will return a version of EntityManagerFactory that is
* based on the available metadata at the time it is called. Future calls
* to refresh will not affect that metadata on this EntityManagerFactory.
*/
@Override
public EntityManagerFactoryDelegate unwrap(){
return delegate;
}
protected void verifyOpen() {
delegate.verifyOpen();
}
/**
* The method return user defined property passed in from
* EntityManagerFactory.
*/
public Object getProperty(String name) {
return delegate.getProperty(name);
}
/**
* Return default join existing transaction property, allows reading through
* write connection.
*/
public boolean getBeginEarlyTransaction() {
return delegate.getBeginEarlyTransaction();
}
/**
* Set default join existing transaction property, allows reading through
* write connection.
*/
public void setBeginEarlyTransaction(boolean beginEarlyTransaction) {
delegate.setBeginEarlyTransaction(beginEarlyTransaction);
}
/**
* Return default property, allows flush before query to be avoided.
*/
public FlushModeType getFlushMode() {
return delegate.getFlushMode();
}
/**
* Set default property, allows flush before query to be avoided.
*/
public void setFlushMode(FlushModeType flushMode) {
delegate.setFlushMode(flushMode);
}
/**
* Return default property, allows weak unit of work references.
*/
public ReferenceMode getReferenceMode() {
return delegate.getReferenceMode();
}
/**
* Set default property, allows weak unit of work references.
*/
public void setReferenceMode(ReferenceMode referenceMode) {
delegate.setReferenceMode(referenceMode);
}
/**
* Return default property to avoid resuming unit of work if going to be
* closed on commit anyway.
*/
public boolean getCloseOnCommit() {
return delegate.getCloseOnCommit();
}
/**
* Set default property to avoid resuming unit of work if going to be closed
* on commit anyway.
*/
public void setCloseOnCommit(boolean closeOnCommit) {
delegate.setCloseOnCommit(closeOnCommit);
}
/**
* Return default property to avoid discover new objects in unit of work if
* application always uses persist.
*/
public boolean getPersistOnCommit() {
return delegate.getPersistOnCommit();
}
/**
* Return interface providing access to utility methods for the persistence
* unit.
*
* @return PersistenceUnitUtil interface
* @throws IllegalStateException
* if the entity manager factory has been closed.
*/
@Override
public PersistenceUnitUtil getPersistenceUnitUtil() {
return delegate.getPersistenceUnitUtil();
}
/**
* Set default property to avoid discover new objects in unit of work if
* application always uses persist.
*/
public void setPersistOnCommit(boolean persistOnCommit) {
delegate.setPersistOnCommit(persistOnCommit);
}
/**
* Return default property to avoid discover new objects in unit of work if
* application always uses persist.
*/
public boolean getCommitWithoutPersistRules() {
return delegate.getCommitWithoutPersistRules();
}
/**
* Set default property to avoid discover new objects in unit of work if
* application always uses persist.
*/
public void setCommitWithoutPersistRules(boolean commitWithoutPersistRules) {
delegate.setCommitWithoutPersistRules(commitWithoutPersistRules);
}
/**
* Return the default FlashClearCache mode to be used. Relevant only in case
* call to flush method followed by call to clear method.
*
* @see org.eclipse.persistence.config.FlushClearCache
*/
public String getFlushClearCache() {
return delegate.getFlushClearCache();
}
/**
* Set the default FlashClearCache mode to be used. Relevant only in case
* call to flush method followed by call to clear method.
*
* @see org.eclipse.persistence.config.FlushClearCache
*/
public void setFlushClearCache(String flushClearCache) {
delegate.setFlushClearCache(flushClearCache);
}
/**
* Return the default to determine if does-exist should be performed on
* persist.
*/
public boolean shouldValidateExistence() {
return delegate.shouldValidateExistence();
}
/**
* Set the default to determine if does-exist should be performed on
* persist.
*/
public void setShouldValidateExistence(boolean shouldValidateExistence) {
delegate.setShouldValidateExistence(shouldValidateExistence);
}
/**
* Access the cache that is associated with the entity manager
* factory (the "second level cache").
* @return instance of the Cache
interface
* @throws IllegalStateException if the entity manager factory has been closed
* @see jakarta.persistence.EntityManagerFactory#getCache()
* @since Java Persistence 2.0
*/
@Override
public Cache getCache() {
return delegate.getCache();
}
/**
* @see jakarta.persistence.EntityManagerFactory#getProperties()
* @since Java Persistence API 2.0
*/
@Override
public Map getProperties() {
return delegate.getProperties();
}
@Override
public DatabaseSessionImpl getDatabaseSession() {
return delegate.getDatabaseSession();
}
/**
* @see jakarta.persistence.EntityManagerFactory#getCriteriaBuilder()
* @since Java Persistence 2.0
*/
@Override
public CriteriaBuilder getCriteriaBuilder() {
return delegate.getCriteriaBuilder();
}
/**
* Return an instance of Metamodel interface for access to the metamodel of
* the persistence unit.
*
* @return Metamodel instance
* @throws IllegalStateException
* if the entity manager factory has been closed.
* @see jakarta.persistence.EntityManagerFactory#getMetamodel()
* @since Java Persistence 2.0
*/
@Override
public Metamodel getMetamodel() {
return delegate.getMetamodel();
}
/**
* INTERNAL: Convenience function to allow us to reset the Metamodel in the
* possible case that we want to regenerate it. This function is outside of
* the JPA 2.0 specification.
*
* @since Java Persistence 2.0
*/
public void setMetamodel(Metamodel aMetamodel) {
delegate.setMetamodel(aMetamodel);
}
/**
* Determine the load state of a given persistent attribute of an entity
* belonging to the persistence unit.
*
* @param entity
* containing the attribute
* @param attributeName
* name of attribute whose load state is to be determined
* @return false if entity's state has not been loaded or if the attribute
* state has not been loaded, otherwise true
*/
@Override
public boolean isLoaded(Object entity, String attributeName) {
return delegate.isLoaded(entity, attributeName);
}
/**
* Determine the load state of an entity belonging to the persistence unit.
* This method can be used to determine the load state of an entity passed
* as a reference. An entity is considered loaded if all attributes for
* which FetchType EAGER has been specified have been loaded. The
* isLoaded(Object, String) method should be used to determine the load
* state of an attribute. Not doing so might lead to unintended loading of
* state.
*
* @param entity
* whose load state is to be determined
* @return false if the entity has not been loaded, else true.
*/
@Override
public boolean isLoaded(Object entity) {
return delegate.isLoaded(entity);
}
/**
* Returns the id of the entity. A generated id is not guaranteed to be
* available until after the database insert has occurred. Returns null if
* the entity does not yet have an id
*
* @return id of the entity
* @throws IllegalStateException
* if the entity is found not to be an entity.
*/
@Override
public Object getIdentifier(Object entity) {
return delegate.getIdentifier(entity);
}
/**
* Return if updates should be ordered by primary key to avoid possible database deadlocks.
*/
public CommitOrderType getCommitOrder() {
return delegate.getCommitOrder();
}
/**
* Set updates should be ordered by primary key to avoid possible database deadlocks.
*/
public void setCommitOrder(CommitOrderType commitOrder) {
delegate.setCommitOrder(commitOrder);
}
@Override
public void addNamedQuery(String name, Query query) {
QueryImpl queryImpl = query.unwrap(QueryImpl.class);
DatabaseQuery unwrapped = (DatabaseQuery) queryImpl.getDatabaseQueryInternal().clone();
if (queryImpl.lockMode != null){
((ObjectLevelReadQuery)unwrapped).setLockModeType(queryImpl.lockMode.name(), getServerSession());
}
if (unwrapped.isReadQuery()){
((ReadQuery)unwrapped).setInternalMax((queryImpl.getMaxResultsInternal()));
((ReadQuery)unwrapped).setFirstResult((queryImpl.getFirstResult()));
}
this.getServerSession().addQuery(name, unwrapped, true);
}
@Override
public T unwrap(Class cls) {
if (cls.equals(JpaEntityManagerFactory.class) || cls.equals(EntityManagerFactoryImpl.class)) {
return (T) this;
}else if (cls.equals(EntityManagerFactoryDelegate.class)) {
return (T) this.delegate;
}else if (cls.equals(Session.class) || cls.equals(AbstractSession.class)) {
return (T) this.delegate.getAbstractSession();
} else if (cls.equals(DatabaseSession.class) || cls.equals(DatabaseSessionImpl.class)) {
return (T) this.getDatabaseSession();
} else if (cls.equals(Server.class) || cls.equals(ServerSession.class)) {
return (T) this.getServerSession();
} else if (cls.equals(SessionBroker.class)) {
return (T) this.getSessionBroker();
}
throw new PersistenceException(ExceptionLocalization.buildMessage("unable_to_unwrap_jpa", new String[]{EntityManagerFactory.class.getName(),cls.getName()}));
}
@Override
public void addNamedEntityGraph(String graphName, EntityGraph entityGraph) {
AttributeGroup group = ((EntityGraphImpl)entityGraph).getAttributeGroup().clone();
group.setName(graphName);
this.getServerSession().getAttributeGroups().put(graphName, group);
this.getServerSession().getDescriptor(((EntityGraphImpl)entityGraph).getClassType()).addAttributeGroup(group);
}
}