package org.hibernate.jpa.internal;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.Cache;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedSubgraph;
import javax.persistence.PersistenceContextType;
import javax.persistence.PersistenceException;
import javax.persistence.PersistenceUnitUtil;
import javax.persistence.Query;
import javax.persistence.SynchronizationType;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.spi.LoadState;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.hibernate.Hibernate;
import org.hibernate.SessionFactory;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.annotations.NamedEntityGraphDefinition;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.engine.spi.NamedQueryDefinitionBuilder;
import org.hibernate.engine.spi.NamedSQLQueryDefinition;
import org.hibernate.engine.spi.NamedSQLQueryDefinitionBuilder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.UUIDGenerator;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.HibernateQuery;
import org.hibernate.jpa.boot.internal.SettingsImpl;
import org.hibernate.jpa.criteria.CriteriaBuilderImpl;
import org.hibernate.jpa.graph.internal.AbstractGraphNode;
import org.hibernate.jpa.graph.internal.AttributeNodeImpl;
import org.hibernate.jpa.graph.internal.EntityGraphImpl;
import org.hibernate.jpa.graph.internal.SubgraphImpl;
import org.hibernate.jpa.internal.metamodel.EntityTypeImpl;
import org.hibernate.jpa.internal.metamodel.MetamodelImpl;
import org.hibernate.jpa.internal.util.PersistenceUtilHelper;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.service.ServiceRegistry;
import org.jboss.logging.Logger;
public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
private static final long serialVersionUID = 5423543L;
private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator();
private static final Logger log = Logger.getLogger( EntityManagerFactoryImpl.class );
private final transient SessionFactoryImpl sessionFactory;
private final transient PersistenceUnitTransactionType transactionType;
private final transient boolean discardOnClose;
private final transient Class sessionInterceptorClass;
private final transient CriteriaBuilderImpl criteriaBuilder;
private final transient MetamodelImpl metamodel;
private final transient HibernatePersistenceUnitUtil util;
private final transient Map<String,Object> properties;
private final String entityManagerFactoryName;
private final transient PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache();
private final transient Map<String,EntityGraphImpl> entityGraphs = new ConcurrentHashMap<String, EntityGraphImpl>();
@SuppressWarnings( "unchecked" )
public EntityManagerFactoryImpl(
PersistenceUnitTransactionType transactionType,
boolean discardOnClose,
Class sessionInterceptorClass,
Configuration cfg,
ServiceRegistry serviceRegistry,
String persistenceUnitName) {
this(
persistenceUnitName,
(SessionFactoryImplementor) cfg.buildSessionFactory( serviceRegistry ),
new SettingsImpl().setReleaseResourcesOnCloseEnabled( discardOnClose ).setSessionInterceptorClass( sessionInterceptorClass ).setTransactionType( transactionType ),
cfg.getProperties(),
cfg
);
}
public EntityManagerFactoryImpl(
String persistenceUnitName,
SessionFactoryImplementor sessionFactory,
SettingsImpl settings,
Map<?, ?> configurationValues,
Configuration cfg) {
this.sessionFactory = (SessionFactoryImpl) sessionFactory;
this.transactionType = settings.getTransactionType();
this.discardOnClose = settings.isReleaseResourcesOnCloseEnabled();
this.sessionInterceptorClass = settings.getSessionInterceptorClass();
final JpaMetaModelPopulationSetting jpaMetaModelPopulationSetting = determineJpaMetaModelPopulationSetting( cfg );
if ( JpaMetaModelPopulationSetting.DISABLED == jpaMetaModelPopulationSetting ) {
this.metamodel = null;
}
else {
this.metamodel = MetamodelImpl.buildMetamodel(
cfg.getClassMappings(),
cfg.getMappedSuperclassMappingsCopy(),
sessionFactory,
JpaMetaModelPopulationSetting.IGNORE_UNSUPPORTED == jpaMetaModelPopulationSetting
);
}
this.criteriaBuilder = new CriteriaBuilderImpl( this );
this.util = new HibernatePersistenceUnitUtil( this );
HashMap<String,Object> props = new HashMap<String, Object>();
addAll( props, sessionFactory.getProperties() );
addAll( props, cfg.getProperties() );
addAll( props, configurationValues );
maskOutSensitiveInformation( props );
this.properties = Collections.unmodifiableMap( props );
String entityManagerFactoryName = (String)this.properties.get( AvailableSettings.ENTITY_MANAGER_FACTORY_NAME);
if (entityManagerFactoryName == null) {
entityManagerFactoryName = persistenceUnitName;
}
if (entityManagerFactoryName == null) {
entityManagerFactoryName = (String) UUID_GENERATOR.generate(null, null);
}
this.entityManagerFactoryName = entityManagerFactoryName;
applyNamedEntityGraphs( cfg.getNamedEntityGraphs() );
EntityManagerFactoryRegistry.INSTANCE.addEntityManagerFactory( entityManagerFactoryName, this );
}
private enum JpaMetaModelPopulationSetting {
ENABLED,
DISABLED,
IGNORE_UNSUPPORTED;
private static JpaMetaModelPopulationSetting parse(String setting) {
if ( "enabled".equalsIgnoreCase( setting ) ) {
return ENABLED;
}
else if ( "disabled".equalsIgnoreCase( setting ) ) {
return DISABLED;
}
else {
return IGNORE_UNSUPPORTED;
}
}
}
protected JpaMetaModelPopulationSetting determineJpaMetaModelPopulationSetting(Configuration cfg) {
String setting = ConfigurationHelper.getString(
AvailableSettings.JPA_METAMODEL_POPULATION,
cfg.getProperties(),
null
);
if ( setting == null ) {
setting = ConfigurationHelper.getString( AvailableSettings.JPA_METAMODEL_GENERATION, cfg.getProperties(), null );
if ( setting != null ) {
log.infof(
"Encountered deprecated setting [%s], use [%s] instead",
AvailableSettings.JPA_METAMODEL_GENERATION,
AvailableSettings.JPA_METAMODEL_POPULATION
);
}
}
return JpaMetaModelPopulationSetting.parse( setting );
}
private static void addAll(HashMap<String, Object> destination, Map<?,?> source) {
for ( Map.Entry entry : source.entrySet() ) {
if ( String.class.isInstance( entry.getKey() ) ) {
destination.put( (String) entry.getKey(), entry.getValue() );
}
}
}
private void maskOutSensitiveInformation(HashMap<String, Object> props) {
maskOutIfSet( props, AvailableSettings.JDBC_PASSWORD );
maskOutIfSet( props, org.hibernate.cfg.AvailableSettings.PASS );
}
private void maskOutIfSet(HashMap<String, Object> props, String setting) {
if ( props.containsKey( setting ) ) {
props.put( setting, "****" );
}
}
@SuppressWarnings("unchecked")
private void applyNamedEntityGraphs(Collection<NamedEntityGraphDefinition> namedEntityGraphs) {
for ( NamedEntityGraphDefinition definition : namedEntityGraphs ) {
log.debugf(
"Applying named entity graph [name=%s, entity-name=%s, jpa-entity-name=%s",
definition.getRegisteredName(),
definition.getEntityName(),
definition.getJpaEntityName()
);
final EntityType entityType = metamodel.getEntityTypeByName( definition.getEntityName() );
if ( entityType == null ) {
throw new IllegalArgumentException(
"Attempted to register named entity graph [" + definition.getRegisteredName()
+ "] for unknown entity ["+ definition.getEntityName() + "]"
);
}
final EntityGraphImpl entityGraph = new EntityGraphImpl(
definition.getRegisteredName(),
entityType,
this
);
final NamedEntityGraph namedEntityGraph = definition.getAnnotation();
if ( namedEntityGraph.includeAllAttributes() ) {
for ( Object attributeObject : entityType.getAttributes() ) {
entityGraph.addAttributeNodes( (Attribute) attributeObject );
}
}
if ( namedEntityGraph.attributeNodes() != null ) {
applyNamedAttributeNodes( namedEntityGraph.attributeNodes(), namedEntityGraph, entityGraph );
}
entityGraphs.put( definition.getRegisteredName(), entityGraph );
}
}
private void applyNamedAttributeNodes(
NamedAttributeNode[] namedAttributeNodes,
NamedEntityGraph namedEntityGraph,
AbstractGraphNode graphNode) {
for ( NamedAttributeNode namedAttributeNode : namedAttributeNodes ) {
final String value = namedAttributeNode.value();
AttributeNodeImpl attributeNode = graphNode.addAttribute( value );
if ( StringHelper.isNotEmpty( namedAttributeNode.subgraph() ) ) {
final SubgraphImpl subgraph = attributeNode.makeSubgraph();
applyNamedSubgraphs(
namedEntityGraph,
namedAttributeNode.subgraph(),
subgraph
);
}
if ( StringHelper.isNotEmpty( namedAttributeNode.keySubgraph() ) ) {
final SubgraphImpl subgraph = attributeNode.makeKeySubgraph();
applyNamedSubgraphs(
namedEntityGraph,
namedAttributeNode.keySubgraph(),
subgraph
);
}
}
}
private void applyNamedSubgraphs(NamedEntityGraph namedEntityGraph, String subgraphName, SubgraphImpl subgraph) {
for ( NamedSubgraph namedSubgraph : namedEntityGraph.subgraphs() ) {
if ( subgraphName.equals( namedSubgraph.name() ) ) {
applyNamedAttributeNodes(
namedSubgraph.attributeNodes(),
namedEntityGraph,
subgraph
);
}
}
}
public EntityManager createEntityManager() {
return internalCreateEntityManager( SynchronizationType.SYNCHRONIZED, Collections.EMPTY_MAP );
}
@Override
public EntityManager createEntityManager(SynchronizationType synchronizationType) {
errorIfResourceLocalDueToExplicitSynchronizationType();
return internalCreateEntityManager( synchronizationType, Collections.EMPTY_MAP );
}
private void errorIfResourceLocalDueToExplicitSynchronizationType() {
if ( transactionType == PersistenceUnitTransactionType.RESOURCE_LOCAL ) {
throw new IllegalStateException(
"Illegal attempt to specify a SynchronizationType when building an EntityManager from a " +
"EntityManagerFactory defined as RESOURCE_LOCAL "
);
}
}
public EntityManager createEntityManager(Map map) {
return internalCreateEntityManager( SynchronizationType.SYNCHRONIZED, map );
}
@Override
public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) {
errorIfResourceLocalDueToExplicitSynchronizationType();
return internalCreateEntityManager( synchronizationType, map );
}
private EntityManager internalCreateEntityManager(SynchronizationType synchronizationType, Map map) {
validateNotClosed();
return new EntityManagerImpl(
this,
PersistenceContextType.EXTENDED,
synchronizationType,
transactionType,
discardOnClose,
sessionInterceptorClass,
map
);
}
public CriteriaBuilder getCriteriaBuilder() {
validateNotClosed();
return criteriaBuilder;
}
public Metamodel getMetamodel() {
validateNotClosed();
return metamodel;
}
public void close() {
validateNotClosed();
sessionFactory.close();
EntityManagerFactoryRegistry.INSTANCE.removeEntityManagerFactory(entityManagerFactoryName, this);
}
public Map<String, Object> getProperties() {
validateNotClosed();
return properties;
}
public Cache getCache() {
validateNotClosed();
return new JPACache( sessionFactory );
}
protected void validateNotClosed() {
if ( ! isOpen() ) {
throw new IllegalStateException( "EntityManagerFactory is closed" );
}
}
public PersistenceUnitUtil getPersistenceUnitUtil() {
validateNotClosed();
return util;
}
@Override
public void addNamedQuery(String name, Query query) {
validateNotClosed();
try {
final StoredProcedureQueryImpl unwrapped = query.unwrap( StoredProcedureQueryImpl.class );
if ( unwrapped != null ) {
addNamedStoredProcedureQuery( name, unwrapped );
return;
}
}
catch ( PersistenceException ignore ) {
}
try {
final HibernateQuery unwrapped = query.unwrap( HibernateQuery.class );
if ( unwrapped != null ) {
final org.hibernate.Query hibernateQuery = unwrapped.getHibernateQuery();
if ( org.hibernate.SQLQuery.class.isInstance( hibernateQuery ) ) {
sessionFactory.registerNamedSQLQueryDefinition(
name,
extractSqlQueryDefinition( (org.hibernate.SQLQuery) hibernateQuery, name )
);
}
else {
sessionFactory.registerNamedQueryDefinition( name, extractHqlQueryDefinition( hibernateQuery, name ) );
}
return;
}
}
catch ( PersistenceException ignore ) {
}
throw new PersistenceException(
String.format(
"Unsure how to how to properly unwrap given Query [%s] as basis for named query",
query
)
);
}
private void addNamedStoredProcedureQuery(String name, StoredProcedureQueryImpl query) {
final ProcedureCall procedureCall = query.getHibernateProcedureCall();
sessionFactory.getNamedQueryRepository().registerNamedProcedureCallMemento(
name,
procedureCall.extractMemento( query.getHints() )
);
}
private NamedSQLQueryDefinition (org.hibernate.SQLQuery nativeSqlQuery, String name) {
final NamedSQLQueryDefinitionBuilder builder = new NamedSQLQueryDefinitionBuilder( name );
fillInNamedQueryBuilder( builder, nativeSqlQuery );
builder.setCallable( nativeSqlQuery.isCallable() )
.setQuerySpaces( nativeSqlQuery.getSynchronizedQuerySpaces() )
.setQueryReturns( nativeSqlQuery.getQueryReturns() );
return builder.createNamedQueryDefinition();
}
private NamedQueryDefinition (org.hibernate.Query hqlQuery, String name) {
final NamedQueryDefinitionBuilder builder = new NamedQueryDefinitionBuilder( name );
fillInNamedQueryBuilder( builder, hqlQuery );
builder.setLockOptions( hqlQuery.getLockOptions().makeCopy() );
return builder.createNamedQueryDefinition();
}
private void fillInNamedQueryBuilder(NamedQueryDefinitionBuilder builder, org.hibernate.Query query) {
builder.setQuery( query.getQueryString() )
.setComment( query.getComment() )
.setCacheable( query.isCacheable() )
.setCacheRegion( query.getCacheRegion() )
.setCacheMode( query.getCacheMode() )
.setTimeout( query.getTimeout() )
.setFetchSize( query.getFetchSize() )
.setFirstResult( query.getFirstResult() )
.setMaxResults( query.getMaxResults() )
.setReadOnly( query.isReadOnly() )
.setFlushMode( query.getFlushMode() );
}
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> cls) {
if ( SessionFactory.class.isAssignableFrom( cls ) ) {
return ( T ) sessionFactory;
}
if ( SessionFactoryImplementor.class.isAssignableFrom( cls ) ) {
return ( T ) sessionFactory;
}
if ( EntityManager.class.isAssignableFrom( cls ) ) {
return ( T ) this;
}
throw new PersistenceException( "Hibernate cannot unwrap EntityManagerFactory as " + cls.getName() );
}
@Override
public <T> void addNamedEntityGraph(String graphName, EntityGraph<T> entityGraph) {
if ( ! EntityGraphImpl.class.isInstance( entityGraph ) ) {
throw new IllegalArgumentException(
"Unknown type of EntityGraph for making named : " + entityGraph.getClass().getName()
);
}
final EntityGraphImpl<T> copy = ( (EntityGraphImpl<T>) entityGraph ).makeImmutableCopy( graphName );
final EntityGraphImpl old = entityGraphs.put( graphName, copy );
if ( old != null ) {
log.debugf( "EntityGraph being replaced on EntityManagerFactory for name %s", graphName );
}
}
public EntityGraphImpl findEntityGraphByName(String name) {
return entityGraphs.get( name );
}
@SuppressWarnings("unchecked")
public <T> List<EntityGraph<? super T>> findEntityGraphsByType(Class<T> entityClass) {
final EntityType<T> entityType = getMetamodel().entity( entityClass );
if ( entityType == null ) {
throw new IllegalArgumentException( "Given class is not an entity : " + entityClass.getName() );
}
final List<EntityGraph<? super T>> results = new ArrayList<EntityGraph<? super T>>();
for ( EntityGraphImpl entityGraph : this.entityGraphs.values() ) {
if ( entityGraph.appliesTo( entityType ) ) {
results.add( entityGraph );
}
}
return results;
}
public boolean isOpen() {
return ! sessionFactory.isClosed();
}
public SessionFactoryImpl getSessionFactory() {
return sessionFactory;
}
@Override
public EntityTypeImpl getEntityTypeByName(String entityName) {
final EntityTypeImpl entityType = metamodel.getEntityTypeByName( entityName );
if ( entityType == null ) {
throw new IllegalArgumentException( "[" + entityName + "] did not refer to EntityType" );
}
return entityType;
}
public String getEntityManagerFactoryName() {
return entityManagerFactoryName;
}
private static class JPACache implements Cache {
private SessionFactoryImplementor sessionFactory;
private JPACache(SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
}
public boolean contains(Class entityClass, Object identifier) {
return sessionFactory.getCache().containsEntity( entityClass, ( Serializable ) identifier );
}
public void evict(Class entityClass, Object identifier) {
sessionFactory.getCache().evictEntity( entityClass, ( Serializable ) identifier );
}
public void evict(Class entityClass) {
sessionFactory.getCache().evictEntityRegion( entityClass );
}
public void evictAll() {
sessionFactory.getCache().evictEntityRegions();
}
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> cls) {
if ( RegionFactory.class.isAssignableFrom( cls ) ) {
return (T) sessionFactory.getSettings().getRegionFactory();
}
if ( org.hibernate.Cache.class.isAssignableFrom( cls ) ) {
return (T) sessionFactory.getCache();
}
throw new PersistenceException( "Hibernate cannot unwrap Cache as " + cls.getName() );
}
}
private static EntityManagerFactory getNamedEntityManagerFactory(String entityManagerFactoryName) throws InvalidObjectException {
Object result = EntityManagerFactoryRegistry.INSTANCE.getNamedEntityManagerFactory(entityManagerFactoryName);
if ( result == null ) {
throw new InvalidObjectException( "could not resolve entity manager factory during entity manager deserialization [name=" + entityManagerFactoryName + "]" );
}
return (EntityManagerFactory)result;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
if (entityManagerFactoryName == null) {
throw new InvalidObjectException( "could not serialize entity manager factory with null entityManagerFactoryName" );
}
oos.defaultWriteObject();
}
private Object readResolve() throws InvalidObjectException {
return getNamedEntityManagerFactory(entityManagerFactoryName);
}
private static class HibernatePersistenceUnitUtil implements PersistenceUnitUtil, Serializable {
private final HibernateEntityManagerFactory emf;
private transient PersistenceUtilHelper.MetadataCache cache;
private HibernatePersistenceUnitUtil(EntityManagerFactoryImpl emf) {
this.emf = emf;
this.cache = emf.cache;
}
public boolean isLoaded(Object entity, String attributeName) {
log.debug("PersistenceUnitUtil#isLoaded is not always accurate; consider using EntityManager#contains instead");
LoadState state = PersistenceUtilHelper.isLoadedWithoutReference( entity, attributeName, cache );
if (state == LoadState.LOADED) {
return true;
}
else if (state == LoadState.NOT_LOADED ) {
return false;
}
else {
return PersistenceUtilHelper.isLoadedWithReference( entity, attributeName, cache ) != LoadState.NOT_LOADED;
}
}
public boolean isLoaded(Object entity) {
log.debug("PersistenceUnitUtil#isLoaded is not always accurate; consider using EntityManager#contains instead");
return PersistenceUtilHelper.isLoaded( entity ) != LoadState.NOT_LOADED;
}
public Object getIdentifier(Object entity) {
final Class entityClass = Hibernate.getClass( entity );
final ClassMetadata classMetadata = emf.getSessionFactory().getClassMetadata( entityClass );
if (classMetadata == null) {
throw new IllegalArgumentException( entityClass + " is not an entity" );
}
return classMetadata.getIdentifier( entity );
}
}
}