- HHH-14409 : Internal format of natural-id values
- org.hibernate.loader.access - `ModelPart.breakDownJdbcValues` - build/keep natural-id loaders per entity (inheritance) - changes to NotImplementedYetExtension - tests
This commit is contained in:
parent
7e34535cfe
commit
9849ea5a1f
|
@ -853,8 +853,8 @@ public interface Session extends SharedSessionContract, EntityManager, AutoClose
|
|||
*
|
||||
* @return load delegate for loading the specified entity type by natural id
|
||||
*
|
||||
* @throws HibernateException If the specified entityClass cannot be resolved as a mapped entity, or if the
|
||||
* entity does not define a natural-id or if its natural-id is made up of multiple attributes.
|
||||
* @throws HibernateException If the specified entityClass cannot be resolved as a mapped entity or if the
|
||||
* entity does not define a natural-id
|
||||
*/
|
||||
<T> SimpleNaturalIdLoadAccess<T> bySimpleNaturalId(String entityName);
|
||||
|
||||
|
@ -866,8 +866,8 @@ public interface Session extends SharedSessionContract, EntityManager, AutoClose
|
|||
*
|
||||
* @return load delegate for loading the specified entity type by natural id
|
||||
*
|
||||
* @throws HibernateException If the specified entityClass cannot be resolved as a mapped entity, or if the
|
||||
* entity does not define a natural-id or if its natural-id is made up of multiple attributes.
|
||||
* @throws HibernateException If the specified entityClass cannot be resolved as a mapped entity or if the
|
||||
* entity does not define a natural-id
|
||||
*/
|
||||
<T> SimpleNaturalIdLoadAccess<T> bySimpleNaturalId(Class<T> entityClass);
|
||||
|
||||
|
|
|
@ -43,13 +43,14 @@ public class EntityUpdateAction extends EntityAction {
|
|||
private final int[] dirtyFields;
|
||||
private final boolean hasDirtyCollection;
|
||||
private final Object rowId;
|
||||
private Object nextVersion;
|
||||
private Object cacheEntry;
|
||||
private SoftLock lock;
|
||||
|
||||
private final NaturalIdMapping naturalIdMapping;
|
||||
private final Object previousNaturalIdValues;
|
||||
|
||||
private Object nextVersion;
|
||||
private Object cacheEntry;
|
||||
private SoftLock lock;
|
||||
|
||||
/**
|
||||
* Constructs an EntityUpdateAction
|
||||
* @param id The entity identifier
|
||||
|
@ -90,9 +91,11 @@ public class EntityUpdateAction extends EntityAction {
|
|||
previousNaturalIdValues = null;
|
||||
}
|
||||
else {
|
||||
this.previousNaturalIdValues = determinePreviousNaturalIdValues( persister, id, previousState, session );
|
||||
this.previousNaturalIdValues = determinePreviousNaturalIdValues( persister, naturalIdMapping, id, previousState, session );
|
||||
session.getPersistenceContextInternal().getNaturalIdHelper().manageLocalResolution(
|
||||
id, state, persister,
|
||||
id,
|
||||
naturalIdMapping.extractNaturalIdValues( state, session ),
|
||||
persister,
|
||||
CachedNaturalIdValueSource.UPDATE
|
||||
);
|
||||
}
|
||||
|
@ -100,11 +103,13 @@ public class EntityUpdateAction extends EntityAction {
|
|||
|
||||
private static Object determinePreviousNaturalIdValues(
|
||||
EntityPersister persister,
|
||||
Object id, Object[] previousState,
|
||||
NaturalIdMapping naturalIdMapping,
|
||||
Object id,
|
||||
Object[] previousState,
|
||||
SharedSessionContractImplementor session) {
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
if ( previousState != null ) {
|
||||
return persistenceContext.getNaturalIdHelper().extractNaturalIdValues( previousState, persister );
|
||||
return naturalIdMapping.extractNaturalIdValues( previousState, session );
|
||||
}
|
||||
|
||||
return persistenceContext.getNaturalIdSnapshot( id, persister );
|
||||
|
@ -222,7 +227,7 @@ public class EntityUpdateAction extends EntityAction {
|
|||
session.getPersistenceContextInternal().getNaturalIdHelper().manageSharedResolution(
|
||||
id,
|
||||
naturalIdMapping.extractNaturalIdValues( state, session ),
|
||||
naturalIdMapping.extractNaturalIdValues( previousState, session ),
|
||||
previousNaturalIdValues,
|
||||
persister,
|
||||
CachedNaturalIdValueSource.UPDATE
|
||||
);
|
||||
|
|
|
@ -14,11 +14,13 @@ import java.util.Set;
|
|||
|
||||
import org.hibernate.EntityMode;
|
||||
import org.hibernate.boot.MappingException;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmCompositeIdType;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmEntityDiscriminatorType;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmGeneratorSpecificationType;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmMultiTenancyType;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmPolymorphismEnum;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmRootEntityType;
|
||||
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmSimpleIdType;
|
||||
import org.hibernate.boot.model.Caching;
|
||||
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
|
||||
import org.hibernate.boot.model.naming.EntityNaming;
|
||||
|
@ -73,11 +75,13 @@ public class EntityHierarchySourceImpl implements EntityHierarchySource {
|
|||
}
|
||||
|
||||
private static IdentifierSource interpretIdentifierSource(RootEntitySourceImpl rootEntitySource) {
|
||||
if ( rootEntitySource.jaxbEntityMapping().getId() == null
|
||||
&& rootEntitySource.jaxbEntityMapping().getCompositeId() == null ) {
|
||||
final JaxbHbmSimpleIdType simpleId = rootEntitySource.jaxbEntityMapping().getId();
|
||||
final JaxbHbmCompositeIdType compositeId = rootEntitySource.jaxbEntityMapping().getCompositeId();
|
||||
|
||||
if ( simpleId == null && compositeId == null ) {
|
||||
throw new MappingException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
Locale.ROOT,
|
||||
"Entity [%s] did not define an identifier",
|
||||
rootEntitySource.getEntityNamingSource().getEntityName()
|
||||
),
|
||||
|
@ -85,15 +89,15 @@ public class EntityHierarchySourceImpl implements EntityHierarchySource {
|
|||
);
|
||||
}
|
||||
|
||||
if ( rootEntitySource.jaxbEntityMapping().getId() != null ) {
|
||||
if ( simpleId != null ) {
|
||||
return new IdentifierSourceSimpleImpl( rootEntitySource );
|
||||
}
|
||||
else {
|
||||
// if we get here, we should have a composite identifier. Just need
|
||||
// to determine if it is aggregated, or non-aggregated...
|
||||
|
||||
if ( rootEntitySource.jaxbEntityMapping().getCompositeId().isMapped() ) {
|
||||
if ( StringHelper.isEmpty( rootEntitySource.jaxbEntityMapping().getCompositeId().getClazz() ) ) {
|
||||
if ( compositeId.isMapped() ) {
|
||||
if ( StringHelper.isEmpty( compositeId.getClazz() ) ) {
|
||||
throw new MappingException(
|
||||
"mapped composite identifier must name component class to use.",
|
||||
rootEntitySource.origin()
|
||||
|
@ -101,23 +105,17 @@ public class EntityHierarchySourceImpl implements EntityHierarchySource {
|
|||
}
|
||||
}
|
||||
|
||||
if ( StringHelper.isEmpty( rootEntitySource.jaxbEntityMapping().getCompositeId().getClazz() ) ) {
|
||||
if ( StringHelper.isEmpty( rootEntitySource.jaxbEntityMapping().getCompositeId().getName() ) ) {
|
||||
if ( StringHelper.isEmpty( compositeId.getName() ) ) {
|
||||
if ( compositeId.isMapped() && StringHelper.isEmpty( compositeId.getClazz() ) ) {
|
||||
throw new MappingException(
|
||||
"dynamic composite-id must specify name",
|
||||
"mapped composite identifier must name component class to use.",
|
||||
rootEntitySource.origin()
|
||||
);
|
||||
}
|
||||
|
||||
// we have a non-aggregated id without an IdClass
|
||||
return new IdentifierSourceNonAggregatedCompositeImpl( rootEntitySource );
|
||||
}
|
||||
else if ( rootEntitySource.jaxbEntityMapping().getCompositeId().isMapped() ) {
|
||||
// we have a non-aggregated id with an IdClass
|
||||
return new IdentifierSourceNonAggregatedCompositeImpl( rootEntitySource );
|
||||
}
|
||||
else {
|
||||
if ( rootEntitySource.jaxbEntityMapping().getCompositeId().isMapped() ) {
|
||||
if ( compositeId.isMapped() ) {
|
||||
throw new MappingException(
|
||||
"cannot combine mapped=\"true\" with specified name",
|
||||
rootEntitySource.origin()
|
||||
|
|
|
@ -2004,8 +2004,11 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
}
|
||||
|
||||
persister = locateProperPersister( persister );
|
||||
final Object naturalIdValues = extractNaturalIdValues( naturalId, persister );
|
||||
final Object previousNaturalIdValues = previousNaturalId == null ? null : extractNaturalIdValues( previousNaturalId, persister );
|
||||
// final Object naturalIdValues = extractNaturalIdValues( naturalId, persister );
|
||||
final Object naturalIdValues = naturalId;
|
||||
|
||||
// final Object previousNaturalIdValues = previousNaturalId == null ? null : extractNaturalIdValues( previousNaturalId, persister );
|
||||
final Object previousNaturalIdValues = previousNaturalId;
|
||||
|
||||
managedSharedResolutions( persister, id, naturalIdValues, previousNaturalIdValues, source );
|
||||
}
|
||||
|
@ -2182,7 +2185,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues) {
|
||||
public Object findCachedNaturalIdResolution(EntityPersister persister, Object naturalIdValues) {
|
||||
return getNaturalIdXrefDelegate().findResolution( locateProperPersister( persister ), naturalIdValues );
|
||||
}
|
||||
|
||||
|
|
|
@ -898,7 +898,7 @@ public interface PersistenceContext {
|
|||
* {@link PersistenceContext.NaturalIdHelper#INVALID_NATURAL_ID_REFERENCE},
|
||||
* or {@code null}.
|
||||
*/
|
||||
Object findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues);
|
||||
Object findCachedNaturalIdResolution(EntityPersister persister, Object naturalIdValues);
|
||||
|
||||
/**
|
||||
* Find all the locally cached primary key cross-reference entries for the given persister.
|
||||
|
|
|
@ -112,7 +112,7 @@ public class NaturalIdMultiLoadAccessStandard<T> implements NaturalIdMultiLoadAc
|
|||
}
|
||||
|
||||
try {
|
||||
return entityDescriptor.getNaturalIdMapping().getMultiNaturalIdLoader().multiLoad( ids, this, session );
|
||||
return (List<T>) entityDescriptor.getMultiNaturalIdLoader().multiLoad( ids, this, session );
|
||||
}
|
||||
finally {
|
||||
if ( graphSemantic != null ) {
|
||||
|
|
|
@ -19,12 +19,9 @@ import java.sql.NClob;
|
|||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import javax.persistence.CacheRetrieveMode;
|
||||
import javax.persistence.CacheStoreMode;
|
||||
import javax.persistence.EntityGraph;
|
||||
|
@ -112,10 +109,8 @@ import org.hibernate.event.spi.ResolveNaturalIdEventListener;
|
|||
import org.hibernate.event.spi.SaveOrUpdateEvent;
|
||||
import org.hibernate.event.spi.SaveOrUpdateEventListener;
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.graph.RootGraph;
|
||||
import org.hibernate.graph.internal.RootGraphImpl;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.jpa.AvailableSettings;
|
||||
import org.hibernate.jpa.QueryHints;
|
||||
import org.hibernate.jpa.internal.util.CacheModeHelper;
|
||||
|
@ -123,7 +118,10 @@ import org.hibernate.jpa.internal.util.ConfigurationHelper;
|
|||
import org.hibernate.jpa.internal.util.FlushModeTypeHelper;
|
||||
import org.hibernate.jpa.internal.util.LockModeTypeHelper;
|
||||
import org.hibernate.jpa.internal.util.LockOptionsHelper;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
import org.hibernate.loader.access.IdentifierLoadAccessImpl;
|
||||
import org.hibernate.loader.access.LoadAccessContext;
|
||||
import org.hibernate.loader.access.NaturalIdLoadAccessImpl;
|
||||
import org.hibernate.loader.access.SimpleNaturalIdLoadAccessImpl;
|
||||
import org.hibernate.metamodel.spi.MetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
@ -164,7 +162,7 @@ import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_STORE_MODE;
|
|||
*/
|
||||
public class SessionImpl
|
||||
extends AbstractSessionImpl
|
||||
implements SessionImplementor, EventSource {
|
||||
implements SessionImplementor, LoadAccessContext, EventSource {
|
||||
private static final EntityManagerMessageLogger log = HEMLogging.messageLogger( SessionImpl.class );
|
||||
|
||||
// Defaults to null which means the properties are the default - as defined in FastSessionServices#defaultSessionProperties
|
||||
|
@ -566,13 +564,23 @@ public class SessionImpl
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void delayedAfterCompletion() {
|
||||
public void delayedAfterCompletion() {
|
||||
if ( getTransactionCoordinator() instanceof JtaTransactionCoordinatorImpl ) {
|
||||
( (JtaTransactionCoordinatorImpl) getTransactionCoordinator() ).getSynchronizationCallbackCoordinator()
|
||||
.processAnyDelayedAfterCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pulseTransactionCoordinator() {
|
||||
super.pulseTransactionCoordinator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkOpenOrWaitingForAutoClose() {
|
||||
super.checkOpenOrWaitingForAutoClose();
|
||||
}
|
||||
|
||||
// saveOrUpdate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@Override
|
||||
|
@ -1111,12 +1119,12 @@ public class SessionImpl
|
|||
|
||||
@Override
|
||||
public <T> IdentifierLoadAccessImpl<T> byId(String entityName) {
|
||||
return new IdentifierLoadAccessImpl<>( entityName );
|
||||
return new IdentifierLoadAccessImpl<>( this, requireEntityPersister( entityName ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> IdentifierLoadAccessImpl<T> byId(Class<T> entityClass) {
|
||||
return new IdentifierLoadAccessImpl<>( entityClass );
|
||||
return new IdentifierLoadAccessImpl<>( this, requireEntityPersister( entityClass ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1131,22 +1139,22 @@ public class SessionImpl
|
|||
|
||||
@Override
|
||||
public <T> NaturalIdLoadAccess<T> byNaturalId(String entityName) {
|
||||
return new NaturalIdLoadAccessImpl<>( entityName );
|
||||
return new NaturalIdLoadAccessImpl<>( this, requireEntityPersister( entityName ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> NaturalIdLoadAccess<T> byNaturalId(Class<T> entityClass) {
|
||||
return new NaturalIdLoadAccessImpl<>( entityClass );
|
||||
return new NaturalIdLoadAccessImpl<>( this, requireEntityPersister( entityClass ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SimpleNaturalIdLoadAccess<T> bySimpleNaturalId(String entityName) {
|
||||
return new SimpleNaturalIdLoadAccessImpl<>( entityName );
|
||||
return new SimpleNaturalIdLoadAccessImpl<>( this, requireEntityPersister( entityName ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SimpleNaturalIdLoadAccess<T> bySimpleNaturalId(Class<T> entityClass) {
|
||||
return new SimpleNaturalIdLoadAccessImpl<>( entityClass );
|
||||
return new SimpleNaturalIdLoadAccessImpl<>( this, requireEntityPersister( entityClass ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1159,7 +1167,8 @@ public class SessionImpl
|
|||
return new NaturalIdMultiLoadAccessStandard<>( requireEntityPersister( entityName ), this );
|
||||
}
|
||||
|
||||
private void fireLoad(LoadEvent event, LoadType loadType) {
|
||||
@Override
|
||||
public void fireLoad(LoadEvent event, LoadType loadType) {
|
||||
checkOpenOrWaitingForAutoClose();
|
||||
fireLoadNoChecks( event, loadType );
|
||||
delayedAfterCompletion();
|
||||
|
@ -2111,147 +2120,6 @@ public class SessionImpl
|
|||
transactionCoordinator.removeObserver( transactionObserver );
|
||||
}
|
||||
|
||||
private class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
|
||||
private final EntityPersister entityPersister;
|
||||
|
||||
private LockOptions lockOptions;
|
||||
private CacheMode cacheMode;
|
||||
private RootGraphImplementor<T> rootGraph;
|
||||
private GraphSemantic graphSemantic;
|
||||
|
||||
private IdentifierLoadAccessImpl(EntityPersister entityPersister) {
|
||||
this.entityPersister = entityPersister;
|
||||
}
|
||||
|
||||
private IdentifierLoadAccessImpl(String entityName) {
|
||||
this( requireEntityPersister( entityName ) );
|
||||
}
|
||||
|
||||
private IdentifierLoadAccessImpl(Class<T> entityClass) {
|
||||
this( requireEntityPersister( entityClass ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IdentifierLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
this.lockOptions = lockOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierLoadAccess<T> with(CacheMode cacheMode) {
|
||||
this.cacheMode = cacheMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierLoadAccess<T> with(RootGraph<T> graph, GraphSemantic semantic) {
|
||||
this.rootGraph = (RootGraphImplementor<T>) graph;
|
||||
this.graphSemantic = semantic;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T getReference(Object id) {
|
||||
return perform( () -> doGetReference( id ) );
|
||||
}
|
||||
|
||||
protected T perform(Supplier<T> executor) {
|
||||
CacheMode sessionCacheMode = getCacheMode();
|
||||
boolean cacheModeChanged = false;
|
||||
if ( cacheMode != null ) {
|
||||
// naive check for now...
|
||||
// todo : account for "conceptually equal"
|
||||
if ( cacheMode != sessionCacheMode ) {
|
||||
setCacheMode( cacheMode );
|
||||
cacheModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if ( graphSemantic != null ) {
|
||||
if ( rootGraph == null ) {
|
||||
throw new IllegalArgumentException( "Graph semantic specified, but no RootGraph was supplied" );
|
||||
}
|
||||
loadQueryInfluencers.getEffectiveEntityGraph().applyGraph( rootGraph, graphSemantic );
|
||||
}
|
||||
|
||||
try {
|
||||
return executor.get();
|
||||
}
|
||||
finally {
|
||||
if ( graphSemantic != null ) {
|
||||
loadQueryInfluencers.getEffectiveEntityGraph().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ( cacheModeChanged ) {
|
||||
// change it back
|
||||
setCacheMode( sessionCacheMode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T doGetReference(Object id) {
|
||||
if ( this.lockOptions != null ) {
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, SessionImpl.this, getReadOnlyFromLoadQueryInfluencers() );
|
||||
fireLoad( event, LoadEventListener.LOAD );
|
||||
return (T) event.getResult();
|
||||
}
|
||||
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, SessionImpl.this, getReadOnlyFromLoadQueryInfluencers() );
|
||||
boolean success = false;
|
||||
try {
|
||||
fireLoad( event, LoadEventListener.LOAD );
|
||||
if ( event.getResult() == null ) {
|
||||
getFactory().getEntityNotFoundDelegate().handleEntityNotFound(
|
||||
entityPersister.getEntityName(),
|
||||
id
|
||||
);
|
||||
}
|
||||
success = true;
|
||||
return (T) event.getResult();
|
||||
}
|
||||
finally {
|
||||
afterOperation( success );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T load(Object id) {
|
||||
return perform( () -> doLoad( id ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> loadOptional(Object id) {
|
||||
return Optional.ofNullable( perform( () -> doLoad( id ) ) );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final T doLoad(Object id) {
|
||||
if ( this.lockOptions != null ) {
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, SessionImpl.this, getReadOnlyFromLoadQueryInfluencers() );
|
||||
fireLoad( event, LoadEventListener.GET );
|
||||
return (T) event.getResult();
|
||||
}
|
||||
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, SessionImpl.this, getReadOnlyFromLoadQueryInfluencers() );
|
||||
boolean success = false;
|
||||
try {
|
||||
fireLoad( event, LoadEventListener.GET );
|
||||
success = true;
|
||||
}
|
||||
catch (ObjectNotFoundException e) {
|
||||
// if session cache contains proxy for non-existing object
|
||||
}
|
||||
finally {
|
||||
afterOperation( success );
|
||||
}
|
||||
return (T) event.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
private EntityPersister requireEntityPersister(Class entityClass) {
|
||||
return getFactory().getMetamodel().locateEntityPersister( entityClass );
|
||||
}
|
||||
|
@ -2260,261 +2128,6 @@ public class SessionImpl
|
|||
return getFactory().getMetamodel().locateEntityPersister( entityName );
|
||||
}
|
||||
|
||||
private abstract class BaseNaturalIdLoadAccessImpl<T> {
|
||||
private final EntityPersister entityPersister;
|
||||
private LockOptions lockOptions;
|
||||
private boolean synchronizationEnabled = true;
|
||||
|
||||
private BaseNaturalIdLoadAccessImpl(EntityPersister entityPersister) {
|
||||
this.entityPersister = entityPersister;
|
||||
|
||||
if ( !entityPersister.hasNaturalIdentifier() ) {
|
||||
throw new HibernateException(
|
||||
String.format( "Entity [%s] did not define a natural id", entityPersister.getEntityName() )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public LockOptions getLockOptions() {
|
||||
return lockOptions;
|
||||
}
|
||||
|
||||
public boolean isSynchronizationEnabled() {
|
||||
return synchronizationEnabled;
|
||||
}
|
||||
|
||||
public BaseNaturalIdLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
this.lockOptions = lockOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void synchronizationEnabled(boolean synchronizationEnabled) {
|
||||
this.synchronizationEnabled = synchronizationEnabled;
|
||||
}
|
||||
|
||||
protected final Object resolveNaturalId(Map<String, Object> naturalIdParameters) {
|
||||
performAnyNeededCrossReferenceSynchronizations();
|
||||
|
||||
final Object resolvedId = entityPersister()
|
||||
.getNaturalIdMapping()
|
||||
.getNaturalIdLoader()
|
||||
.resolveNaturalIdToId( naturalIdParameters, SessionImpl.this );
|
||||
|
||||
return resolvedId == PersistenceContext.NaturalIdHelper.INVALID_NATURAL_ID_REFERENCE
|
||||
? null
|
||||
: resolvedId;
|
||||
}
|
||||
|
||||
protected void performAnyNeededCrossReferenceSynchronizations() {
|
||||
if ( !synchronizationEnabled ) {
|
||||
// synchronization (this process) was disabled
|
||||
return;
|
||||
}
|
||||
if ( entityPersister.getEntityMetamodel().hasImmutableNaturalId() ) {
|
||||
// only mutable natural-ids need this processing
|
||||
return;
|
||||
}
|
||||
if ( !isTransactionInProgress() ) {
|
||||
// not in a transaction so skip synchronization
|
||||
return;
|
||||
}
|
||||
|
||||
final PersistenceContext persistenceContext = getPersistenceContextInternal();
|
||||
final boolean debugEnabled = log.isDebugEnabled();
|
||||
for ( Object pk : persistenceContext.getNaturalIdHelper()
|
||||
.getCachedPkResolutions( entityPersister ) ) {
|
||||
final EntityKey entityKey = generateEntityKey( pk, entityPersister );
|
||||
final Object entity = persistenceContext.getEntity( entityKey );
|
||||
final EntityEntry entry = persistenceContext.getEntry( entity );
|
||||
|
||||
if ( entry == null ) {
|
||||
if ( debugEnabled ) {
|
||||
log.debug(
|
||||
"Cached natural-id/pk resolution linked to null EntityEntry in persistence context : "
|
||||
+ MessageHelper.infoString( entityPersister, pk, getFactory() )
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !entry.requiresDirtyCheck( entity ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// MANAGED is the only status we care about here...
|
||||
if ( entry.getStatus() != Status.MANAGED ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
persistenceContext.getNaturalIdHelper().handleSynchronization(
|
||||
entityPersister,
|
||||
pk,
|
||||
entity
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected final IdentifierLoadAccess getIdentifierLoadAccess() {
|
||||
final IdentifierLoadAccessImpl identifierLoadAccess = new IdentifierLoadAccessImpl( entityPersister );
|
||||
if ( this.lockOptions != null ) {
|
||||
identifierLoadAccess.with( lockOptions );
|
||||
}
|
||||
return identifierLoadAccess;
|
||||
}
|
||||
|
||||
protected EntityPersister entityPersister() {
|
||||
return entityPersister;
|
||||
}
|
||||
}
|
||||
|
||||
private class NaturalIdLoadAccessImpl<T> extends BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadAccess<T> {
|
||||
private final Map<String, Object> naturalIdParameters = new LinkedHashMap<>();
|
||||
|
||||
private NaturalIdLoadAccessImpl(EntityPersister entityPersister) {
|
||||
super( entityPersister );
|
||||
}
|
||||
|
||||
private NaturalIdLoadAccessImpl(String entityName) {
|
||||
this( requireEntityPersister( entityName ) );
|
||||
}
|
||||
|
||||
private NaturalIdLoadAccessImpl(Class entityClass) {
|
||||
this( requireEntityPersister( entityClass ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
return (NaturalIdLoadAccessImpl<T>) super.with( lockOptions );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccess<T> using(String attributeName, Object value) {
|
||||
naturalIdParameters.put( attributeName, value );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccess<T> using(Object... mappings) {
|
||||
CollectionHelper.collectMapEntries( naturalIdParameters::put, mappings );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccessImpl<T> setSynchronizationEnabled(boolean synchronizationEnabled) {
|
||||
super.synchronizationEnabled( synchronizationEnabled );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final T getReference() {
|
||||
final Object entityId = resolveNaturalId( this.naturalIdParameters );
|
||||
if ( entityId == null ) {
|
||||
return null;
|
||||
}
|
||||
return (T) this.getIdentifierLoadAccess().getReference( entityId );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final T load() {
|
||||
autoFlushIfRequired( (Set) CollectionHelper.setOf( entityPersister().getQuerySpaces() ) );
|
||||
final Object entityId = resolveNaturalId( this.naturalIdParameters );
|
||||
if ( entityId == null ) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (T) this.getIdentifierLoadAccess().load( entityId );
|
||||
}
|
||||
catch (EntityNotFoundException | ObjectNotFoundException enf) {
|
||||
// OK
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> loadOptional() {
|
||||
return Optional.ofNullable( load() );
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleNaturalIdLoadAccessImpl<T>
|
||||
extends BaseNaturalIdLoadAccessImpl<T>
|
||||
implements SimpleNaturalIdLoadAccess<T>, NaturalIdLoader.LoadOptions {
|
||||
private final String naturalIdAttributeName;
|
||||
|
||||
private SimpleNaturalIdLoadAccessImpl(EntityPersister entityPersister) {
|
||||
super( entityPersister );
|
||||
|
||||
if ( entityPersister.getNaturalIdentifierProperties().length != 1 ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"Entity [%s] did not define a simple natural id",
|
||||
entityPersister.getEntityName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final int naturalIdAttributePosition = entityPersister.getNaturalIdentifierProperties()[0];
|
||||
this.naturalIdAttributeName = entityPersister.getPropertyNames()[naturalIdAttributePosition];
|
||||
}
|
||||
|
||||
private SimpleNaturalIdLoadAccessImpl(String entityName) {
|
||||
this( requireEntityPersister( entityName ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
return super.getLockOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSynchronizationEnabled() {
|
||||
return super.isSynchronizationEnabled();
|
||||
}
|
||||
|
||||
private SimpleNaturalIdLoadAccessImpl(Class entityClass) {
|
||||
this( requireEntityPersister( entityClass ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SimpleNaturalIdLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
return (SimpleNaturalIdLoadAccessImpl<T>) super.with( lockOptions );
|
||||
}
|
||||
|
||||
private Map<String, Object> getNaturalIdParameters(Object naturalIdValue) {
|
||||
return Collections.singletonMap( naturalIdAttributeName, naturalIdValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleNaturalIdLoadAccessImpl<T> setSynchronizationEnabled(boolean synchronizationEnabled) {
|
||||
super.synchronizationEnabled( synchronizationEnabled );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T getReference(Object naturalIdValue) {
|
||||
final Object entityId = entityPersister().getNaturalIdLoader().resolveNaturalIdToId( naturalIdValue, SessionImpl.this );
|
||||
if ( entityId == null ) {
|
||||
return null;
|
||||
}
|
||||
return (T) this.getIdentifierLoadAccess().getReference( entityId );
|
||||
}
|
||||
|
||||
@Override
|
||||
public T load(Object naturalIdValue) {
|
||||
//noinspection unchecked
|
||||
autoFlushIfRequired( (Set) CollectionHelper.setOf( entityPersister().getQuerySpaces() ) );
|
||||
return (T) entityPersister().getNaturalIdLoader().load( naturalIdValue, this, SessionImpl.this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> loadOptional(Serializable naturalIdValue) {
|
||||
return Optional.ofNullable( load( naturalIdValue ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTransactionBoundary() {
|
||||
checkOpenOrWaitingForAutoClose();
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.loader;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Logging for loaders
|
||||
*/
|
||||
public interface LoaderLogging {
|
||||
String LOGGER_NAME = "org.hibernate.orm.loader";
|
||||
|
||||
Logger LOADER_LOGGER = Logger.getLogger( LOGGER_NAME );
|
||||
|
||||
boolean DEBUG_ENABLED = LOADER_LOGGER.isDebugEnabled();
|
||||
boolean TRACE_ENABLED = LOADER_LOGGER.isTraceEnabled();
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
package org.hibernate.loader.access;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.IdentifierLoadAccess;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
import org.hibernate.loader.LoaderLogging;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoadOptions;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import static org.hibernate.engine.spi.PersistenceContext.NaturalIdHelper.INVALID_NATURAL_ID_REFERENCE;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadOptions {
|
||||
private final LoadAccessContext context;
|
||||
private final EntityMappingType entityDescriptor;
|
||||
|
||||
private LockOptions lockOptions;
|
||||
private boolean synchronizationEnabled = true;
|
||||
|
||||
protected BaseNaturalIdLoadAccessImpl(LoadAccessContext context, EntityMappingType entityDescriptor) {
|
||||
this.context = context;
|
||||
this.entityDescriptor = entityDescriptor;
|
||||
|
||||
if ( entityDescriptor.getNaturalIdMapping() == null ) {
|
||||
throw new HibernateException(
|
||||
String.format( "Entity [%s] did not define a natural id", entityDescriptor.getEntityName() )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public LockOptions getLockOptions() {
|
||||
return lockOptions;
|
||||
}
|
||||
|
||||
public boolean isSynchronizationEnabled() {
|
||||
return synchronizationEnabled;
|
||||
}
|
||||
|
||||
public BaseNaturalIdLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
this.lockOptions = lockOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void synchronizationEnabled(boolean synchronizationEnabled) {
|
||||
this.synchronizationEnabled = synchronizationEnabled;
|
||||
}
|
||||
|
||||
protected final Object resolveNaturalId(Map<String, Object> naturalIdParameters) {
|
||||
performAnyNeededCrossReferenceSynchronizations();
|
||||
|
||||
final Object resolvedId = entityPersister()
|
||||
.getNaturalIdLoader()
|
||||
.resolveNaturalIdToId( naturalIdParameters, context.getSession() );
|
||||
|
||||
return resolvedId == INVALID_NATURAL_ID_REFERENCE
|
||||
? null
|
||||
: resolvedId;
|
||||
}
|
||||
|
||||
protected void performAnyNeededCrossReferenceSynchronizations() {
|
||||
if ( !synchronizationEnabled ) {
|
||||
// synchronization (this process) was disabled
|
||||
return;
|
||||
}
|
||||
|
||||
final NaturalIdMapping naturalIdMapping = entityDescriptor.getNaturalIdMapping();
|
||||
|
||||
if ( !naturalIdMapping.isMutable() ) {
|
||||
// only mutable natural-ids need this processing
|
||||
return;
|
||||
}
|
||||
|
||||
final SessionImplementor session = context.getSession();
|
||||
|
||||
if ( ! session.isTransactionInProgress() ) {
|
||||
// not in a transaction so skip synchronization
|
||||
return;
|
||||
}
|
||||
|
||||
final PersistenceContext persistenceContext = context.getSession().getPersistenceContextInternal();
|
||||
for ( Object pk : persistenceContext.getNaturalIdHelper().getCachedPkResolutions( entityPersister() ) ) {
|
||||
final EntityKey entityKey = context.getSession().generateEntityKey( pk, entityPersister() );
|
||||
final Object entity = persistenceContext.getEntity( entityKey );
|
||||
final EntityEntry entry = persistenceContext.getEntry( entity );
|
||||
|
||||
if ( entry == null ) {
|
||||
if ( LoaderLogging.DEBUG_ENABLED ) {
|
||||
LoaderLogging.LOADER_LOGGER.debugf(
|
||||
"Cached natural-id/pk resolution linked to null EntityEntry in persistence context : %s#%s",
|
||||
entityDescriptor.getEntityName(),
|
||||
pk
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !entry.requiresDirtyCheck( entity ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// MANAGED is the only status we care about here...
|
||||
if ( entry.getStatus() != Status.MANAGED ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
persistenceContext.getNaturalIdHelper().handleSynchronization(
|
||||
entityPersister(),
|
||||
pk,
|
||||
entity
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
protected final T doGetReference(Object normalizedNaturalIdValue) {
|
||||
performAnyNeededCrossReferenceSynchronizations();
|
||||
|
||||
context.checkOpenOrWaitingForAutoClose();
|
||||
context.pulseTransactionCoordinator();
|
||||
|
||||
final SessionImplementor session = context.getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
|
||||
final Object cachedResolution = persistenceContext.getNaturalIdHelper().findCachedNaturalIdResolution(
|
||||
entityPersister(),
|
||||
normalizedNaturalIdValue
|
||||
);
|
||||
|
||||
if ( cachedResolution == INVALID_NATURAL_ID_REFERENCE ) {
|
||||
// the entity is deleted, although not yet flushed - return null
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( cachedResolution != null ) {
|
||||
return (T) getIdentifierLoadAccess().getReference( cachedResolution );
|
||||
}
|
||||
|
||||
LoaderLogging.LOADER_LOGGER.debugf(
|
||||
"Selecting entity identifier by natural-id for `#getReference` handling - %s : %s",
|
||||
entityPersister().getEntityName(),
|
||||
normalizedNaturalIdValue
|
||||
);
|
||||
|
||||
final Object idFromDatabase = entityPersister().getNaturalIdLoader().resolveNaturalIdToId( normalizedNaturalIdValue, session );
|
||||
if ( idFromDatabase != null ) {
|
||||
return (T) getIdentifierLoadAccess().getReference( idFromDatabase );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected final T doLoad(Object normalizedNaturalIdValue) {
|
||||
performAnyNeededCrossReferenceSynchronizations();
|
||||
|
||||
context.checkOpenOrWaitingForAutoClose();
|
||||
context.pulseTransactionCoordinator();
|
||||
|
||||
final SessionImplementor session = context.getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
|
||||
final Object cachedResolution = persistenceContext.getNaturalIdHelper().findCachedNaturalIdResolution(
|
||||
entityPersister(),
|
||||
normalizedNaturalIdValue
|
||||
);
|
||||
|
||||
if ( cachedResolution == INVALID_NATURAL_ID_REFERENCE ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final T loaded;
|
||||
|
||||
if ( cachedResolution != null ) {
|
||||
loaded = (T) getIdentifierLoadAccess().load( cachedResolution );
|
||||
} else {
|
||||
loaded = (T) entityPersister().getNaturalIdLoader().load( normalizedNaturalIdValue, this, session );
|
||||
}
|
||||
|
||||
if ( loaded != null ) {
|
||||
final EntityEntry entry = persistenceContext.getEntry( loaded );
|
||||
assert entry != null;
|
||||
if ( entry.getStatus() == Status.DELETED ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
finally {
|
||||
context.delayedAfterCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
protected final IdentifierLoadAccess<?> getIdentifierLoadAccess() {
|
||||
final IdentifierLoadAccessImpl<?> identifierLoadAccess = new IdentifierLoadAccessImpl<>( context, entityPersister() );
|
||||
if ( this.lockOptions != null ) {
|
||||
identifierLoadAccess.with( lockOptions );
|
||||
}
|
||||
return identifierLoadAccess;
|
||||
}
|
||||
|
||||
protected LoadAccessContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public EntityMappingType getEntityDescriptor() {
|
||||
return entityDescriptor;
|
||||
}
|
||||
|
||||
protected EntityPersister entityPersister() {
|
||||
return entityDescriptor.getEntityPersister();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
package org.hibernate.loader.access;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.IdentifierLoadAccess;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.ObjectNotFoundException;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.LoadEvent;
|
||||
import org.hibernate.event.spi.LoadEventListener;
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.graph.RootGraph;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T> {
|
||||
private final LoadAccessContext context;
|
||||
private final EntityPersister entityPersister;
|
||||
|
||||
private LockOptions lockOptions;
|
||||
private CacheMode cacheMode;
|
||||
private RootGraphImplementor<T> rootGraph;
|
||||
private GraphSemantic graphSemantic;
|
||||
|
||||
public IdentifierLoadAccessImpl(LoadAccessContext context, EntityPersister entityPersister) {
|
||||
this.context = context;
|
||||
this.entityPersister = entityPersister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IdentifierLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
this.lockOptions = lockOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierLoadAccess<T> with(CacheMode cacheMode) {
|
||||
this.cacheMode = cacheMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierLoadAccess<T> with(RootGraph<T> graph, GraphSemantic semantic) {
|
||||
this.rootGraph = (RootGraphImplementor<T>) graph;
|
||||
this.graphSemantic = semantic;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T getReference(Object id) {
|
||||
return perform( () -> doGetReference( id ) );
|
||||
}
|
||||
|
||||
protected T perform(Supplier<T> executor) {
|
||||
final SessionImplementor session = context.getSession();
|
||||
|
||||
CacheMode sessionCacheMode = session.getCacheMode();
|
||||
boolean cacheModeChanged = false;
|
||||
if ( cacheMode != null ) {
|
||||
// naive check for now...
|
||||
// todo : account for "conceptually equal"
|
||||
if ( cacheMode != sessionCacheMode ) {
|
||||
session.setCacheMode( cacheMode );
|
||||
cacheModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if ( graphSemantic != null ) {
|
||||
if ( rootGraph == null ) {
|
||||
throw new IllegalArgumentException( "Graph semantic specified, but no RootGraph was supplied" );
|
||||
}
|
||||
session.getLoadQueryInfluencers().getEffectiveEntityGraph().applyGraph( rootGraph, graphSemantic );
|
||||
}
|
||||
|
||||
try {
|
||||
return executor.get();
|
||||
}
|
||||
finally {
|
||||
if ( graphSemantic != null ) {
|
||||
session.getLoadQueryInfluencers().getEffectiveEntityGraph().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ( cacheModeChanged ) {
|
||||
// change it back
|
||||
session.setCacheMode( sessionCacheMode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
protected T doGetReference(Object id) {
|
||||
final SessionImplementor session = context.getSession();
|
||||
final EventSource eventSource = (EventSource) session;
|
||||
final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
|
||||
|
||||
if ( this.lockOptions != null ) {
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, eventSource, loadQueryInfluencers.getReadOnly() );
|
||||
context.fireLoad( event, LoadEventListener.LOAD );
|
||||
return (T) event.getResult();
|
||||
}
|
||||
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, eventSource, loadQueryInfluencers.getReadOnly() );
|
||||
boolean success = false;
|
||||
try {
|
||||
context.fireLoad( event, LoadEventListener.LOAD );
|
||||
if ( event.getResult() == null ) {
|
||||
session.getFactory().getEntityNotFoundDelegate().handleEntityNotFound(
|
||||
entityPersister.getEntityName(),
|
||||
id
|
||||
);
|
||||
}
|
||||
success = true;
|
||||
return (T) event.getResult();
|
||||
}
|
||||
finally {
|
||||
context.afterOperation( success );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T load(Object id) {
|
||||
return perform( () -> doLoad( id ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> loadOptional(Object id) {
|
||||
return Optional.ofNullable( perform( () -> doLoad( id ) ) );
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
protected final T doLoad(Object id) {
|
||||
final SessionImplementor session = context.getSession();
|
||||
final EventSource eventSource = (EventSource) session;
|
||||
final LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
|
||||
|
||||
if ( this.lockOptions != null ) {
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, eventSource, loadQueryInfluencers.getReadOnly() );
|
||||
context.fireLoad( event, LoadEventListener.GET );
|
||||
return (T) event.getResult();
|
||||
}
|
||||
|
||||
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, eventSource, loadQueryInfluencers.getReadOnly() );
|
||||
boolean success = false;
|
||||
try {
|
||||
context.fireLoad( event, LoadEventListener.GET );
|
||||
success = true;
|
||||
}
|
||||
catch (ObjectNotFoundException e) {
|
||||
// if session cache contains proxy for non-existing object
|
||||
}
|
||||
finally {
|
||||
context.afterOperation( success );
|
||||
}
|
||||
return (T) event.getResult();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.loader.access;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.event.spi.LoadEvent;
|
||||
import org.hibernate.event.spi.LoadEventListener;
|
||||
|
||||
/**
|
||||
* Context for loader-access objects. Generally this is equivalent
|
||||
* to the Session
|
||||
*/
|
||||
@Incubating
|
||||
@Internal
|
||||
public interface LoadAccessContext {
|
||||
/**
|
||||
* The session from which the load originates
|
||||
*/
|
||||
SessionImplementor getSession();
|
||||
|
||||
/**
|
||||
* Callback to check whether the session is "active"
|
||||
*/
|
||||
void checkOpenOrWaitingForAutoClose();
|
||||
|
||||
/**
|
||||
* Callback to pulse the transaction coo
|
||||
*/
|
||||
void pulseTransactionCoordinator();
|
||||
void delayedAfterCompletion();
|
||||
|
||||
void afterOperation(boolean success);
|
||||
|
||||
void fireLoad(LoadEvent event, LoadEventListener.LoadType load);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package org.hibernate.loader.access;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.NaturalIdLoadAccess;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class NaturalIdLoadAccessImpl<T> extends BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadAccess<T> {
|
||||
private final Map<String, Object> naturalIdParameters = new LinkedHashMap<>();
|
||||
|
||||
public NaturalIdLoadAccessImpl(LoadAccessContext context, EntityMappingType entityDescriptor) {
|
||||
super( context, entityDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
return (NaturalIdLoadAccessImpl<T>) super.with( lockOptions );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccess<T> using(String attributeName, Object value) {
|
||||
naturalIdParameters.put( attributeName, value );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccess<T> using(Object... mappings) {
|
||||
CollectionHelper.collectMapEntries( naturalIdParameters::put, mappings );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoadAccessImpl<T> setSynchronizationEnabled(boolean synchronizationEnabled) {
|
||||
super.synchronizationEnabled( synchronizationEnabled );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T getReference() {
|
||||
final SessionImplementor session = getContext().getSession();
|
||||
final Object normalizedValue = entityPersister().getNaturalIdMapping().normalizeInput( naturalIdParameters, session );
|
||||
|
||||
return doGetReference( normalizedValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T load() {
|
||||
final SessionImplementor session = getContext().getSession();
|
||||
final Object normalizedValue = entityPersister().getNaturalIdMapping().normalizeInput( naturalIdParameters, session );
|
||||
|
||||
return doLoad( normalizedValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> loadOptional() {
|
||||
return Optional.ofNullable( load() );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package org.hibernate.loader.access;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.SimpleNaturalIdLoadAccess;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.loader.LoaderLogging;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleNaturalIdMapping;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SimpleNaturalIdLoadAccessImpl<T>
|
||||
extends BaseNaturalIdLoadAccessImpl<T>
|
||||
implements SimpleNaturalIdLoadAccess<T> {
|
||||
private final boolean hasSimpleNaturalId;
|
||||
|
||||
public SimpleNaturalIdLoadAccessImpl(LoadAccessContext context, EntityMappingType entityDescriptor) {
|
||||
super( context, entityDescriptor );
|
||||
|
||||
hasSimpleNaturalId = entityDescriptor.getNaturalIdMapping() instanceof SimpleNaturalIdMapping;
|
||||
|
||||
if ( !hasSimpleNaturalId ) {
|
||||
// just log it - we allow this for composite natural-ids with the assumption
|
||||
// that a singular representation of the natural-id (Map or array) will be passed
|
||||
LoaderLogging.LOADER_LOGGER.debugf(
|
||||
"Entity [%s] did not define a simple natural id",
|
||||
entityDescriptor.getEntityName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
return super.getLockOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSynchronizationEnabled() {
|
||||
return super.isSynchronizationEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SimpleNaturalIdLoadAccessImpl<T> with(LockOptions lockOptions) {
|
||||
return (SimpleNaturalIdLoadAccessImpl<T>) super.with( lockOptions );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleNaturalIdLoadAccessImpl<T> setSynchronizationEnabled(boolean synchronizationEnabled) {
|
||||
super.synchronizationEnabled( synchronizationEnabled );
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getReference(Object naturalIdValue) {
|
||||
verifySimplicity( naturalIdValue );
|
||||
|
||||
final SessionImplementor session = getContext().getSession();
|
||||
final Object normalizedNaturalIdValue = entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue, session );
|
||||
|
||||
return doGetReference( normalizedNaturalIdValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public T load(Object naturalIdValue) {
|
||||
verifySimplicity( naturalIdValue );
|
||||
|
||||
final SessionImplementor session = getContext().getSession();
|
||||
final Object normalizedNaturalIdValue = entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue, session );
|
||||
|
||||
return doLoad( normalizedNaturalIdValue );
|
||||
}
|
||||
|
||||
private void verifySimplicity(Object naturalIdValue) {
|
||||
assert naturalIdValue != null;
|
||||
|
||||
if ( hasSimpleNaturalId ) {
|
||||
// implicitly
|
||||
return;
|
||||
}
|
||||
|
||||
if ( naturalIdValue.getClass().isArray() ) {
|
||||
// we allow compound natural-id "simple" loading all the values are passed as an array
|
||||
// (we assume the array is properly ordered following the mapping-model attribute ordering)
|
||||
return;
|
||||
}
|
||||
|
||||
if ( naturalIdValue instanceof List || naturalIdValue instanceof Map ) {
|
||||
// also allowed. For Lists, just like arrays, we assume the user has ordered them properly;
|
||||
// for Maps, the key is expected to be the attribute name
|
||||
return;
|
||||
}
|
||||
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Cannot interpret natural-id value [%s] for compound natural-id: %s",
|
||||
naturalIdValue,
|
||||
entityPersister().getEntityName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> loadOptional(Serializable naturalIdValue) {
|
||||
return Optional.ofNullable( load( naturalIdValue ) );
|
||||
}
|
||||
}
|
|
@ -8,34 +8,52 @@ package org.hibernate.loader.ast.internal;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.loader.NaturalIdPostLoadListener;
|
||||
import org.hibernate.loader.NaturalIdPreLoadListener;
|
||||
import org.hibernate.loader.ast.spi.Loadable;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoadOptions;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.mapping.SelectionMapping;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||
import org.hibernate.sql.exec.spi.Callback;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
/**
|
||||
* Base support for NaturalIdLoader implementations
|
||||
|
@ -48,18 +66,10 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
private final NaturalIdMapping naturalIdMapping;
|
||||
private final EntityMappingType entityDescriptor;
|
||||
|
||||
private final NaturalIdPreLoadListener preLoadListener;
|
||||
private final NaturalIdPostLoadListener postLoadListener;
|
||||
|
||||
public AbstractNaturalIdLoader(
|
||||
NaturalIdMapping naturalIdMapping,
|
||||
NaturalIdPreLoadListener preLoadListener,
|
||||
NaturalIdPostLoadListener postLoadListener,
|
||||
EntityMappingType entityDescriptor,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
EntityMappingType entityDescriptor) {
|
||||
this.naturalIdMapping = naturalIdMapping;
|
||||
this.preLoadListener = preLoadListener;
|
||||
this.postLoadListener = postLoadListener;
|
||||
this.entityDescriptor = entityDescriptor;
|
||||
}
|
||||
|
||||
|
@ -77,35 +87,132 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public T load(Object naturalIdValue, LoadOptions options, SharedSessionContractImplementor session) {
|
||||
final Object bindValue = resolveNaturalIdBindValue( naturalIdValue, session );
|
||||
preLoadListener.startingLoadByNaturalId( entityDescriptor, bindValue, session );
|
||||
public T load(Object naturalIdValue, NaturalIdLoadOptions options, SharedSessionContractImplementor session) {
|
||||
return selectByNaturalId(
|
||||
naturalIdMapping().normalizeInput( naturalIdValue, session ),
|
||||
options,
|
||||
(tableGroup, creationState) -> entityDescriptor.createDomainResult(
|
||||
new NavigablePath( entityDescriptor().getRootPathName() ),
|
||||
tableGroup,
|
||||
null,
|
||||
creationState
|
||||
),
|
||||
(fetchParent, querySpec, creationState) -> {
|
||||
final List<Fetch> fetches = new ArrayList<>( naturalIdMapping.getNaturalIdAttributes().size() );
|
||||
fetchParent.getReferencedMappingContainer().visitFetchables(
|
||||
fetchable -> {
|
||||
final NavigablePath navigablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() );
|
||||
final Fetch fetch = fetchable.generateFetch(
|
||||
fetchParent,
|
||||
navigablePath,
|
||||
fetchable.getMappedFetchOptions().getTiming(),
|
||||
true,
|
||||
options.getLockOptions() != null ? options.getLockOptions().getLockMode() : LockMode.READ,
|
||||
null,
|
||||
creationState
|
||||
);
|
||||
fetches.add( fetch );
|
||||
},
|
||||
entityDescriptor
|
||||
);
|
||||
return fetches;
|
||||
},
|
||||
(statsEnabled) -> {
|
||||
// entityDescriptor().getPreLoadListener().startingLoad( entityDescriptor, naturalIdValue, KeyType.NATURAL_ID, LoadSource.DATABASE );
|
||||
// return statsEnabled ? System.nanoTime() : -1;
|
||||
return -1L;
|
||||
},
|
||||
(result,startToken) -> {
|
||||
// entityDescriptor().getPostLoadListener().completedLoad( result, entityDescriptor(), naturalIdValue, KeyType.NATURAL_ID, LoadSource.DATABASE );
|
||||
// if ( startToken > 0 ) {
|
||||
// // todo (6.0) : need a "load-by-natural-id" stat
|
||||
// // e.g.,
|
||||
// // final Object identifier = entityDescriptor().getIdentifierMapping().getIdentifier( result, session );
|
||||
// // session.getFactory().getStatistics().entityLoadedByNaturalId( entityDescriptor(), identifier );
|
||||
// }
|
||||
},
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a select, restricted by natural-id, based on `domainResultProducer` and `fetchProcessor`
|
||||
*/
|
||||
protected <L> L selectByNaturalId(
|
||||
Object bindValue,
|
||||
NaturalIdLoadOptions options,
|
||||
BiFunction<TableGroup,LoaderSqlAstCreationState, DomainResult<?>> domainResultProducer,
|
||||
LoaderSqlAstCreationState.FetchProcessor fetchProcessor,
|
||||
Function<Boolean,Long> statementStartHandler,
|
||||
BiConsumer<Object,Long> statementCompletionHandler,
|
||||
SharedSessionContractImplementor session) {
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||
|
||||
final List<JdbcParameter> jdbcParameters = new ArrayList<>();
|
||||
final SelectStatement sqlSelect = LoaderSelectBuilder.createSelect(
|
||||
entityDescriptor(),
|
||||
Collections.emptyList(),
|
||||
naturalIdMapping(),
|
||||
null,
|
||||
1,
|
||||
session.getLoadQueryInfluencers(),
|
||||
options.getLockOptions(),
|
||||
jdbcParameters::add,
|
||||
final LockOptions lockOptions;
|
||||
if ( options.getLockOptions() != null ) {
|
||||
lockOptions = options.getLockOptions();
|
||||
}
|
||||
else {
|
||||
lockOptions = LockOptions.READ;
|
||||
}
|
||||
final LockMode lockMode = lockOptions.getLockMode();
|
||||
|
||||
final NavigablePath entityPath = new NavigablePath( entityDescriptor.getRootPathName() );
|
||||
final QuerySpec rootQuerySpec = new QuerySpec( true );
|
||||
|
||||
final LoaderSqlAstCreationState sqlAstCreationState = new LoaderSqlAstCreationState(
|
||||
rootQuerySpec,
|
||||
new SqlAliasBaseManager(),
|
||||
new SimpleFromClauseAccessImpl(),
|
||||
lockOptions,
|
||||
fetchProcessor,
|
||||
true,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup(
|
||||
entityPath,
|
||||
null,
|
||||
true,
|
||||
lockMode,
|
||||
() -> rootQuerySpec::applyPredicate,
|
||||
sqlAstCreationState,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
rootQuerySpec.getFromClause().addRoot( rootTableGroup );
|
||||
sqlAstCreationState.getFromClauseAccess().registerTableGroup( entityPath, rootTableGroup );
|
||||
|
||||
final DomainResult<?> domainResult = domainResultProducer.apply( rootTableGroup, sqlAstCreationState );
|
||||
|
||||
final SelectStatement sqlSelect = new SelectStatement( rootQuerySpec, Collections.singletonList( domainResult ) );
|
||||
|
||||
final List<JdbcParameter> jdbcParameters = new ArrayList<>( naturalIdMapping.getJdbcTypeCount() );
|
||||
final JdbcParameterBindings jdbcParamBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() );
|
||||
applyNaturalIdAsJdbcParameters( bindValue, jdbcParameters, jdbcParamBindings, session );
|
||||
|
||||
applyNaturalIdRestriction(
|
||||
bindValue,
|
||||
rootTableGroup,
|
||||
rootQuerySpec::applyPredicate,
|
||||
(jdbcParameter, jdbcParameterBinding) -> {
|
||||
jdbcParameters.add( jdbcParameter );
|
||||
jdbcParamBindings.addBinding( jdbcParameter, jdbcParameterBinding );
|
||||
},
|
||||
sqlAstCreationState,
|
||||
session
|
||||
);
|
||||
|
||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
.translate( jdbcParamBindings, QueryOptions.NONE );
|
||||
|
||||
final StatisticsImplementor statistics = sessionFactory.getStatistics();
|
||||
final Long startToken = statementStartHandler.apply( statistics.isStatisticsEnabled() );
|
||||
|
||||
//noinspection unchecked
|
||||
final List<T> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
final List<L> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParamBindings,
|
||||
new ExecutionContext() {
|
||||
|
@ -126,11 +233,10 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return afterLoadAction -> {
|
||||
};
|
||||
return afterLoadAction -> {};
|
||||
}
|
||||
},
|
||||
row -> (T) row[0],
|
||||
row -> (L) row[0],
|
||||
true
|
||||
);
|
||||
|
||||
|
@ -143,91 +249,112 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
);
|
||||
}
|
||||
|
||||
final T result = results.get( 0 );
|
||||
postLoadListener.completedLoadByNaturalId( entityDescriptor, result, session );
|
||||
if ( results.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final L result = results.get( 0 );
|
||||
|
||||
statementCompletionHandler.accept( result, startToken );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract Object resolveNaturalIdBindValue(Object naturalIdToLoad, SharedSessionContractImplementor session);
|
||||
/**
|
||||
* Apply restriction necessary to match the given natural-id value. Should
|
||||
* apply any predicates to `predicateConsumer` as well and any parameter / binding
|
||||
* pairs
|
||||
*/
|
||||
protected abstract void applyNaturalIdRestriction(
|
||||
Object bindValue,
|
||||
TableGroup rootTableGroup,
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
BiConsumer<JdbcParameter, JdbcParameterBinding> jdbcParameterConsumer,
|
||||
LoaderSqlAstCreationState sqlAstCreationState,
|
||||
SharedSessionContractImplementor session);
|
||||
|
||||
protected abstract void applyNaturalIdAsJdbcParameters(
|
||||
Object naturalIdToLoad,
|
||||
List<JdbcParameter> jdbcParameters,
|
||||
JdbcParameterBindings jdbcParamBindings, SharedSessionContractImplementor session);
|
||||
|
||||
@Override
|
||||
public Object resolveNaturalIdToId(Object naturalIdValue, SharedSessionContractImplementor session) {
|
||||
final Object bindValue = resolveNaturalIdBindValue( naturalIdValue, session );
|
||||
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||
|
||||
final List<JdbcParameter> jdbcParameters = new ArrayList<>();
|
||||
final SelectStatement sqlSelect = LoaderSelectBuilder.createSelect(
|
||||
entityDescriptor(),
|
||||
Collections.singletonList( entityDescriptor().getIdentifierMapping() ),
|
||||
naturalIdMapping(),
|
||||
null,
|
||||
1,
|
||||
session.getLoadQueryInfluencers(),
|
||||
LockOptions.READ,
|
||||
jdbcParameters::add,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
final JdbcParameterBindings jdbcParamBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() );
|
||||
applyNaturalIdAsJdbcParameters(
|
||||
bindValue,
|
||||
jdbcParameters,
|
||||
jdbcParamBindings,
|
||||
session
|
||||
);
|
||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
.translate( jdbcParamBindings, QueryOptions.NONE );
|
||||
|
||||
final List<?> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParamBindings,
|
||||
new ExecutionContext() {
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return QueryOptions.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getQueryParameterBindings() {
|
||||
return QueryParameterBindings.NO_PARAM_BINDINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return afterLoadAction -> {
|
||||
};
|
||||
}
|
||||
},
|
||||
row -> row[0],
|
||||
true
|
||||
);
|
||||
|
||||
if ( results.size() > 1 ) {
|
||||
throw new HibernateException(
|
||||
/**
|
||||
* Helper to resolve ColumnReferences
|
||||
*/
|
||||
protected Expression resolveColumnReference(
|
||||
TableGroup rootTableGroup,
|
||||
SelectionMapping selectionMapping,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
final TableReference tableReference = rootTableGroup.getTableReference( selectionMapping.getContainingTableExpression() );
|
||||
if ( tableReference == null ) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Resolving natural-id to id returned more that one row : %s [%s]",
|
||||
entityDescriptor().getEntityName(),
|
||||
bindValue
|
||||
Locale.ROOT,
|
||||
"Unable to locate TableReference for `%s` : %s",
|
||||
selectionMapping.getContainingTableExpression(),
|
||||
rootTableGroup
|
||||
)
|
||||
);
|
||||
}
|
||||
return sqlExpressionResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey( tableReference, selectionMapping.getSelectionExpression() ),
|
||||
(processingState) -> new ColumnReference(
|
||||
tableReference,
|
||||
selectionMapping.getSelectionExpression(),
|
||||
selectionMapping.isFormula(),
|
||||
selectionMapping.getCustomReadExpression(),
|
||||
selectionMapping.getCustomWriteExpression(),
|
||||
selectionMapping.getJdbcMapping(),
|
||||
sessionFactory
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return results.get( 0 );
|
||||
@Override
|
||||
public Object resolveNaturalIdToId(Object naturalIdValue, SharedSessionContractImplementor session) {
|
||||
return selectByNaturalId(
|
||||
naturalIdMapping().normalizeInput( naturalIdValue, session ),
|
||||
NaturalIdLoadOptions.NONE,
|
||||
(tableGroup, creationState) -> entityDescriptor.getIdentifierMapping().createDomainResult(
|
||||
tableGroup.getNavigablePath().append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
|
||||
tableGroup,
|
||||
null,
|
||||
creationState
|
||||
),
|
||||
(fetchParent, querySpec, creationState) -> {
|
||||
final List<Fetch> fetches = new ArrayList<>();
|
||||
|
||||
fetchParent.getReferencedMappingContainer().visitFetchables(
|
||||
(fetchable) -> {
|
||||
final NavigablePath navigablePath = fetchParent.getNavigablePath().append( fetchable.getFetchableName() );
|
||||
final Fetch fetch = fetchable.generateFetch(
|
||||
fetchParent,
|
||||
navigablePath,
|
||||
fetchable.getMappedFetchOptions().getTiming(),
|
||||
true,
|
||||
LockMode.READ,
|
||||
null,
|
||||
creationState
|
||||
);
|
||||
fetches.add( fetch );
|
||||
},
|
||||
null
|
||||
);
|
||||
|
||||
return fetches;
|
||||
},
|
||||
(statsEnabled) -> {
|
||||
// entityDescriptor().getPreLoadListener().startingLoad( entityDescriptor, naturalIdValue, KeyType.NATURAL_ID, LoadSource.DATABASE );
|
||||
// return statsEnabled ? System.nanoTime() : -1;
|
||||
return -1L;
|
||||
},
|
||||
(result,startToken) -> {
|
||||
// entityDescriptor().getPostLoadListener().completedLoad( result, entityDescriptor(), naturalIdValue, KeyType.NATURAL_ID, LoadSource.DATABASE );
|
||||
// if ( startToken > 0 ) {
|
||||
// // todo (6.0) : need a "load-by-natural-id" stat
|
||||
// // e.g.,
|
||||
// // final Object identifier = entityDescriptor().getIdentifierMapping().getIdentifier( result, session );
|
||||
// // session.getFactory().getStatistics().entityLoadedByNaturalId( entityDescriptor(), identifier );
|
||||
// }
|
||||
},
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -237,7 +364,7 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
final List<JdbcParameter> jdbcParameters = new ArrayList<>();
|
||||
final SelectStatement sqlSelect = LoaderSelectBuilder.createSelect(
|
||||
entityDescriptor(),
|
||||
naturalIdMapping().getNaturalIdAttributes(),
|
||||
Collections.singletonList( naturalIdMapping() ),
|
||||
entityDescriptor().getIdentifierMapping(),
|
||||
null,
|
||||
1,
|
||||
|
@ -263,7 +390,7 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
.translate( jdbcParamBindings, QueryOptions.NONE );
|
||||
|
||||
final List<Object[]> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
final List<Object> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParamBindings,
|
||||
new ExecutionContext() {
|
||||
|
@ -288,10 +415,18 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
};
|
||||
}
|
||||
},
|
||||
row -> row,
|
||||
(row) -> {
|
||||
// because we select the natural-id we want to "reduce" the result
|
||||
assert row.length == 1;
|
||||
return row[0];
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
if ( results.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( results.size() > 1 ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
|
@ -302,13 +437,6 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
|
|||
);
|
||||
}
|
||||
|
||||
final Object[] objects = results.get( 0 );
|
||||
if ( isSimple() ) {
|
||||
return objects[0];
|
||||
}
|
||||
|
||||
return objects;
|
||||
return results.get( 0 );
|
||||
}
|
||||
|
||||
protected abstract boolean isSimple();
|
||||
}
|
||||
|
|
|
@ -6,21 +6,26 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.loader.NaturalIdPostLoadListener;
|
||||
import org.hibernate.loader.NaturalIdPreLoadListener;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.CompoundNaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||
|
||||
/**
|
||||
* NaturalIdLoader implementation for compound natural-ids
|
||||
|
@ -29,65 +34,70 @@ public class CompoundNaturalIdLoader<T> extends AbstractNaturalIdLoader<T> {
|
|||
|
||||
public CompoundNaturalIdLoader(
|
||||
CompoundNaturalIdMapping naturalIdMapping,
|
||||
NaturalIdPreLoadListener preLoadListener,
|
||||
NaturalIdPostLoadListener postLoadListener,
|
||||
EntityMappingType entityDescriptor,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
super( naturalIdMapping, preLoadListener, postLoadListener, entityDescriptor, creationProcess );
|
||||
EntityMappingType entityDescriptor) {
|
||||
super( naturalIdMapping, entityDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNaturalIdBindValue(Object naturalIdValue, SharedSessionContractImplementor session) {
|
||||
// the "real" form as an array, although we also accept here a Map and reduce it to
|
||||
// the appropriately ordered array
|
||||
if ( naturalIdValue instanceof Object[] ) {
|
||||
return naturalIdValue;
|
||||
}
|
||||
|
||||
final List<SingularAttributeMapping> attributes = naturalIdMapping().getNaturalIdAttributes();
|
||||
final Object[] naturalId = new Object[ attributes.size() ];
|
||||
|
||||
if ( naturalIdValue instanceof Map ) {
|
||||
final Map<String,?> valueMap = (Map<String,?>) naturalIdValue;
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
final SingularAttributeMapping attributeMapping = attributes.get( i );
|
||||
naturalId[ i ] = valueMap.get( attributeMapping.getAttributeName() );
|
||||
}
|
||||
return naturalId;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException( "Unexpected natural-id reference [" + naturalIdValue + "; expecting array or Map" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyNaturalIdAsJdbcParameters(
|
||||
Object naturalIdToLoad,
|
||||
List<JdbcParameter> jdbcParameters,
|
||||
JdbcParameterBindings jdbcParamBindings,
|
||||
protected void applyNaturalIdRestriction(
|
||||
Object bindValue,
|
||||
TableGroup rootTableGroup,
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
BiConsumer<JdbcParameter, JdbcParameterBinding> jdbcParameterConsumer,
|
||||
LoaderSqlAstCreationState sqlAstCreationState,
|
||||
SharedSessionContractImplementor session) {
|
||||
assert naturalIdToLoad instanceof Object[];
|
||||
final Object[] naturalIdValueArray = (Object[]) naturalIdToLoad;
|
||||
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||
final SessionFactoryImplementor factory = session.getFactory();
|
||||
|
||||
int offset = 0;
|
||||
if ( bindValue == null ) {
|
||||
final List<SingularAttributeMapping> naturalIdAttributes = naturalIdMapping().getNaturalIdAttributes();
|
||||
for ( int i = 0; i < naturalIdAttributes.size(); i++ ) {
|
||||
naturalIdAttributes.get( i ).forEachSelection(
|
||||
(selectionIndex, selectionMapping) -> {
|
||||
final Expression columnRef = resolveColumnReference(
|
||||
rootTableGroup,
|
||||
selectionMapping,
|
||||
sqlExpressionResolver,
|
||||
factory
|
||||
);
|
||||
predicateConsumer.accept( new NullnessPredicate( columnRef ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
for ( int i = 0; i < naturalIdMapping().getNaturalIdAttributes().size(); i++ ) {
|
||||
final SingularAttributeMapping attrMapping = naturalIdMapping().getNaturalIdAttributes().get( i );
|
||||
offset += jdbcParamBindings.registerParametersForEachJdbcValue(
|
||||
naturalIdValueArray[i],
|
||||
Clause.WHERE,
|
||||
offset,
|
||||
attrMapping,
|
||||
jdbcParameters,
|
||||
session
|
||||
);
|
||||
// EARLY EXIT!!
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure we've exhausted all JDBC parameters
|
||||
assert offset == jdbcParameters.size();
|
||||
naturalIdMapping().breakDownJdbcValues(
|
||||
bindValue,
|
||||
(jdbcValue, jdbcValueMapping) -> {
|
||||
final Expression columnReference = resolveColumnReference(
|
||||
rootTableGroup,
|
||||
jdbcValueMapping,
|
||||
sqlExpressionResolver,
|
||||
factory
|
||||
);
|
||||
|
||||
if ( jdbcValue == null ) {
|
||||
predicateConsumer.accept( new NullnessPredicate( columnReference ) );
|
||||
}
|
||||
else {
|
||||
final JdbcParameter jdbcParameter = new JdbcParameterImpl( jdbcValueMapping.getJdbcMapping() );
|
||||
final ComparisonPredicate predicate = new ComparisonPredicate(
|
||||
columnReference,
|
||||
ComparisonOperator.EQUAL,
|
||||
jdbcParameter
|
||||
);
|
||||
predicateConsumer.accept( predicate );
|
||||
jdbcParameterConsumer.accept(
|
||||
jdbcParameter,
|
||||
new JdbcParameterBindingImpl( jdbcValueMapping.getJdbcMapping(), jdbcValue )
|
||||
);
|
||||
}
|
||||
},
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSimple() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,9 +82,8 @@ class DatabaseSnapshotExecutor {
|
|||
null,
|
||||
true,
|
||||
LockMode.NONE,
|
||||
sqlAliasBaseManager,
|
||||
state.getSqlExpressionResolver(),
|
||||
() -> rootQuerySpec::applyPredicate,
|
||||
state,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
|
|
|
@ -240,11 +240,11 @@ public class LoaderSelectBuilder {
|
|||
private final List<ModelPart> restrictedParts;
|
||||
private final DomainResult cachedDomainResult;
|
||||
private final int numberOfKeysToLoad;
|
||||
private final boolean forceIdentifierSelection;
|
||||
private final LoadQueryInfluencers loadQueryInfluencers;
|
||||
private final LockOptions lockOptions;
|
||||
private final Consumer<JdbcParameter> jdbcParameterConsumer;
|
||||
private final EntityGraphTraversalState entityGraphTraversalState;
|
||||
private boolean forceIdentifierSelection;
|
||||
|
||||
private int fetchDepth;
|
||||
private Map<OrderByFragment, TableGroup> orderByFragments;
|
||||
|
@ -378,9 +378,8 @@ public class LoaderSelectBuilder {
|
|||
null,
|
||||
true,
|
||||
lockOptions.getLockMode(),
|
||||
sqlAstCreationState.getSqlAliasBaseManager(),
|
||||
sqlAstCreationState.getSqlExpressionResolver(),
|
||||
() -> rootQuerySpec::applyPredicate,
|
||||
sqlAstCreationState,
|
||||
creationContext
|
||||
);
|
||||
|
||||
|
@ -831,9 +830,8 @@ public class LoaderSelectBuilder {
|
|||
null,
|
||||
true,
|
||||
lockOptions.getLockMode(),
|
||||
sqlAstCreationState.getSqlAliasBaseManager(),
|
||||
sqlAstCreationState.getSqlExpressionResolver(),
|
||||
() -> rootQuerySpec::applyPredicate,
|
||||
sqlAstCreationState,
|
||||
creationContext
|
||||
);
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoadOptions;
|
||||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.sql.results.LoadingLogger;
|
||||
|
||||
/**
|
||||
|
@ -31,7 +30,7 @@ public class MultiNaturalIdLoaderStandard<E> implements MultiNaturalIdLoader<E>
|
|||
|
||||
private final EntityMappingType entityDescriptor;
|
||||
|
||||
public MultiNaturalIdLoaderStandard(EntityMappingType entityDescriptor, MappingModelCreationProcess creationProcess) {
|
||||
public MultiNaturalIdLoaderStandard(EntityMappingType entityDescriptor) {
|
||||
this.entityDescriptor = entityDescriptor;
|
||||
}
|
||||
|
||||
|
@ -75,7 +74,7 @@ public class MultiNaturalIdLoaderStandard<E> implements MultiNaturalIdLoader<E>
|
|||
(naturalId, session1) -> {
|
||||
// `naturalId` here is the one passed in by the API as part of the values array
|
||||
// todo (6.0) : use this to help create the ordered results
|
||||
return entityDescriptor.getNaturalIdMapping().normalizeIncomingValue( naturalId, session );
|
||||
return entityDescriptor.getNaturalIdMapping().normalizeInput( naturalId, session );
|
||||
},
|
||||
session.getLoadQueryInfluencers(),
|
||||
lockOptions,
|
||||
|
|
|
@ -47,7 +47,7 @@ public class MultiNaturalIdLoadingBatcher {
|
|||
* the "true" form - single value for simple natural-ids and an array for
|
||||
* compound natural-ids.
|
||||
*
|
||||
* Generally delegates to {@link org.hibernate.metamodel.mapping.NaturalIdMapping#normalizeIncomingValue}
|
||||
* Generally delegates to {@link org.hibernate.metamodel.mapping.NaturalIdMapping#normalizeInput}
|
||||
*/
|
||||
Object resolveKeyToLoad(Object incoming, SharedSessionContractImplementor session);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.loader.ast.internal;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockOptions;
|
||||
|
@ -16,21 +18,27 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
|||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.loader.NaturalIdPostLoadListener;
|
||||
import org.hibernate.loader.NaturalIdPreLoadListener;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleNaturalIdMapping;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
||||
import org.hibernate.sql.exec.spi.Callback;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||
|
||||
|
@ -41,11 +49,8 @@ public class SimpleNaturalIdLoader<T> extends AbstractNaturalIdLoader<T> {
|
|||
|
||||
public SimpleNaturalIdLoader(
|
||||
SimpleNaturalIdMapping naturalIdMapping,
|
||||
NaturalIdPreLoadListener preLoadListener,
|
||||
NaturalIdPostLoadListener postLoadListener,
|
||||
EntityMappingType entityDescriptor,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
super( naturalIdMapping, preLoadListener, postLoadListener, entityDescriptor, creationProcess );
|
||||
EntityMappingType entityDescriptor) {
|
||||
super( naturalIdMapping, entityDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,114 +59,71 @@ public class SimpleNaturalIdLoader<T> extends AbstractNaturalIdLoader<T> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void applyNaturalIdAsJdbcParameters(
|
||||
Object naturalIdToLoad,
|
||||
List<JdbcParameter> jdbcParameters,
|
||||
JdbcParameterBindings jdbcParamBindings,
|
||||
protected void applyNaturalIdRestriction(
|
||||
Object bindValue,
|
||||
TableGroup rootTableGroup,
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
BiConsumer<JdbcParameter, JdbcParameterBinding> jdbcParameterConsumer,
|
||||
LoaderSqlAstCreationState sqlAstCreationState,
|
||||
SharedSessionContractImplementor session) {
|
||||
assert jdbcParameters.size() == 1;
|
||||
|
||||
final Object bindableValue = naturalIdMapping().normalizeIncomingValue( naturalIdToLoad, session );
|
||||
|
||||
final SingularAttributeMapping attributeMapping = naturalIdMapping().getNaturalIdAttributes().get( 0 );
|
||||
jdbcParamBindings.registerParametersForEachJdbcValue(
|
||||
bindableValue,
|
||||
Clause.WHERE,
|
||||
attributeMapping,
|
||||
jdbcParameters,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveNaturalIdBindValue(Object naturalIdToLoad, SharedSessionContractImplementor session) {
|
||||
return naturalIdMapping().normalizeIncomingValue( naturalIdToLoad, session );
|
||||
if ( bindValue == null ) {
|
||||
naturalIdMapping().getAttribute().forEachSelection(
|
||||
(selectionIndex, selectionMapping) -> {
|
||||
final Expression columnReference = resolveColumnReference(
|
||||
rootTableGroup,
|
||||
selectionMapping,
|
||||
sqlAstCreationState.getSqlExpressionResolver(),
|
||||
session.getFactory()
|
||||
);
|
||||
predicateConsumer.accept( new NullnessPredicate( columnReference ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
naturalIdMapping().getAttribute().breakDownJdbcValues(
|
||||
bindValue,
|
||||
(jdbcValue, jdbcValueMapping) -> {
|
||||
final Expression columnReference = resolveColumnReference(
|
||||
rootTableGroup,
|
||||
jdbcValueMapping,
|
||||
sqlAstCreationState.getSqlExpressionResolver(),
|
||||
session.getFactory()
|
||||
);
|
||||
if ( jdbcValue == null ) {
|
||||
predicateConsumer.accept( new NullnessPredicate( columnReference ) );
|
||||
}
|
||||
else {
|
||||
final JdbcParameter jdbcParameter = new JdbcParameterImpl( jdbcValueMapping.getJdbcMapping() );
|
||||
final ComparisonPredicate predicate = new ComparisonPredicate(
|
||||
columnReference,
|
||||
ComparisonOperator.EQUAL,
|
||||
jdbcParameter
|
||||
);
|
||||
predicateConsumer.accept( predicate );
|
||||
jdbcParameterConsumer.accept(
|
||||
jdbcParameter,
|
||||
new JdbcParameterBindingImpl( jdbcValueMapping.getJdbcMapping(), jdbcValue )
|
||||
);
|
||||
}
|
||||
},
|
||||
session
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveIdToNaturalId(Object id, SharedSessionContractImplementor session) {
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
final Object rawValue = super.resolveIdToNaturalId( id, session );
|
||||
assert rawValue instanceof Object[];
|
||||
|
||||
final List<JdbcParameter> jdbcParameters = new ArrayList<>();
|
||||
final SelectStatement sqlSelect = LoaderSelectBuilder.createSelect(
|
||||
entityDescriptor(),
|
||||
naturalIdMapping().getNaturalIdAttributes(),
|
||||
entityDescriptor().getIdentifierMapping(),
|
||||
null,
|
||||
1,
|
||||
session.getLoadQueryInfluencers(),
|
||||
LockOptions.READ,
|
||||
jdbcParameters::add,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||
|
||||
final JdbcParameterBindings jdbcParamBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() );
|
||||
jdbcParamBindings.registerParametersForEachJdbcValue(
|
||||
id,
|
||||
Clause.WHERE,
|
||||
entityDescriptor().getIdentifierMapping(),
|
||||
jdbcParameters,
|
||||
session
|
||||
);
|
||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlSelect )
|
||||
.translate( jdbcParamBindings, QueryOptions.NONE );
|
||||
|
||||
final List<Object[]> results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParamBindings,
|
||||
new ExecutionContext() {
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return QueryOptions.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getQueryParameterBindings() {
|
||||
return QueryParameterBindings.NO_PARAM_BINDINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return afterLoadAction -> {
|
||||
};
|
||||
}
|
||||
},
|
||||
row -> row,
|
||||
true
|
||||
);
|
||||
|
||||
if ( results.size() > 1 ) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"Resolving id to natural-id returned more that one row : %s #%s",
|
||||
entityDescriptor().getEntityName(),
|
||||
id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return results.get( 0 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSimple() {
|
||||
return true;
|
||||
return ( (Object[]) rawValue )[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveNaturalIdToId(
|
||||
Object naturalIdValue,
|
||||
SharedSessionContractImplementor session) {
|
||||
final Object bindValue = naturalIdMapping().normalizeIncomingValue( naturalIdValue, session );
|
||||
final Object bindValue = naturalIdMapping().normalizeInput( naturalIdValue, session );
|
||||
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
|
@ -180,7 +142,6 @@ public class SimpleNaturalIdLoader<T> extends AbstractNaturalIdLoader<T> {
|
|||
jdbcParameters::add,
|
||||
sessionFactory
|
||||
);
|
||||
assert jdbcParameters.size() == 1;
|
||||
|
||||
final JdbcParameterBindings jdbcParamBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() );
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ import org.hibernate.NotYetImplementedFor6Exception;
|
|||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.tree.from.RootTableGroupProducer;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
|
@ -43,10 +42,8 @@ public interface Loadable extends ModelPart, RootTableGroupProducer {
|
|||
String explicitSourceAlias,
|
||||
boolean canUseInnerJoins,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
SqlAstCreationState creationState, SqlAstCreationContext creationContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package org.hibernate.loader.ast.spi;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
|
||||
/**
|
||||
* Options for loading by natural-id
|
||||
*/
|
||||
public interface NaturalIdLoadOptions {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
NaturalIdLoadOptions NONE = new NaturalIdLoadOptions() {
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
return LockOptions.READ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSynchronizationEnabled() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The locking options for the loaded entity
|
||||
*/
|
||||
LockOptions getLockOptions();
|
||||
|
||||
/**
|
||||
* Whether Hibernate should perform "synchronization" prior to performing
|
||||
* look-ups?
|
||||
*/
|
||||
boolean isSynchronizationEnabled();
|
||||
}
|
|
@ -6,9 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.spi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
|
@ -17,36 +14,6 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface NaturalIdLoader<T> extends Loader {
|
||||
/**
|
||||
* Options for the {@link #load} method
|
||||
*/
|
||||
interface LoadOptions {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
LoadOptions NONE = new LoadOptions() {
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
return LockOptions.READ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSynchronizationEnabled() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The locking options for the loaded entity
|
||||
*/
|
||||
LockOptions getLockOptions();
|
||||
|
||||
/**
|
||||
* Whether Hibernate should perform "synchronization" prior to performing
|
||||
* look-ups?
|
||||
*/
|
||||
boolean isSynchronizationEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the load of the entity by its natural-id
|
||||
|
@ -61,7 +28,7 @@ public interface NaturalIdLoader<T> extends Loader {
|
|||
* @param options The options to apply to the load operation
|
||||
* @param session The session into which the entity is being loaded
|
||||
*/
|
||||
T load(Object naturalIdToLoad, LoadOptions options, SharedSessionContractImplementor session);
|
||||
T load(Object naturalIdToLoad, NaturalIdLoadOptions options, SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Resolve the id from natural-id value
|
||||
|
|
|
@ -975,10 +975,20 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
|
|||
return java.util.Collections.unmodifiableMap( tuplizerImpls );
|
||||
}
|
||||
|
||||
private Boolean hasNaturalId;
|
||||
|
||||
public boolean hasNaturalId() {
|
||||
Iterator props = getRootClass().getPropertyIterator();
|
||||
if ( hasNaturalId == null ) {
|
||||
hasNaturalId = determineIfNaturalIdDefined();
|
||||
}
|
||||
return hasNaturalId;
|
||||
}
|
||||
|
||||
private boolean determineIfNaturalIdDefined() {
|
||||
//noinspection unchecked
|
||||
final Iterator<Property> props = getRootClass().getPropertyIterator();
|
||||
while ( props.hasNext() ) {
|
||||
if ( ( (Property) props.next() ).isNaturalIdentifier() ) {
|
||||
if ( props.next().isNaturalIdentifier() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import javax.persistence.OneToOne;
|
|||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.Type;
|
||||
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.internal.EntityManagerMessageLogger;
|
||||
import org.hibernate.internal.HEMLogging;
|
||||
|
@ -233,39 +232,40 @@ public class AttributeFactory {
|
|||
final Component component = (Component) typeContext.getHibernateValue();
|
||||
final EmbeddableTypeImpl<Y> embeddableType;
|
||||
|
||||
if ( component.getComponentClass() != null
|
||||
|| component.getComponentClassName() != null ) {
|
||||
final EmbeddableRepresentationStrategy representationStrategy = context.getTypeConfiguration()
|
||||
.getMetadataBuildingContext()
|
||||
.getBuildingOptions()
|
||||
.getManagedTypeRepresentationResolver()
|
||||
.resolveStrategy( component, context.getRuntimeModelCreationContext() );
|
||||
|
||||
if ( component.isDynamic() ) {
|
||||
final JavaTypeDescriptor javaTypeDescriptor = context.getJavaTypeDescriptorRegistry().getDescriptor( Map.class );
|
||||
|
||||
embeddableType = new EmbeddableTypeImpl<>(
|
||||
javaTypeDescriptor,
|
||||
representationStrategy,
|
||||
true,
|
||||
context.getJpaMetamodel()
|
||||
);
|
||||
}
|
||||
else {
|
||||
// we should have a non-dynamic embeddable
|
||||
assert component.getComponentClassName() != null;
|
||||
final Class<Y> embeddableClass = component.getComponentClass();
|
||||
|
||||
final Class embeddableClass;
|
||||
if ( component.getComponentClass() != null ) {
|
||||
embeddableClass = component.getComponentClass();
|
||||
}
|
||||
else {
|
||||
embeddableClass = context.getTypeConfiguration()
|
||||
.getServiceRegistry()
|
||||
.getService( ClassLoaderService.class )
|
||||
.classForName( component.getComponentClassName() );
|
||||
}
|
||||
|
||||
final EmbeddableDomainType cached = context.locateEmbeddable( embeddableClass );
|
||||
final EmbeddableDomainType<Y> cached = context.locateEmbeddable( embeddableClass );
|
||||
if ( cached != null ) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
final JavaTypeDescriptorRegistry registry = context.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry();
|
||||
final JavaTypeDescriptor javaTypeDescriptor = registry.resolveDescriptor( embeddableClass );
|
||||
|
||||
final EmbeddableRepresentationStrategy representationStrategy = context.getTypeConfiguration()
|
||||
.getMetadataBuildingContext()
|
||||
.getBuildingOptions()
|
||||
.getManagedTypeRepresentationResolver()
|
||||
.resolveStrategy( component, context.getRuntimeModelCreationContext() );
|
||||
final JavaTypeDescriptor<Y> javaTypeDescriptor = registry.resolveDescriptor( embeddableClass );
|
||||
|
||||
embeddableType = new EmbeddableTypeImpl<>(
|
||||
javaTypeDescriptor,
|
||||
representationStrategy,
|
||||
false,
|
||||
context.getJpaMetamodel()
|
||||
);
|
||||
|
||||
|
@ -273,12 +273,6 @@ public class AttributeFactory {
|
|||
|
||||
return embeddableType;
|
||||
}
|
||||
else {
|
||||
embeddableType = new EmbeddableTypeImpl(
|
||||
component.getRoleName(),
|
||||
context.getJpaMetamodel()
|
||||
);
|
||||
}
|
||||
|
||||
final EmbeddableTypeImpl.InFlightAccess<Y> inFlightAccess = embeddableType.getInFlightAccess();
|
||||
final Iterator<Property> subProperties = component.getPropertyIterator();
|
||||
|
|
|
@ -20,11 +20,9 @@ import javax.persistence.metamodel.IdentifiableType;
|
|||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.persistence.metamodel.Type;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.EntityManagerMessageLogger;
|
||||
import org.hibernate.internal.HEMLogging;
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
|
@ -52,7 +50,6 @@ import org.hibernate.metamodel.model.domain.internal.MappedSuperclassTypeImpl;
|
|||
import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl;
|
||||
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.type.EmbeddedComponentType;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
@ -414,8 +411,8 @@ public class MetadataContext {
|
|||
private void applyIdClassMetadata(Component identifier, Component idClass) {
|
||||
final JavaTypeDescriptorRegistry registry = getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry();
|
||||
final Class componentClass = identifier.getComponentClass();
|
||||
final JavaTypeDescriptor javaTypeDescriptor = registry.resolveDescriptor( componentClass );
|
||||
final Class<?> componentClass = identifier.getComponentClass();
|
||||
final JavaTypeDescriptor<?> javaTypeDescriptor = registry.resolveDescriptor( componentClass );
|
||||
|
||||
final EmbeddableRepresentationStrategy representationStrategy = getTypeConfiguration()
|
||||
.getMetadataBuildingContext()
|
||||
|
@ -423,22 +420,23 @@ public class MetadataContext {
|
|||
.getManagedTypeRepresentationResolver()
|
||||
.resolveStrategy( idClass, getRuntimeModelCreationContext() );
|
||||
|
||||
final EmbeddableTypeImpl embeddableType = new EmbeddableTypeImpl<>(
|
||||
final EmbeddableTypeImpl<?> embeddableType = new EmbeddableTypeImpl<>(
|
||||
javaTypeDescriptor,
|
||||
representationStrategy,
|
||||
false,
|
||||
getJpaMetamodel()
|
||||
);
|
||||
registerEmbeddableType( embeddableType, idClass );
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private <X> void applyIdMetadata(MappedSuperclass mappingType, MappedSuperclassDomainType<X> jpaMappingType) {
|
||||
if ( mappingType.hasIdentifierProperty() ) {
|
||||
final Property declaredIdentifierProperty = mappingType.getDeclaredIdentifierProperty();
|
||||
if ( declaredIdentifierProperty != null ) {
|
||||
final SingularPersistentAttribute<X, Object> attribute = attributeFactory.buildIdAttribute( jpaMappingType, declaredIdentifierProperty );
|
||||
//noinspection unchecked
|
||||
( ( AttributeContainer) jpaMappingType ).getInFlightAccess().applyIdAttribute(
|
||||
attributeFactory.buildIdAttribute( jpaMappingType, declaredIdentifierProperty )
|
||||
);
|
||||
( ( AttributeContainer) jpaMappingType ).getInFlightAccess().applyIdAttribute( attribute );
|
||||
}
|
||||
}
|
||||
//a MappedSuperclass can have no identifier if the id is set below in the hierarchy
|
||||
|
|
|
@ -8,11 +8,13 @@ package org.hibernate.metamodel.mapping;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Contract for things at the domain/mapping level that can be bound into a JDBC
|
||||
|
|
|
@ -8,23 +8,25 @@ package org.hibernate.metamodel.mapping;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.CascadeStyle;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.MutableInteger;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.BasicValue;
|
||||
|
@ -34,13 +36,10 @@ import org.hibernate.mapping.IndexedConsumer;
|
|||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.SelectionMappingsImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.SelectionMappingsImpl;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
|
@ -98,14 +97,35 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
);
|
||||
|
||||
creationProcess.registerInitializationCallback(
|
||||
"EmbeddableMappingType(" + bootDescriptor.getRoleName() + ")#finishInitialization",
|
||||
() -> mappingType.finishInitialization(
|
||||
bootDescriptor,
|
||||
compositeType,
|
||||
rootTableExpression,
|
||||
rootTableKeyColumnNames,
|
||||
creationProcess
|
||||
)
|
||||
"EmbeddableMappingType(" + mappingType.getNavigableRole().getFullPath() + ")#finishInitialization",
|
||||
() -> {
|
||||
try {
|
||||
final boolean finished = mappingType.finishInitialization(
|
||||
bootDescriptor,
|
||||
compositeType,
|
||||
rootTableExpression,
|
||||
rootTableKeyColumnNames,
|
||||
creationProcess
|
||||
);
|
||||
|
||||
if ( finished ) {
|
||||
return finished;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
MappingModelCreationLogger.LOGGER.debugf(
|
||||
e,
|
||||
"(DEBUG) Error finalizing EmbeddableMappingType(%s)",
|
||||
mappingType.embeddedRole
|
||||
);
|
||||
}
|
||||
|
||||
MappingModelCreationLogger.LOGGER.debugf(
|
||||
"EmbeddableMappingType(%s) finalization was not able to complete successfully",
|
||||
mappingType.embeddedRole
|
||||
);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
return mappingType;
|
||||
|
@ -116,9 +136,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
// private final Map<String,AttributeMapping> attributeMappings = new TreeMap<>();
|
||||
private final List<AttributeMapping> attributes = new ArrayList<>();
|
||||
private final Map<String, AttributeMapping> attributeMap = new HashMap<>();
|
||||
private final List<AttributeMapping> attributeMappings = new ArrayList<>();
|
||||
private SelectionMappings selectionMappings;
|
||||
|
||||
private final EmbeddableValuedModelPart valueMapping;
|
||||
|
@ -156,18 +174,20 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
MappingModelCreationProcess creationProcess) {
|
||||
final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory();
|
||||
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
final Dialect dialect = jdbcEnvironment.getDialect();
|
||||
|
||||
final String baseTableExpression = valueMapping.getContainingTableExpression();
|
||||
final Dialect dialect = creationProcess.getCreationContext().getSessionFactory().getJdbcServices().getDialect();
|
||||
final Type[] subtypes = compositeType.getSubtypes();
|
||||
|
||||
int attributeIndex = 0;
|
||||
int columnPosition = 0;
|
||||
|
||||
//noinspection unchecked
|
||||
final Iterator<Property> propertyIterator = bootDescriptor.getPropertyIterator();
|
||||
while ( propertyIterator.hasNext() ) {
|
||||
final Property bootPropertyDescriptor = propertyIterator.next();
|
||||
final AttributeMapping attributeMapping;
|
||||
|
||||
final Type subtype = subtypes[attributeIndex];
|
||||
if ( subtype instanceof BasicType ) {
|
||||
|
@ -185,7 +205,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
if ( selectable instanceof Column ) {
|
||||
containingTableExpression = getTableIdentifierExpression(
|
||||
( (Column) selectable ).getValue().getTable(),
|
||||
creationProcess
|
||||
jdbcEnvironment
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
@ -197,7 +217,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
columnExpression = rootTableKeyColumnNames[columnPosition];
|
||||
}
|
||||
|
||||
BasicValuedSingularAttributeMapping attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping(
|
||||
attributeMapping = MappingModelCreationHelper.buildBasicAttributeMapping(
|
||||
bootPropertyDescriptor.getName(),
|
||||
valueMapping.getNavigableRole().append( bootPropertyDescriptor.getName() ),
|
||||
attributeIndex,
|
||||
|
@ -206,14 +226,14 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
(BasicType<?>) subtype,
|
||||
containingTableExpression,
|
||||
columnExpression,
|
||||
selectable.isFormula(),
|
||||
false,
|
||||
selectable.getCustomReadExpression(),
|
||||
selectable.getCustomWriteExpression(),
|
||||
representationStrategy.resolvePropertyAccess( bootPropertyDescriptor ),
|
||||
compositeType.getCascadeStyle( attributeIndex ),
|
||||
creationProcess
|
||||
);
|
||||
addAttribute( attributeMapping );
|
||||
|
||||
columnPosition++;
|
||||
}
|
||||
else if ( subtype instanceof AnyType ) {
|
||||
|
@ -302,7 +322,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
}
|
||||
};
|
||||
|
||||
DiscriminatedAssociationAttributeMapping attributeMapping = new DiscriminatedAssociationAttributeMapping(
|
||||
attributeMapping = new DiscriminatedAssociationAttributeMapping(
|
||||
valueMapping.getNavigableRole().append( bootPropertyDescriptor.getName() ),
|
||||
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Object.class ),
|
||||
this,
|
||||
|
@ -315,7 +335,6 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
bootValueMapping,
|
||||
creationProcess
|
||||
);
|
||||
addAttribute( attributeMapping );
|
||||
}
|
||||
else if ( subtype instanceof CompositeType ) {
|
||||
final CompositeType subCompositeType = (CompositeType) subtype;
|
||||
|
@ -332,7 +351,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
System.arraycopy( rootTableKeyColumnNames, columnPosition, subRootTableKeyColumnNames, 0, columnSpan );
|
||||
}
|
||||
|
||||
EmbeddedAttributeMapping attributeMapping = MappingModelCreationHelper.buildEmbeddedAttributeMapping(
|
||||
attributeMapping = MappingModelCreationHelper.buildEmbeddedAttributeMapping(
|
||||
bootPropertyDescriptor.getName(),
|
||||
attributeIndex,
|
||||
bootPropertyDescriptor,
|
||||
|
@ -344,42 +363,51 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
compositeType.getCascadeStyle( attributeIndex ),
|
||||
creationProcess
|
||||
);
|
||||
addAttribute( attributeMapping );
|
||||
|
||||
columnPosition += columnSpan;
|
||||
}
|
||||
else {
|
||||
final EntityPersister entityPersister = creationProcess
|
||||
.getEntityPersister( bootDescriptor.getOwner().getEntityName() );
|
||||
if ( subtype instanceof CollectionType ) {
|
||||
PluralAttributeMapping attributeMapping = MappingModelCreationHelper.buildPluralAttributeMapping(
|
||||
bootPropertyDescriptor.getName(),
|
||||
attributeIndex,
|
||||
bootPropertyDescriptor,
|
||||
entityPersister,
|
||||
representationStrategy.resolvePropertyAccess( bootPropertyDescriptor ),
|
||||
compositeType.getCascadeStyle( attributeIndex ),
|
||||
compositeType.getFetchMode( attributeIndex ),
|
||||
creationProcess
|
||||
);
|
||||
addAttribute( attributeMapping );
|
||||
}
|
||||
else if ( subtype instanceof EntityType ) {
|
||||
final int columnSpan = subtype.getColumnSpan( sessionFactory );
|
||||
final ToOneAttributeMapping attributeMapping = MappingModelCreationHelper.buildSingularAssociationAttributeMapping(
|
||||
bootPropertyDescriptor.getName(),
|
||||
valueMapping.getNavigableRole().append( bootPropertyDescriptor.getName() ),
|
||||
attributeIndex,
|
||||
bootPropertyDescriptor,
|
||||
entityPersister,
|
||||
(EntityType) subtype,
|
||||
getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ),
|
||||
compositeType.getCascadeStyle( attributeIndex ),
|
||||
creationProcess
|
||||
);
|
||||
addAttribute( attributeMapping );
|
||||
columnPosition += columnSpan;
|
||||
}
|
||||
else if ( subtype instanceof CollectionType ) {
|
||||
final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner().getEntityName() );
|
||||
|
||||
attributeMapping = MappingModelCreationHelper.buildPluralAttributeMapping(
|
||||
bootPropertyDescriptor.getName(),
|
||||
attributeIndex,
|
||||
bootPropertyDescriptor,
|
||||
entityPersister,
|
||||
representationStrategy.resolvePropertyAccess( bootPropertyDescriptor ),
|
||||
compositeType.getCascadeStyle( attributeIndex),
|
||||
compositeType.getFetchMode( attributeIndex ),
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
else if ( subtype instanceof EntityType ) {
|
||||
final EntityPersister entityPersister = creationProcess.getEntityPersister( bootDescriptor.getOwner().getEntityName() );
|
||||
|
||||
attributeMapping = MappingModelCreationHelper.buildSingularAssociationAttributeMapping(
|
||||
bootPropertyDescriptor.getName(),
|
||||
valueMapping.getNavigableRole().append( bootPropertyDescriptor.getName() ),
|
||||
attributeIndex,
|
||||
bootPropertyDescriptor,
|
||||
entityPersister,
|
||||
(EntityType) subtype,
|
||||
getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ),
|
||||
compositeType.getCascadeStyle( attributeIndex ),
|
||||
creationProcess
|
||||
);
|
||||
columnPosition += bootPropertyDescriptor.getColumnSpan();
|
||||
}
|
||||
else {
|
||||
throw new MappingException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Unable to determine attribute nature : %s#%s",
|
||||
bootDescriptor.getOwner().getEntityName(),
|
||||
bootPropertyDescriptor.getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
addAttribute( attributeMapping );
|
||||
|
||||
attributeIndex++;
|
||||
}
|
||||
|
@ -392,11 +420,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
return true;
|
||||
}
|
||||
|
||||
private static String getTableIdentifierExpression(Table table, MappingModelCreationProcess creationProcess) {
|
||||
final JdbcEnvironment jdbcEnvironment = creationProcess.getCreationContext()
|
||||
.getMetadata()
|
||||
.getDatabase()
|
||||
.getJdbcEnvironment();
|
||||
private static String getTableIdentifierExpression(Table table, JdbcEnvironment jdbcEnvironment) {
|
||||
return jdbcEnvironment
|
||||
.getQualifiedObjectNameFormatter().format(
|
||||
table.getQualifiedTableName(),
|
||||
|
@ -410,18 +434,16 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
}
|
||||
|
||||
private void addAttribute(AttributeMapping attributeMapping) {
|
||||
if ( attributeMap.put( attributeMapping.getAttributeName(), attributeMapping ) == null ) {
|
||||
attributes.add( attributeMapping );
|
||||
}
|
||||
else {
|
||||
for ( ListIterator<AttributeMapping> iterator = attributes.listIterator(); iterator.hasNext(); ) {
|
||||
final AttributeMapping existingMapping = iterator.next();
|
||||
if ( existingMapping.getAttributeName().equals( attributeMapping.getAttributeName() ) ) {
|
||||
iterator.set( attributeMapping );
|
||||
break;
|
||||
}
|
||||
// check if we've already seen this attribute...
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
final AttributeMapping previous = attributeMappings.get( i );
|
||||
if ( attributeMapping.getAttributeName().equals( previous.getAttributeName() ) ) {
|
||||
attributeMappings.set( i, attributeMapping );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
attributeMappings.add( attributeMapping );
|
||||
}
|
||||
|
||||
public EmbeddableValuedModelPart getEmbeddedValueMapping() {
|
||||
|
@ -471,7 +493,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
|
||||
@Override
|
||||
public int getNumberOfFetchables() {
|
||||
return attributeMap.size();
|
||||
return attributeMappings.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -506,14 +528,45 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
if ( domainValue instanceof Object[] ) {
|
||||
final Object[] values = (Object[]) domainValue;
|
||||
assert values.length == attributeMappings.size();
|
||||
|
||||
final MutableInteger positionRef = new MutableInteger();
|
||||
attributeMappings.forEach(
|
||||
(attributeMapping) -> {
|
||||
final int position = positionRef.getAndIncrement();
|
||||
final Object attributeValue = values[ position ];
|
||||
attributeMapping.breakDownJdbcValues( attributeValue, valueConsumer, session );
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
attributeMappings.forEach(
|
||||
(attributeMapping) -> {
|
||||
final Object attributeValue = attributeMapping.getPropertyAccess().getGetter().get( domainValue );
|
||||
attributeMapping.breakDownJdbcValues( attributeValue, valueConsumer, session );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
Object[] result = new Object[attributes.size()];
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
AttributeMapping mapping = attributes.get( i );
|
||||
final Collection<AttributeMapping> attributeMappings = getAttributeMappings();
|
||||
|
||||
Object[] result = new Object[attributeMappings.size()];
|
||||
int i = 0;
|
||||
final Iterator<AttributeMapping> iterator = attributeMappings.iterator();
|
||||
while ( iterator.hasNext() ) {
|
||||
AttributeMapping mapping = iterator.next();
|
||||
Object o = mapping.getPropertyAccess().getGetter().get( value );
|
||||
result[i] = mapping.disassemble( o, session );
|
||||
i++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -525,8 +578,9 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
JdbcValuesConsumer consumer,
|
||||
SharedSessionContractImplementor session) {
|
||||
int span = 0;
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
final AttributeMapping attributeMapping = attributes.get( i );
|
||||
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
final AttributeMapping attributeMapping = attributeMappings.get( i );
|
||||
final Object o = attributeMapping.getPropertyAccess().getGetter().get( value );
|
||||
span += attributeMapping.forEachJdbcValue( o, clause, span + offset, consumer, session );
|
||||
}
|
||||
|
@ -542,8 +596,8 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
SharedSessionContractImplementor session) {
|
||||
final Object[] values = (Object[]) value;
|
||||
int span = 0;
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
final AttributeMapping mapping = attributes.get( i );
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
final AttributeMapping mapping = attributeMappings.get( i );
|
||||
span += mapping.forEachDisassembledJdbcValue( values[i], clause, span + offset, valuesConsumer, session );
|
||||
}
|
||||
return span;
|
||||
|
@ -566,34 +620,40 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
|
||||
@Override
|
||||
public int getNumberOfAttributeMappings() {
|
||||
return attributeMap.size();
|
||||
return attributeMappings.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeMapping findAttributeMapping(String name) {
|
||||
return attributeMap.get( name );
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
final AttributeMapping attr = attributeMappings.get( i );
|
||||
if ( name.equals( attr.getAttributeName() ) ) {
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AttributeMapping> getAttributeMappings() {
|
||||
return attributes;
|
||||
return attributeMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachAttributeMapping(IndexedConsumer<AttributeMapping> consumer) {
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
consumer.accept( i, attributes.get( i ) );
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
consumer.accept( i, attributeMappings.get( i ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttributeMappings(Consumer<AttributeMapping> action) {
|
||||
attributes.forEach( action );
|
||||
attributeMappings.forEach( action );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
|
||||
return attributeMap.get( name );
|
||||
return findAttributeMapping( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -605,8 +665,8 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectionMappi
|
|||
|
||||
public void setPropertyValues(Object compositeInstance, Object[] resolvedValues) {
|
||||
// todo (6.0) : reflection optimizer...
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
attributes.get( i )
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
attributeMappings.get( i )
|
||||
.getAttributeMetadataAccess()
|
||||
.resolveAttributeMetadata( null )
|
||||
.getPropertyAccess()
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -16,13 +17,15 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.loader.ast.spi.Loadable;
|
||||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
|
@ -177,6 +180,14 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
|
|||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
default EntityMappingType getRootEntityDescriptor() {
|
||||
final EntityMappingType superMappingType = getSuperMappingType();
|
||||
if ( superMappingType == null ) {
|
||||
return this;
|
||||
}
|
||||
return superMappingType.getRootEntityDescriptor();
|
||||
}
|
||||
|
||||
interface ConstraintOrderedTableConsumer {
|
||||
void consume(String tableExpression, Supplier<Consumer<SelectionConsumer>> tableKeyColumnVisitationSupplier);
|
||||
}
|
||||
|
@ -215,6 +226,23 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Loading
|
||||
|
||||
|
||||
/**
|
||||
* Access to performing natural-id database selection. This is per-entity in the hierarchy
|
||||
*/
|
||||
NaturalIdLoader<?> getNaturalIdLoader();
|
||||
|
||||
/**
|
||||
* Access to performing multi-value natural-id database selection. This is per-entity in the hierarchy
|
||||
*/
|
||||
MultiNaturalIdLoader<?> getMultiNaturalIdLoader();
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Loadable
|
||||
|
||||
|
@ -239,18 +267,16 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
|
|||
String explicitSourceAlias,
|
||||
boolean canUseInnerJoins,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
return getEntityPersister().createRootTableGroup(
|
||||
navigablePath,
|
||||
explicitSourceAlias,
|
||||
canUseInnerJoins,
|
||||
lockMode,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
additionalPredicateCollectorAccess,
|
||||
creationState,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
|
|
@ -104,6 +104,13 @@ public interface ModelPart extends MappingModelExpressable {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface JdbcValueConsumer {
|
||||
void consume(Object value, SelectionMapping jdbcValueMapping);
|
||||
}
|
||||
|
||||
void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session);
|
||||
|
||||
EntityMappingType findContainingEntityMapping();
|
||||
|
||||
default boolean areEqual(Object one, Object other, SharedSessionContractImplementor session) {
|
||||
|
|
|
@ -8,7 +8,7 @@ package org.hibernate.metamodel.mapping;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
|
@ -28,7 +28,18 @@ public interface NaturalIdMapping extends VirtualModelPart {
|
|||
* Whether the natural-id is immutable. This is the same as saying that none of
|
||||
* the attributes are mutable
|
||||
*/
|
||||
boolean isImmutable();
|
||||
boolean isMutable();
|
||||
|
||||
@Override
|
||||
default String getPartName() {
|
||||
return PART_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to the natural-id's L2 cache access. Returns null if the natural-id is not
|
||||
* configured for caching
|
||||
*/
|
||||
NaturalIdDataAccess getCacheAccess();
|
||||
|
||||
/**
|
||||
* Verify the natural-id value(s) we are about to flush to the database
|
||||
|
@ -39,14 +50,6 @@ public interface NaturalIdMapping extends VirtualModelPart {
|
|||
Object[] loadedState,
|
||||
SharedSessionContractImplementor session);
|
||||
|
||||
@Override
|
||||
default String getPartName() {
|
||||
return PART_NAME;
|
||||
}
|
||||
|
||||
NaturalIdLoader getNaturalIdLoader();
|
||||
MultiNaturalIdLoader getMultiNaturalIdLoader();
|
||||
|
||||
/**
|
||||
* Given an array of "full entity state", extract the normalized natural id representation
|
||||
*
|
||||
|
@ -65,10 +68,15 @@ public interface NaturalIdMapping extends VirtualModelPart {
|
|||
*/
|
||||
Object extractNaturalIdValues(Object entity, SharedSessionContractImplementor session);
|
||||
|
||||
|
||||
/**
|
||||
* Normalize an incoming (user supplied) natural-id value.
|
||||
* Normalize a user-provided natural-id value into the representation Hibernate uses internally
|
||||
*
|
||||
* @param incoming The user-supplied value
|
||||
*
|
||||
* @return The normalized, internal representation
|
||||
*/
|
||||
Object normalizeIncomingValue(Object incoming, SharedSessionContractImplementor session);
|
||||
Object normalizeInput(Object incoming, SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Validates a natural id value(s) for the described natural-id based on the expected internal representation
|
||||
|
@ -83,4 +91,14 @@ public interface NaturalIdMapping extends VirtualModelPart {
|
|||
* @return The hash-code
|
||||
*/
|
||||
int calculateHashCode(Object value, SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Make a loader capable of loading a single entity by natural-id
|
||||
*/
|
||||
NaturalIdLoader<?> makeLoader(EntityMappingType entityDescriptor);
|
||||
|
||||
/**
|
||||
* Make a loader capable of loading multiple entities by natural-id
|
||||
*/
|
||||
MultiNaturalIdLoader<?> makeMultiLoader(EntityMappingType entityDescriptor);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
|
@ -15,26 +16,39 @@ import org.hibernate.metamodel.model.domain.NavigableRole;
|
|||
*/
|
||||
public abstract class AbstractNaturalIdMapping implements NaturalIdMapping {
|
||||
private final EntityMappingType declaringType;
|
||||
private final String cacheRegionName;
|
||||
private final boolean mutable;
|
||||
private final NaturalIdDataAccess cachesAccess;
|
||||
|
||||
private final NavigableRole role;
|
||||
|
||||
public AbstractNaturalIdMapping(EntityMappingType declaringType, String cacheRegionName) {
|
||||
public AbstractNaturalIdMapping(EntityMappingType declaringType, boolean mutable) {
|
||||
this.declaringType = declaringType;
|
||||
this.cacheRegionName = cacheRegionName;
|
||||
this.mutable = mutable;
|
||||
|
||||
this.cachesAccess = declaringType.getEntityPersister().getNaturalIdCacheAccessStrategy();
|
||||
|
||||
this.role = declaringType.getNavigableRole().append( PART_NAME );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableRole getNavigableRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public EntityMappingType getDeclaringType() {
|
||||
return declaringType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableRole getNavigableRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return mutable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdDataAccess getCacheAccess() {
|
||||
return cachesAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return declaringType;
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
|
||||
|
@ -122,6 +123,11 @@ public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions
|
|||
return navigableRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return declaringType.findContainingEntityMapping();
|
||||
|
|
|
@ -202,6 +202,11 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
|
|||
return getJdbcTypeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
|
||||
action.accept( offset, jdbcMapping );
|
||||
|
|
|
@ -134,6 +134,11 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
|
|||
return getJdbcTypeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return entityPersister;
|
||||
|
|
|
@ -261,6 +261,11 @@ public class BasicValuedCollectionPart
|
|||
return getJdbcTypeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachDisassembledJdbcValue(
|
||||
Object value,
|
||||
|
|
|
@ -268,4 +268,9 @@ public class BasicValuedSingularAttributeMapping
|
|||
consumer.accept( offset, this );
|
||||
return getJdbcTypeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( disassemble( domainValue, session ), this );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -112,6 +113,11 @@ public class CollectionIdentifierDescriptorImpl implements CollectionIdentifierD
|
|||
return navigableRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return collectionDescriptor.getAttributeMapping().findContainingEntityMapping();
|
||||
|
|
|
@ -11,72 +11,100 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.loader.NaturalIdPostLoadListener;
|
||||
import org.hibernate.loader.NaturalIdPreLoadListener;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.loader.ast.internal.CompoundNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.internal.MultiNaturalIdLoaderStandard;
|
||||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.FetchableContainer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
* Multi-attribute NaturalIdMapping implementation
|
||||
*/
|
||||
public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implements MappingType {
|
||||
public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implements MappingType, FetchableContainer {
|
||||
|
||||
// todo (6.0) : create a composite MappingType for this descriptor's Object[]?
|
||||
|
||||
private final List<SingularAttributeMapping> attributes;
|
||||
private final List<JdbcMapping> jdbcMappings;
|
||||
private final boolean immutable;
|
||||
private final JavaTypeDescriptor<?> jtd;
|
||||
|
||||
private final NaturalIdLoader<?> loader;
|
||||
private final MultiNaturalIdLoader<?> multiLoader;
|
||||
private List<JdbcMapping> jdbcMappings;
|
||||
|
||||
public CompoundNaturalIdMapping(
|
||||
EntityMappingType declaringType,
|
||||
List<SingularAttributeMapping> attributes,
|
||||
String cacheRegionName,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
super( declaringType, cacheRegionName );
|
||||
super( declaringType, isMutable( declaringType, attributes, creationProcess ) );
|
||||
this.attributes = attributes;
|
||||
|
||||
boolean anyMutable = false;
|
||||
final List<JdbcMapping> jdbcMappings = new ArrayList<>();
|
||||
jtd = creationProcess.getCreationContext().getTypeConfiguration().getJavaTypeDescriptorRegistry().getDescriptor(
|
||||
Object[].class
|
||||
);
|
||||
|
||||
creationProcess.registerInitializationCallback(
|
||||
"Determine compound natural-id JDBC mappings ( " + declaringType.getEntityName() + ")",
|
||||
() -> {
|
||||
final List<JdbcMapping> jdbcMappings = new ArrayList<>();
|
||||
attributes.forEach(
|
||||
(attribute) -> attribute.forEachJdbcType(
|
||||
(index, jdbcMapping) -> jdbcMappings.add( jdbcMapping )
|
||||
)
|
||||
);
|
||||
this.jdbcMappings = jdbcMappings;
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean isMutable(
|
||||
EntityMappingType entityDescriptor,
|
||||
List<SingularAttributeMapping> attributes,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
final SingularAttributeMapping attributeMapping = attributes.get( i );
|
||||
attributeMapping.forEachJdbcType( (index, jdbcMapping) -> jdbcMappings.add( jdbcMapping ) );
|
||||
anyMutable = anyMutable || attributeMapping.getAttributeMetadataAccess().resolveAttributeMetadata( null ).isUpdatable();
|
||||
}
|
||||
this.jdbcMappings = jdbcMappings;
|
||||
this.immutable = ! anyMutable;
|
||||
final StateArrayContributorMetadataAccess metadataAccess = attributeMapping.getAttributeMetadataAccess();
|
||||
|
||||
loader = new CompoundNaturalIdLoader<>(
|
||||
this,
|
||||
NaturalIdPreLoadListener.NO_OP,
|
||||
NaturalIdPostLoadListener.NO_OP,
|
||||
declaringType,
|
||||
creationProcess
|
||||
);
|
||||
multiLoader = new MultiNaturalIdLoaderStandard<>( declaringType, creationProcess );
|
||||
if ( ! metadataAccess.resolveAttributeMetadata( entityDescriptor ).isUpdatable() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -113,7 +141,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
|
||||
@Override
|
||||
@SuppressWarnings( "rawtypes" )
|
||||
public Object[] normalizeIncomingValue(Object incoming, SharedSessionContractImplementor session) {
|
||||
public Object[] normalizeInput(Object incoming, SharedSessionContractImplementor session) {
|
||||
if ( incoming instanceof Object[] ) {
|
||||
return (Object[]) incoming;
|
||||
}
|
||||
|
@ -160,7 +188,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
|
||||
@Override
|
||||
public void verifyFlushState(Object id, Object[] currentState, Object[] loadedState, SharedSessionContractImplementor session) {
|
||||
if ( ! immutable ) {
|
||||
if ( isMutable() ) {
|
||||
// EARLY EXIT!!!
|
||||
// the natural id is mutable (!immutable), no need to do the checks
|
||||
return;
|
||||
|
@ -216,18 +244,13 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmutable() {
|
||||
return immutable;
|
||||
public NaturalIdLoader<?> makeLoader(EntityMappingType entityDescriptor) {
|
||||
return new CompoundNaturalIdLoader<>( this, entityDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoader<?> getNaturalIdLoader() {
|
||||
return loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiNaturalIdLoader<?> getMultiNaturalIdLoader() {
|
||||
return multiLoader;
|
||||
public MultiNaturalIdLoader<?> makeMultiLoader(EntityMappingType entityDescriptor) {
|
||||
return new MultiNaturalIdLoaderStandard<>( entityDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -251,7 +274,28 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
|
||||
@Override
|
||||
public <T> DomainResult<T> createDomainResult(NavigablePath navigablePath, TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
assert navigablePath.getLocalName().equals( NaturalIdMapping.PART_NAME );
|
||||
|
||||
final SessionFactoryImplementor sessionFactory = creationState.getSqlAstCreationState().getCreationContext().getSessionFactory();
|
||||
|
||||
final JavaTypeDescriptor<Object[]> jtd = sessionFactory
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.getDescriptor( Object[].class );
|
||||
|
||||
// register the table group under `...{natural-id}` as well
|
||||
creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
|
||||
navigablePath,
|
||||
(np) -> tableGroup
|
||||
);
|
||||
|
||||
return (DomainResult<T>) new DomainResultImpl(
|
||||
navigablePath,
|
||||
this,
|
||||
jtd,
|
||||
resultVariable,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -268,6 +312,39 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
if ( domainValue == null ) {
|
||||
attributes.forEach(
|
||||
attributeMapping -> attributeMapping.breakDownJdbcValues( null, valueConsumer, session )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
assert domainValue instanceof Object[];
|
||||
|
||||
final Object[] values = (Object[]) domainValue;
|
||||
assert values.length == attributes.size();
|
||||
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
final SingularAttributeMapping attributeMapping = attributes.get( i );
|
||||
final Object value = values[ i ];
|
||||
if ( attributeMapping instanceof ToOneAttributeMapping ) {
|
||||
final ToOneAttributeMapping toOne = (ToOneAttributeMapping) attributeMapping;
|
||||
final ForeignKeyDescriptor fKDescriptor = toOne.getForeignKeyDescriptor();
|
||||
|
||||
final EntityMappingType associatedEntityMapping = toOne.getEntityMappingType();
|
||||
final EntityIdentifierMapping associatedEntityMappingIdentifierMapping = associatedEntityMapping.getIdentifierMapping();
|
||||
|
||||
final Object keyValue = value == null ? null : associatedEntityMappingIdentifierMapping.getIdentifier( value, session );
|
||||
fKDescriptor.breakDownJdbcValues( keyValue, valueConsumer, session );
|
||||
}
|
||||
else {
|
||||
attributeMapping.breakDownJdbcValues( value, valueConsumer, session );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachSelection(int offset, SelectionConsumer consumer) {
|
||||
int span = 0;
|
||||
|
@ -355,4 +432,222 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
|
|||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfFetchables() {
|
||||
return attributes.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
|
||||
for ( int i = 0; i < attributes.size(); i++ ) {
|
||||
if ( name.equals( attributes.get( i ).getAttributeName() ) ) {
|
||||
return attributes.get( i );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubParts(Consumer<ModelPart> consumer, EntityMappingType treatTargetType) {
|
||||
attributes.forEach( consumer );
|
||||
}
|
||||
|
||||
|
||||
public static class DomainResultImpl implements DomainResult<Object[]>, FetchParent {
|
||||
private final NavigablePath navigablePath;
|
||||
private final CompoundNaturalIdMapping naturalIdMapping;
|
||||
private final JavaTypeDescriptor<Object[]> arrayJtd;
|
||||
|
||||
private final List<Fetch> fetches;
|
||||
|
||||
private final String resultVariable;
|
||||
|
||||
public DomainResultImpl(
|
||||
NavigablePath navigablePath,
|
||||
CompoundNaturalIdMapping naturalIdMapping,
|
||||
JavaTypeDescriptor<Object[]> arrayJtd,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
this.navigablePath = navigablePath;
|
||||
this.naturalIdMapping = naturalIdMapping;
|
||||
this.arrayJtd = arrayJtd;
|
||||
this.resultVariable = resultVariable;
|
||||
|
||||
this.fetches = creationState.visitFetches( this );
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// DomainResult
|
||||
|
||||
@Override
|
||||
public String getResultVariable() {
|
||||
return resultVariable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<Object[]> createResultAssembler(AssemblerCreationState creationState) {
|
||||
return new AssemblerImpl(
|
||||
fetches,
|
||||
navigablePath,
|
||||
naturalIdMapping,
|
||||
arrayJtd,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor<Object[]> getResultJavaTypeDescriptor() {
|
||||
return arrayJtd;
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// FetchParent
|
||||
|
||||
@Override
|
||||
public FetchableContainer getReferencedMappingContainer() {
|
||||
return getReferencedMappingType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchableContainer getReferencedMappingType() {
|
||||
return naturalIdMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Fetch> getFetches() {
|
||||
return fetches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch findFetch(Fetchable fetchable) {
|
||||
assert fetchable != null;
|
||||
|
||||
for ( int i = 0; i < fetches.size(); i++ ) {
|
||||
final Fetch fetch = fetches.get( i );
|
||||
if ( fetchable.equals( fetch.getFetchedMapping() ) ) {
|
||||
return fetch;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class AssemblerImpl implements DomainResultAssembler<Object[]> {
|
||||
private final NavigablePath navigablePath;
|
||||
private final CompoundNaturalIdMapping naturalIdMapping;
|
||||
private final JavaTypeDescriptor<Object[]> jtd;
|
||||
|
||||
private final List<DomainResultAssembler<?>> subAssemblers;
|
||||
|
||||
private AssemblerImpl(
|
||||
List<Fetch> fetches,
|
||||
NavigablePath navigablePath,
|
||||
CompoundNaturalIdMapping naturalIdMapping,
|
||||
JavaTypeDescriptor<Object[]> jtd,
|
||||
AssemblerCreationState creationState) {
|
||||
this.navigablePath = navigablePath;
|
||||
this.naturalIdMapping = naturalIdMapping;
|
||||
this.jtd = jtd;
|
||||
|
||||
// we don't even register the Initializer here... its really no-op.
|
||||
// we just "need it" as an impl detail for handling Fetches
|
||||
final InitializerImpl initializer = new InitializerImpl( navigablePath, naturalIdMapping );
|
||||
|
||||
this.subAssemblers = CollectionHelper.arrayList( fetches.size() );
|
||||
for ( int i = 0; i < fetches.size(); i++ ) {
|
||||
final Fetch fetch = fetches.get( i );
|
||||
final DomainResultAssembler<?> fetchAssembler = fetch.createAssembler( initializer, creationState );
|
||||
subAssemblers.add( fetchAssembler );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] assemble(
|
||||
RowProcessingState rowProcessingState,
|
||||
JdbcValuesSourceProcessingOptions options) {
|
||||
final Object[] result = new Object[ subAssemblers.size() ];
|
||||
for ( int i = 0; i < subAssemblers.size(); i++ ) {
|
||||
result[ i ] = subAssemblers.get( i ).assemble( rowProcessingState, options );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor<Object[]> getAssembledJavaTypeDescriptor() {
|
||||
return jtd;
|
||||
}
|
||||
}
|
||||
|
||||
private static class InitializerImpl implements FetchParentAccess {
|
||||
private final NavigablePath navigablePath;
|
||||
private final CompoundNaturalIdMapping naturalIdMapping;
|
||||
|
||||
public InitializerImpl(NavigablePath navigablePath, CompoundNaturalIdMapping naturalIdMapping) {
|
||||
this.navigablePath = navigablePath;
|
||||
this.naturalIdMapping = naturalIdMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParentAccess findFirstEntityDescriptorAccess() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getFetchParentInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart getInitializedPart() {
|
||||
return naturalIdMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getInitializedInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> resolvedParentConsumer) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.engine.FetchStrategy;
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.mapping.Property;
|
||||
|
@ -135,6 +136,11 @@ public class DiscriminatedAssociationAttributeMapping
|
|||
return getDiscriminatorPart().getJdbcTypeCount() + getKeyPart().getJdbcTypeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
discriminatorMapping.getDiscriminatorPart().breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
|
||||
int span = getDiscriminatorPart().forEachJdbcType( offset, action );
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
|
@ -172,6 +173,11 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode
|
|||
return getDiscriminatorPart().getJdbcTypeCount() + getKeyPart().getJdbcTypeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
discriminatorMapping.getDiscriminatorPart().breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
|
||||
int span = getDiscriminatorPart().forEachJdbcType( offset, action );
|
||||
|
|
|
@ -11,18 +11,16 @@ import java.util.function.BiConsumer;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.FetchStrategy;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
|
||||
|
@ -126,6 +124,11 @@ public class EmbeddedAttributeMapping
|
|||
return getEmbeddableTypeDescriptor().forEachSelection( offset, consumer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
getEmbeddableTypeDescriptor().breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DomainResult<T> createDomainResult(
|
||||
NavigablePath navigablePath,
|
||||
|
|
|
@ -15,9 +15,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
|
@ -265,6 +263,11 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
|||
return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
getEmbeddableTypeDescriptor().breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchStyle getStyle() {
|
||||
return FetchStyle.JOIN;
|
||||
|
|
|
@ -14,14 +14,14 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectionMappings;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectionMappings;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
|
@ -396,6 +396,19 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor, Model
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
assert domainValue instanceof Object[];
|
||||
|
||||
final Object[] values = (Object[]) domainValue;
|
||||
|
||||
keySelectionMappings.forEachSelection(
|
||||
(selectionIndex, selectionMapping) -> {
|
||||
valueConsumer.consume( values[ selectionIndex ], selectionMapping );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -25,7 +24,8 @@ import org.hibernate.sql.ast.Clause;
|
|||
*
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class EmbeddedIdentifierMappingImpl extends AbstractCompositeIdentifierMapping
|
||||
public class EmbeddedIdentifierMappingImpl
|
||||
extends AbstractCompositeIdentifierMapping
|
||||
implements SingleAttributeIdentifierMapping {
|
||||
private final String name;
|
||||
private final PropertyAccess propertyAccess;
|
||||
|
@ -108,6 +108,11 @@ public class EmbeddedIdentifierMappingImpl extends AbstractCompositeIdentifierMa
|
|||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
getEmbeddableTypeDescriptor().breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachDisassembledJdbcValue(
|
||||
Object value,
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
|
@ -178,6 +179,11 @@ public class EntityCollectionPart
|
|||
return entityMappingType.forEachSelection( offset, consumer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
fkTargetModelPart.breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applySqlSelections(
|
||||
NavigablePath navigablePath,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -71,6 +72,11 @@ public class EntityDiscriminatorMappingImpl extends AbstractEntityDiscriminatorM
|
|||
return navigableRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return getEntityDescriptor();
|
||||
|
|
|
@ -8,11 +8,13 @@ package org.hibernate.metamodel.mapping.internal;
|
|||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.mapping.SelectionMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
|
@ -25,12 +27,13 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.type.JavaObjectType;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
* @author Nathan Xu
|
||||
*/
|
||||
public class EntityRowIdMappingImpl implements EntityRowIdMapping {
|
||||
public class EntityRowIdMappingImpl implements EntityRowIdMapping, SelectionMapping {
|
||||
private final String rowIdName;
|
||||
private final EntityMappingType declaringType;
|
||||
private final String tableExpression;
|
||||
|
@ -135,4 +138,38 @@ public class EntityRowIdMappingImpl implements EntityRowIdMapping {
|
|||
BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContainingTableExpression() {
|
||||
return tableExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectionExpression() {
|
||||
return rowIdName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCustomReadExpression() {
|
||||
return rowIdName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCustomWriteExpression() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFormula() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcMapping getJdbcMapping() {
|
||||
return StandardBasicTypes.INTEGER;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.function.BiConsumer;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.FetchStyle;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -213,6 +214,11 @@ public class EntityVersionMappingImpl implements EntityVersionMapping, FetchOpti
|
|||
selectionConsumer.accept( resolveSqlSelection( tableGroup, creationState ), getJdbcMapping() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
private SqlSelection resolveSqlSelection(TableGroup tableGroup, DomainResultCreationState creationState) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping.internal;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -77,6 +78,11 @@ public class JoinedSubclassDiscriminatorMappingImpl extends AbstractEntityDiscri
|
|||
return navigableRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType findContainingEntityMapping() {
|
||||
return getEntityDescriptor();
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
package org.hibernate.metamodel.mapping.internal;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
|
@ -116,6 +115,19 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
assert domainValue instanceof Object[];
|
||||
|
||||
final Object[] values = (Object[]) domainValue;
|
||||
assert values.length == idAttributeMappings.size();
|
||||
|
||||
for ( int i = 0; i < idAttributeMappings.size(); i++ ) {
|
||||
final SingularAttributeMapping attribute = idAttributeMappings.get( i );
|
||||
attribute.breakDownJdbcValues( values[ i ], valueConsumer, session );
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// EmbeddableValuedFetchable
|
||||
|
||||
|
|
|
@ -81,7 +81,8 @@ import org.jboss.logging.Logger;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PluralAttributeMappingImpl extends AbstractAttributeMapping
|
||||
public class PluralAttributeMappingImpl
|
||||
extends AbstractAttributeMapping
|
||||
implements PluralAttributeMapping, FetchOptions {
|
||||
private static final Logger log = Logger.getLogger( PluralAttributeMappingImpl.class );
|
||||
|
||||
|
@ -922,17 +923,16 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping
|
|||
String explicitSourceAlias,
|
||||
boolean canUseInnerJoins,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
if ( getCollectionDescriptor().isOneToMany() ) {
|
||||
return createOneToManyTableGroup(
|
||||
navigablePath,
|
||||
canUseInnerJoins,
|
||||
lockMode,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
creationState.getSqlAliasBaseGenerator(),
|
||||
creationState.getSqlExpressionResolver(),
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -941,8 +941,8 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping
|
|||
navigablePath,
|
||||
canUseInnerJoins,
|
||||
lockMode,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
creationState.getSqlAliasBaseGenerator(),
|
||||
creationState.getSqlExpressionResolver(),
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -1005,6 +1005,11 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping
|
|||
elementDescriptor.applySqlSelections( navigablePath, tableGroup, creationState, selectionConsumer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubParts(Consumer<ModelPart> consumer, EntityMappingType treatTargetType) {
|
||||
consumer.accept( elementDescriptor );
|
||||
|
|
|
@ -258,6 +258,27 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachDisassembledJdbcValue(
|
||||
Object value,
|
||||
Clause clause,
|
||||
int offset,
|
||||
JdbcValuesConsumer valuesConsumer,
|
||||
SharedSessionContractImplementor session) {
|
||||
valuesConsumer.consume( offset, value, getJdbcMapping() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
valueConsumer.consume( domainValue, keySelectionMapping );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int visitReferringColumns(int offset, SelectionConsumer consumer) {
|
||||
consumer.accept( offset, keySelectionMapping );
|
||||
|
|
|
@ -14,17 +14,15 @@ import java.util.function.BiConsumer;
|
|||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.loader.NaturalIdPostLoadListener;
|
||||
import org.hibernate.loader.NaturalIdPreLoadListener;
|
||||
import org.hibernate.loader.ast.internal.MultiNaturalIdLoaderStandard;
|
||||
import org.hibernate.loader.ast.internal.SimpleNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
|
@ -41,36 +39,20 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
|||
public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
|
||||
private final SingularAttributeMapping attribute;
|
||||
|
||||
private final boolean immutable;
|
||||
|
||||
private final SimpleNaturalIdLoader<?> loader;
|
||||
private final MultiNaturalIdLoader<?> multiLoader;
|
||||
|
||||
public SimpleNaturalIdMapping(
|
||||
SingularAttributeMapping attribute,
|
||||
EntityMappingType declaringType,
|
||||
String cacheRegionName,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
super( declaringType, cacheRegionName );
|
||||
this.attribute = attribute;
|
||||
|
||||
this.immutable = ! attribute.getAttributeMetadataAccess()
|
||||
.resolveAttributeMetadata( null )
|
||||
.isUpdatable();
|
||||
|
||||
this.loader = new SimpleNaturalIdLoader<>(
|
||||
this,
|
||||
NaturalIdPreLoadListener.NO_OP,
|
||||
NaturalIdPostLoadListener.NO_OP,
|
||||
super(
|
||||
declaringType,
|
||||
creationProcess
|
||||
attribute.getAttributeMetadataAccess().resolveAttributeMetadata( declaringType ).isUpdatable()
|
||||
);
|
||||
this.multiLoader = new MultiNaturalIdLoaderStandard<>( declaringType, creationProcess );
|
||||
this.attribute = attribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verifyFlushState(Object id, Object[] currentState, Object[] loadedState, SharedSessionContractImplementor session) {
|
||||
if ( ! immutable ) {
|
||||
if ( isMutable() ) {
|
||||
// EARLY EXIT!!!
|
||||
// the natural id is mutable (!immutable), no need to do the checks
|
||||
return;
|
||||
|
@ -134,7 +116,7 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object normalizeIncomingValue(Object incoming, SharedSessionContractImplementor session) {
|
||||
public Object normalizeInput(Object incoming, SharedSessionContractImplementor session) {
|
||||
return normalizeIncomingValue( incoming );
|
||||
}
|
||||
|
||||
|
@ -160,26 +142,11 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
|
|||
return attribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoader<?> getNaturalIdLoader() {
|
||||
return loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiNaturalIdLoader<?> getMultiNaturalIdLoader() {
|
||||
return multiLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SingularAttributeMapping> getNaturalIdAttributes() {
|
||||
return Collections.singletonList( attribute );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmutable() {
|
||||
return immutable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingType getPartMappingType() {
|
||||
return attribute.getPartMappingType();
|
||||
|
@ -238,6 +205,11 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
|
|||
return attribute.disassemble( value, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
attribute.breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachDisassembledJdbcValue(
|
||||
Object value,
|
||||
|
@ -257,4 +229,14 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping {
|
|||
SharedSessionContractImplementor session) {
|
||||
return attribute.forEachJdbcValue( value, clause, offset, valuesConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoader<?> makeLoader(EntityMappingType entityDescriptor) {
|
||||
return new SimpleNaturalIdLoader<>( this, entityDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiNaturalIdLoader<?> makeMultiLoader(EntityMappingType entityDescriptor) {
|
||||
return new MultiNaturalIdLoaderStandard<>( entityDescriptor );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,17 +11,19 @@ import org.hibernate.engine.FetchStrategy;
|
|||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.mapping.ManyToOne;
|
||||
import org.hibernate.mapping.OneToOne;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -59,7 +61,8 @@ import org.hibernate.sql.results.internal.domain.CircularFetchImpl;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
||||
public class ToOneAttributeMapping
|
||||
extends AbstractSingularAttributeMapping
|
||||
implements EntityValuedFetchable, EntityAssociationMapping, TableGroupJoinProducer {
|
||||
|
||||
public enum Cardinality {
|
||||
|
@ -214,24 +217,6 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
|||
return navigableRole;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcValue(
|
||||
Object value,
|
||||
Clause clause,
|
||||
int offset,
|
||||
JdbcValuesConsumer consumer,
|
||||
SharedSessionContractImplementor session) {
|
||||
return getForeignKeyDescriptor().forEachJdbcValue(
|
||||
getAssociatedEntityMappingType()
|
||||
.getIdentifierMapping()
|
||||
.getIdentifier( value, session ),
|
||||
clause,
|
||||
offset,
|
||||
consumer,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch resolveCircularFetch(
|
||||
NavigablePath fetchablePath,
|
||||
|
@ -628,6 +613,14 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
|||
return "SingularAssociationAttributeMapping {" + navigableRole + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(
|
||||
Object domainValue,
|
||||
JdbcValueConsumer valueConsumer,
|
||||
SharedSessionContractImplementor session) {
|
||||
foreignKeyDescriptor.breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachSelection(int offset, SelectionConsumer consumer) {
|
||||
if ( isKeyReferringSide ) {
|
||||
|
@ -637,4 +630,29 @@ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCount() {
|
||||
return foreignKeyDescriptor.getJdbcTypeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> action) {
|
||||
return foreignKeyDescriptor.forEachJdbcType( offset, action );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object disassemble(Object value, SharedSessionContractImplementor session) {
|
||||
return foreignKeyDescriptor.disassemble( value, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachDisassembledJdbcValue(Object value, Clause clause, int offset, JdbcValuesConsumer valuesConsumer, SharedSessionContractImplementor session) {
|
||||
return foreignKeyDescriptor.forEachDisassembledJdbcValue( value, clause, offset, valuesConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int forEachJdbcValue(Object value, Clause clause, int offset, JdbcValuesConsumer consumer, SharedSessionContractImplementor session) {
|
||||
return foreignKeyDescriptor.forEachJdbcValue( value, clause, offset, consumer, session );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,14 +48,17 @@ public class EmbeddableTypeImpl<J>
|
|||
extends AbstractManagedType<J>
|
||||
implements EmbeddableDomainType<J>, Serializable, MappingModelExpressable<J> {
|
||||
|
||||
private final boolean isDynamic;
|
||||
private final EmbeddableRepresentationStrategy representationStrategy;
|
||||
|
||||
public EmbeddableTypeImpl(
|
||||
JavaTypeDescriptor<J> javaTypeDescriptor,
|
||||
EmbeddableRepresentationStrategy representationStrategy,
|
||||
boolean isDynamic,
|
||||
JpaMetamodel domainMetamodel) {
|
||||
super( javaTypeDescriptor.getJavaType().getName(), javaTypeDescriptor, null, domainMetamodel );
|
||||
this.representationStrategy = representationStrategy;
|
||||
this.isDynamic = isDynamic;
|
||||
}
|
||||
|
||||
public EmbeddableTypeImpl(
|
||||
|
|
|
@ -123,6 +123,7 @@ import org.hibernate.loader.ast.internal.SingleUniqueKeyEntityLoaderStandard;
|
|||
import org.hibernate.loader.ast.spi.Loader;
|
||||
import org.hibernate.loader.ast.spi.MultiIdEntityLoader;
|
||||
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
|
||||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.SingleIdEntityLoader;
|
||||
import org.hibernate.loader.ast.spi.SingleUniqueKeyEntityLoader;
|
||||
|
@ -145,22 +146,22 @@ import org.hibernate.metamodel.RepresentationMode;
|
|||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.AttributeMetadata;
|
||||
import org.hibernate.metamodel.mapping.AttributeMetadataAccess;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityVersionMapping;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.Queryable;
|
||||
import org.hibernate.metamodel.mapping.SelectionConsumer;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
|
||||
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicEntityIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.CompoundNaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EntityDiscriminatorMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.EntityRowIdMappingImpl;
|
||||
|
@ -168,8 +169,8 @@ import org.hibernate.metamodel.mapping.internal.EntityVersionMappingImpl;
|
|||
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleNaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleNaturalIdMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
|
@ -203,6 +204,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase;
|
|||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
|
@ -256,9 +258,12 @@ public abstract class AbstractEntityPersister
|
|||
public static final String ENTITY_CLASS = "class";
|
||||
|
||||
private final String sqlAliasStem;
|
||||
private EntityMappingType rootEntityDescriptor;
|
||||
|
||||
private final SingleIdEntityLoader singleIdEntityLoader;
|
||||
private final MultiIdEntityLoader multiIdEntityLoader;
|
||||
private NaturalIdLoader naturalIdLoader;
|
||||
private MultiNaturalIdLoader multiNaturalIdLoader;
|
||||
|
||||
private SqmMultiTableMutationStrategy sqmMultiTableMutationStrategy;
|
||||
|
||||
|
@ -1347,10 +1352,11 @@ public abstract class AbstractEntityPersister
|
|||
String explicitSourceAlias,
|
||||
boolean canUseInnerJoins,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver();
|
||||
final SqlAliasBaseGenerator aliasBaseGenerator = creationState.getSqlAliasBaseGenerator();
|
||||
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
|
||||
|
||||
final TableReference primaryTableReference = createPrimaryTableReference(
|
||||
|
@ -5688,6 +5694,12 @@ public abstract class AbstractEntityPersister
|
|||
return entityMetamodel.getNaturalIdentifierProperties();
|
||||
}
|
||||
|
||||
private void verifyHasNaturalId() {
|
||||
if ( ! hasNaturalIdentifier() ) {
|
||||
throw new HibernateException( "Entity does not define a natural id : " + getEntityName() );
|
||||
}
|
||||
}
|
||||
|
||||
public Object[] getNaturalIdentifierSnapshot(Object id, SharedSessionContractImplementor session) {
|
||||
verifyHasNaturalId();
|
||||
|
||||
|
@ -5699,7 +5711,7 @@ public abstract class AbstractEntityPersister
|
|||
);
|
||||
}
|
||||
|
||||
final Object result = naturalIdMapping.getNaturalIdLoader().resolveIdToNaturalId( id, session );
|
||||
final Object result = getNaturalIdLoader().resolveIdToNaturalId( id, session );
|
||||
if ( result instanceof Object[] ) {
|
||||
return (Object[]) result;
|
||||
}
|
||||
|
@ -5708,16 +5720,27 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
}
|
||||
|
||||
private void verifyHasNaturalId() {
|
||||
if ( ! hasNaturalIdentifier() ) {
|
||||
throw new HibernateException( "Entity does not define a natural id : " + getEntityName() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdLoader<?> getNaturalIdLoader() {
|
||||
verifyHasNaturalId();
|
||||
return naturalIdMapping.getNaturalIdLoader();
|
||||
|
||||
if ( naturalIdLoader == null ) {
|
||||
naturalIdLoader = naturalIdMapping.makeLoader( this );
|
||||
}
|
||||
|
||||
return naturalIdLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiNaturalIdLoader<?> getMultiNaturalIdLoader() {
|
||||
verifyHasNaturalId();
|
||||
|
||||
if ( multiNaturalIdLoader == null ) {
|
||||
multiNaturalIdLoader = naturalIdMapping.makeMultiLoader( this );
|
||||
}
|
||||
|
||||
return multiNaturalIdLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5735,7 +5758,7 @@ public abstract class AbstractEntityPersister
|
|||
);
|
||||
}
|
||||
|
||||
return naturalIdMapping.getNaturalIdLoader().resolveNaturalIdToId( naturalIdValues, session );
|
||||
return getNaturalIdLoader().resolveNaturalIdToId( naturalIdValues, session );
|
||||
}
|
||||
|
||||
public boolean hasNaturalIdentifier() {
|
||||
|
@ -5954,9 +5977,11 @@ public abstract class AbstractEntityPersister
|
|||
else {
|
||||
prepareMappingModel( creationProcess, bootEntityDescriptor );
|
||||
}
|
||||
rootEntityDescriptor = superMappingType.getRootEntityDescriptor();
|
||||
}
|
||||
else {
|
||||
prepareMappingModel( creationProcess, bootEntityDescriptor );
|
||||
rootEntityDescriptor = this;
|
||||
}
|
||||
|
||||
final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
|
||||
|
@ -6047,7 +6072,7 @@ public abstract class AbstractEntityPersister
|
|||
else {
|
||||
rowIdMapping = creationProcess.processSubPart(
|
||||
rowIdName,
|
||||
(role, process) -> new EntityRowIdMappingImpl( rowIdName, this.getTableName(), this)
|
||||
(role, process) -> new EntityRowIdMappingImpl( rowIdName, this.getTableName(), this )
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6055,7 +6080,10 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
|
||||
private void postProcessAttributeMappings(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {
|
||||
if ( bootEntityDescriptor.hasNaturalId() ) {
|
||||
if ( superMappingType != null ) {
|
||||
naturalIdMapping = superMappingType.getNaturalIdMapping();
|
||||
}
|
||||
else if ( bootEntityDescriptor.hasNaturalId() ) {
|
||||
naturalIdMapping = generateNaturalIdMapping( creationProcess, bootEntityDescriptor );
|
||||
}
|
||||
else {
|
||||
|
@ -6066,43 +6094,47 @@ public abstract class AbstractEntityPersister
|
|||
private NaturalIdMapping generateNaturalIdMapping(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {
|
||||
assert bootEntityDescriptor.hasNaturalId();
|
||||
|
||||
final List<SingularAttributeMapping> naturalIdAttributes = new ArrayList<>();
|
||||
final Iterator<Property> iterator = bootEntityDescriptor.getPropertyIterator();
|
||||
iterator.forEachRemaining(
|
||||
property -> {
|
||||
if ( property.isNaturalIdentifier() ) {
|
||||
final AttributeMapping attributeMapping = findAttributeMapping( property.getName() );
|
||||
if ( attributeMapping instanceof SingularAttributeMapping ) {
|
||||
naturalIdAttributes.add( (SingularAttributeMapping) attributeMapping );
|
||||
}
|
||||
else {
|
||||
throw new MappingException( "Natural-id only valid for singular attributes : " + property.getName() );
|
||||
}
|
||||
final int[] naturalIdAttributeIndexes = entityMetamodel.getNaturalIdentifierProperties();
|
||||
assert naturalIdAttributeIndexes.length > 0;
|
||||
|
||||
if ( naturalIdAttributeIndexes.length == 1 ) {
|
||||
final String propertyName = entityMetamodel.getPropertyNames()[ naturalIdAttributeIndexes[ 0 ] ];
|
||||
final AttributeMapping attributeMapping = findAttributeMapping( propertyName );
|
||||
|
||||
return new SimpleNaturalIdMapping(
|
||||
(SingularAttributeMapping) attributeMapping,
|
||||
this,
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
|
||||
// collect the names of the attributes making up the natural-id.
|
||||
final Set<String> attributeNames = CollectionHelper.setOfSize( naturalIdAttributeIndexes.length );
|
||||
for ( int naturalIdAttributeIndex : naturalIdAttributeIndexes ) {
|
||||
attributeNames.add( this.getPropertyNames()[ naturalIdAttributeIndex ] );
|
||||
}
|
||||
|
||||
// then iterate over the attribute mappings finding the ones having names
|
||||
// in the collected names. iterate here because it is already alphabetical
|
||||
|
||||
final List<SingularAttributeMapping> collectedAttrMappings = new ArrayList<>();
|
||||
this.attributeMappings.forEach(
|
||||
(attributeMapping) -> {
|
||||
if ( attributeNames.contains( attributeMapping.getAttributeName() ) ) {
|
||||
collectedAttrMappings.add( (SingularAttributeMapping) attributeMapping );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if ( naturalIdAttributes.isEmpty() ) {
|
||||
throw new MappingException( "Could not locate natural-id attribute(s)" );
|
||||
}
|
||||
|
||||
if ( naturalIdAttributes.size() == 1 ) {
|
||||
return new SimpleNaturalIdMapping(
|
||||
naturalIdAttributes.get( 0 ),
|
||||
this,
|
||||
bootEntityDescriptor.getNaturalIdCacheRegionName(),
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new CompoundNaturalIdMapping(
|
||||
this,
|
||||
naturalIdAttributes,
|
||||
bootEntityDescriptor.getNaturalIdCacheRegionName(),
|
||||
creationProcess
|
||||
);
|
||||
if ( collectedAttrMappings.size() <= 1 ) {
|
||||
throw new MappingException( "Expected multiple natural-id attributes, but found only one: " + getEntityName() );
|
||||
}
|
||||
|
||||
return new CompoundNaturalIdMapping(
|
||||
this,
|
||||
collectedAttrMappings,
|
||||
creationProcess
|
||||
);
|
||||
}
|
||||
|
||||
protected static SqmMultiTableMutationStrategy interpretSqmMultiTableStrategy(
|
||||
|
|
|
@ -34,8 +34,10 @@ import org.hibernate.internal.FilterAliasGenerator;
|
|||
import org.hibernate.internal.TableGroupFilterAliasGenerator;
|
||||
import org.hibernate.loader.ast.spi.Loadable;
|
||||
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
|
||||
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
|
||||
import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
||||
import org.hibernate.metadata.ClassMetadata;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
|
||||
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
||||
|
@ -404,6 +406,25 @@ public interface EntityPersister
|
|||
*/
|
||||
IdentifierGenerator getIdentifierGenerator();
|
||||
|
||||
@Override
|
||||
default void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
final List<AttributeMapping> attributeMappings = getAttributeMappings();
|
||||
if ( domainValue instanceof Object[] ) {
|
||||
final Object[] values = (Object[]) domainValue;
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
final AttributeMapping attributeMapping = attributeMappings.get( i );
|
||||
attributeMapping.breakDownJdbcValues( values[ i ], valueConsumer, session );
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( int i = 0; i < attributeMappings.size(); i++ ) {
|
||||
final AttributeMapping attributeMapping = attributeMappings.get( i );
|
||||
final Object attributeValue = attributeMapping.getPropertyAccess().getGetter().get( domainValue );
|
||||
attributeMapping.breakDownJdbcValues( attributeValue, valueConsumer, session );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this entity defines any lazy properties (ala
|
||||
* bytecode instrumentation).
|
||||
|
@ -414,7 +435,13 @@ public interface EntityPersister
|
|||
|
||||
default NaturalIdLoader getNaturalIdLoader() {
|
||||
throw new UnsupportedOperationException(
|
||||
"EntityPersister implementation `" + getClass().getName() + "` does not support `#getNaturalIdLoader`"
|
||||
"EntityPersister implementation `" + getClass().getName() + "` does not support `NaturalIdLoader`"
|
||||
);
|
||||
}
|
||||
|
||||
default MultiNaturalIdLoader<?> getMultiNaturalIdLoader() {
|
||||
throw new UnsupportedOperationException(
|
||||
"EntityPersister implementation `" + getClass().getName() + "` does not support `MultiNaturalIdLoader`"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.internal.DynamicFilterAliasGenerator;
|
||||
import org.hibernate.internal.FilterAliasGenerator;
|
||||
import org.hibernate.internal.util.MarkerObject;
|
||||
import org.hibernate.internal.util.MutableInteger;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.mapping.Column;
|
||||
|
@ -48,8 +47,8 @@ import org.hibernate.query.NavigablePath;
|
|||
import org.hibernate.sql.InFragment;
|
||||
import org.hibernate.sql.Insert;
|
||||
import org.hibernate.sql.SelectFragment;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
|
@ -903,25 +902,22 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
String explicitSourceAlias,
|
||||
boolean canUseInnerJoins,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableGroup tableGroup = super.createRootTableGroup(
|
||||
navigablePath,
|
||||
explicitSourceAlias,
|
||||
canUseInnerJoins,
|
||||
lockMode,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
additionalPredicateCollectorAccess,
|
||||
creationContext
|
||||
creationState, creationContext
|
||||
);
|
||||
|
||||
if ( needsDiscriminator() ) {
|
||||
final Predicate discriminatorPredicate = createDiscriminatorPredicate(
|
||||
tableGroup,
|
||||
sqlExpressionResolver,
|
||||
creationState.getSqlExpressionResolver(),
|
||||
creationContext
|
||||
);
|
||||
additionalPredicateCollectorAccess.get().accept( discriminatorPredicate );
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.id.IdentityGenerator;
|
||||
import org.hibernate.internal.FilterAliasGenerator;
|
||||
import org.hibernate.internal.StaticFilterAliasGenerator;
|
||||
import org.hibernate.internal.util.MutableInteger;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.internal.util.collections.JoinedIterator;
|
||||
import org.hibernate.internal.util.collections.SingletonIterator;
|
||||
|
@ -46,9 +45,8 @@ import org.hibernate.persister.spi.PersisterCreationContext;
|
|||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.SelectFragment;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
|
||||
|
@ -229,11 +227,10 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
String explicitSourceAlias,
|
||||
boolean canUseInnerJoins,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
|
||||
final SqlAliasBase sqlAliasBase = creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() );
|
||||
|
||||
final TableReference tableReference = resolvePrimaryTableReference(sqlAliasBase);
|
||||
|
||||
|
|
|
@ -83,9 +83,8 @@ public class CompleteResultBuilderEntityJpa implements CompleteResultBuilderEnti
|
|||
null,
|
||||
false,
|
||||
lockMode,
|
||||
impl.getSqlAliasBaseManager(),
|
||||
impl.getSqlAstCreationState().getSqlExpressionResolver(),
|
||||
() -> predicate -> {},
|
||||
impl.getSqlAstCreationState(),
|
||||
impl.getSqlAstCreationState().getCreationContext()
|
||||
)
|
||||
);
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.query.results.complete;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
|
@ -16,13 +14,10 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
|||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
||||
import org.hibernate.query.results.FetchBuilder;
|
||||
import org.hibernate.query.results.ResultBuilder;
|
||||
import org.hibernate.query.results.ResultBuilderBasicValued;
|
||||
import org.hibernate.query.results.ResultsHelper;
|
||||
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.sql.results.graph.entity.EntityResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
|
@ -76,10 +71,9 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde
|
|||
null,
|
||||
false,
|
||||
lockMode,
|
||||
impl.getSqlAliasBaseManager(),
|
||||
impl.getSqlAstCreationState().getSqlExpressionResolver(),
|
||||
() -> predicate -> {},
|
||||
impl.getSqlAstCreationState().getCreationContext()
|
||||
impl,
|
||||
impl.getCreationContext()
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -62,11 +62,10 @@ public class ImplicitModelPartResultBuilderEntity
|
|||
null,
|
||||
false,
|
||||
LockMode.READ,
|
||||
creationStateImpl.getSqlAliasBaseGenerator(),
|
||||
creationStateImpl,
|
||||
() -> predicate -> {
|
||||
},
|
||||
creationStateImpl.getSqlAstCreationState().getCreationContext()
|
||||
creationStateImpl,
|
||||
creationStateImpl.getCreationContext()
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -83,12 +83,10 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
|
|||
mutatingEntityExplicitAlias,
|
||||
true,
|
||||
LockMode.PESSIMISTIC_WRITE,
|
||||
getSqlAliasBaseGenerator(),
|
||||
getSqlExpressionResolver(),
|
||||
() -> predicate -> {
|
||||
},
|
||||
creationContext.getSessionFactory()
|
||||
);
|
||||
this,
|
||||
creationContext.getSessionFactory() );
|
||||
|
||||
getFromClauseAccess().registerTableGroup( navigablePath, mutatingTableGroup );
|
||||
}
|
||||
|
|
|
@ -215,6 +215,7 @@ import org.hibernate.sql.ast.tree.expression.ExtractUnit;
|
|||
import org.hibernate.sql.ast.tree.expression.Format;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.expression.NullnessLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
|
||||
|
@ -529,9 +530,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
sqmStatement.getRoot().getAlias(),
|
||||
false,
|
||||
LockMode.WRITE,
|
||||
getSqlAliasBaseGenerator(),
|
||||
getSqlExpressionResolver(),
|
||||
() -> predicate -> additionalRestrictions = predicate,
|
||||
this,
|
||||
getCreationContext()
|
||||
);
|
||||
|
||||
|
@ -725,9 +725,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
statement.getRoot().getAlias(),
|
||||
false,
|
||||
LockMode.WRITE,
|
||||
stem -> getSqlAliasBaseGenerator().createSqlAliasBase( stem ),
|
||||
getSqlExpressionResolver(),
|
||||
() -> predicate -> additionalRestrictions = predicate,
|
||||
this,
|
||||
getCreationContext()
|
||||
);
|
||||
getFromClauseAccess().registerTableGroup( rootPath, rootTableGroup );
|
||||
|
@ -804,9 +803,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
sqmStatement.getTarget().getExplicitAlias(),
|
||||
false,
|
||||
LockMode.WRITE,
|
||||
stem -> getSqlAliasBaseGenerator().createSqlAliasBase( stem ),
|
||||
getSqlExpressionResolver(),
|
||||
() -> predicate -> additionalRestrictions = predicate,
|
||||
this,
|
||||
getCreationContext()
|
||||
);
|
||||
|
||||
|
@ -867,9 +865,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
sqmStatement.getTarget().getExplicitAlias(),
|
||||
false,
|
||||
LockMode.WRITE,
|
||||
stem -> getSqlAliasBaseGenerator().createSqlAliasBase( stem ),
|
||||
getSqlExpressionResolver(),
|
||||
() -> predicate -> additionalRestrictions = predicate,
|
||||
this,
|
||||
getCreationContext()
|
||||
);
|
||||
|
||||
|
@ -1543,9 +1540,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
sqmRoot.getExplicitAlias(),
|
||||
true,
|
||||
LockMode.NONE,
|
||||
sqlAliasBaseManager,
|
||||
sqlExpressionResolver,
|
||||
() -> predicate -> {},
|
||||
this,
|
||||
creationContext
|
||||
);
|
||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||
|
@ -1611,12 +1607,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
sqmRoot.getExplicitAlias(),
|
||||
true,
|
||||
LockMode.NONE,
|
||||
sqlAliasBaseManager,
|
||||
sqlExpressionResolver,
|
||||
() -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||
additionalRestrictions,
|
||||
predicate
|
||||
),
|
||||
this,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -1764,12 +1759,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
sqmJoin.getExplicitAlias(),
|
||||
true,
|
||||
determineLockMode( sqmJoin.getExplicitAlias() ),
|
||||
sqlAliasBaseManager,
|
||||
getSqlExpressionResolver(),
|
||||
() -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||
additionalRestrictions,
|
||||
predicate
|
||||
),
|
||||
this,
|
||||
getCreationContext()
|
||||
);
|
||||
|
||||
|
@ -1795,12 +1789,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
sqmJoin.getExplicitAlias(),
|
||||
true,
|
||||
determineLockMode( sqmJoin.getExplicitAlias() ),
|
||||
sqlAliasBaseManager,
|
||||
getSqlExpressionResolver(),
|
||||
() -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||
additionalRestrictions,
|
||||
predicate
|
||||
),
|
||||
this,
|
||||
getCreationContext()
|
||||
);
|
||||
getFromClauseIndex().register( sqmJoin, tableGroup );
|
||||
|
@ -2028,7 +2021,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
@Override
|
||||
public Expression visitLiteral(SqmLiteral<?> literal) {
|
||||
if ( literal instanceof SqmLiteralNull ) {
|
||||
return new QueryLiteral<>( null, (BasicValuedMapping) inferableTypeAccessStack.getCurrent().get() );
|
||||
return new NullnessLiteral( inferableTypeAccessStack.getCurrent().get() );
|
||||
}
|
||||
|
||||
return new QueryLiteral<>(
|
||||
|
@ -3349,9 +3342,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
null,
|
||||
true,
|
||||
LockOptions.NONE.getLockMode(),
|
||||
sqlAliasBaseManager,
|
||||
getSqlExpressionResolver(),
|
||||
() -> querySpec::applyPredicate,
|
||||
this,
|
||||
creationContext
|
||||
);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.sql.ast.tree.expression.ExtractUnit;
|
|||
import org.hibernate.sql.ast.tree.expression.Format;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.expression.NullnessLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
|
||||
|
@ -142,6 +143,8 @@ public interface SqlAstWalker {
|
|||
|
||||
void visitQueryLiteral(QueryLiteral queryLiteral);
|
||||
|
||||
void visitNullnessLiteral(NullnessLiteral nullnessLiteral);
|
||||
|
||||
void visitUnaryOperationExpression(UnaryOperation unaryOperationExpression);
|
||||
|
||||
void visitBetweenPredicate(BetweenPredicate betweenPredicate);
|
||||
|
|
|
@ -73,6 +73,7 @@ import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
|||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.LiteralAsParameter;
|
||||
import org.hibernate.sql.ast.tree.expression.NullnessLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
|
||||
|
@ -2562,6 +2563,12 @@ public abstract class AbstractSqlAstWalker
|
|||
visitLiteral( queryLiteral );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNullnessLiteral(NullnessLiteral nullnessLiteral) {
|
||||
// todo (6.0) : account for composite nulls?
|
||||
appendSql( "null" );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void visitLiteral(Literal literal) {
|
||||
if ( literal.getLiteralValue() == null ) {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.sql.ast.tree.expression;
|
||||
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
|
||||
/**
|
||||
* Assertion that the SQL state of a "mapping model expressable" is null
|
||||
*/
|
||||
public class NullnessLiteral implements Expression {
|
||||
private final MappingModelExpressable<?> valueMapping;
|
||||
|
||||
public NullnessLiteral(MappingModelExpressable<?> valueMapping) {
|
||||
this.valueMapping = valueMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingModelExpressable<?> getExpressionType() {
|
||||
return valueMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SqlAstWalker sqlTreeWalker) {
|
||||
sqlTreeWalker.visitNullnessLiteral( this );
|
||||
}
|
||||
}
|
|
@ -12,9 +12,8 @@ import java.util.function.Supplier;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
|
||||
/**
|
||||
|
@ -32,8 +31,6 @@ public interface RootTableGroupProducer extends TableGroupProducer, ModelPartCon
|
|||
String explicitSourceAlias,
|
||||
boolean canUseInnerJoins,
|
||||
LockMode lockMode,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationContext creationContext);
|
||||
SqlAstCreationState creationState, SqlAstCreationContext creationContext);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ public class NullnessPredicate implements Predicate {
|
|||
private final Expression expression;
|
||||
private final boolean negated;
|
||||
|
||||
public NullnessPredicate(Expression expression) {
|
||||
this( expression, false );
|
||||
}
|
||||
|
||||
public NullnessPredicate(Expression expression, boolean negated) {
|
||||
this.expression = expression;
|
||||
this.negated = negated;
|
||||
|
|
|
@ -149,6 +149,11 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc
|
|||
return ( (Association) fetchParent ).getForeignKeyDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
fetchable.breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.sql.results.internal.domain;
|
|||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.mapping.Association;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -180,6 +181,11 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
|
|||
return ( (Association) fetchParent ).getForeignKeyDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) {
|
||||
fetchable.breakDownJdbcValues( domainValue, valueConsumer, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.hibernate.cfg.AvailableSettings;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedIdentifierMappingImpl;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
|
@ -30,7 +30,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
@ServiceRegistry(
|
||||
settings = @Setting( name = AvailableSettings.HBM2DDL_AUTO, value = "create-drop" )
|
||||
)
|
||||
public class DynamicCompositeIdBasicTests {
|
||||
public class DynamicCompositeIdBasicBindingTests {
|
||||
@Test
|
||||
public void testBinding(ServiceRegistryScope scope) {
|
||||
final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) new MetadataSources( scope.getRegistry() )
|
||||
|
@ -43,11 +43,11 @@ public class DynamicCompositeIdBasicTests {
|
|||
.getMappingMetamodel()
|
||||
.findEntityDescriptor( "DynamicCompositeIdBasic" );
|
||||
|
||||
assertThat( entityDescriptor.getNumberOfAttributeMappings(), is( 3 ) );
|
||||
assertThat( entityDescriptor.getNumberOfAttributeMappings(), is( 1 ) );
|
||||
|
||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||
assertThat( identifierMapping, instanceOf( NonAggregatedIdentifierMappingImpl.class ) );
|
||||
final NonAggregatedIdentifierMappingImpl cid = (NonAggregatedIdentifierMappingImpl) identifierMapping;
|
||||
assertThat( identifierMapping, instanceOf( EmbeddedIdentifierMappingImpl.class ) );
|
||||
final EmbeddedIdentifierMappingImpl cid = (EmbeddedIdentifierMappingImpl) identifierMapping;
|
||||
assertThat( cid.getEmbeddableTypeDescriptor().getNumberOfAttributeMappings(), is( 2 ) );
|
||||
|
||||
final AttributeMapping key1 = cid.getEmbeddableTypeDescriptor().findAttributeMapping( "key1" );
|
||||
|
@ -58,10 +58,6 @@ public class DynamicCompositeIdBasicTests {
|
|||
|
||||
final AttributeMapping attr1 = entityDescriptor.findAttributeMapping( "attr1" );
|
||||
assertThat( attr1, notNullValue() );
|
||||
|
||||
sessionFactory.inTransaction(
|
||||
session -> session.createQuery( "from DynamicCompositeIdBasic e where e.key1 = 1" ).list()
|
||||
);
|
||||
}
|
||||
finally {
|
||||
sessionFactory.close();
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.bootstrap.binding.hbm.cid.nonaggregated.dynamic;
|
||||
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedIdentifierMappingImpl;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@ServiceRegistry(
|
||||
settings = @Setting( name = AvailableSettings.HBM2DDL_AUTO, value = "create-drop" )
|
||||
)
|
||||
@DomainModel( xmlMappings = "org/hibernate/orm/test/bootstrap/binding/hbm/cid/nonaggregated/dynamic/DynamicCompositeIdBasic.hbm.xml" )
|
||||
@SessionFactory
|
||||
public class DynamicCompositeIdBasicUsageTests {
|
||||
@Test
|
||||
public void testFullQueryReference(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> session.createQuery( "from DynamicCompositeIdBasic e where e.id.key1 = 1" ).list()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected( reason = "Do we want to allow this?" )
|
||||
public void testEmbeddedQueryReference(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> session.createQuery( "from DynamicCompositeIdBasic e where e.key1 = 1" ).list()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
|
@ -25,7 +25,6 @@ import static org.hamcrest.CoreMatchers.instanceOf;
|
|||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction;
|
||||
|
||||
/**
|
||||
* Note that this test uses a composite-id with key-many-to-one as part of a
|
||||
|
@ -36,7 +35,7 @@ import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction;
|
|||
@ServiceRegistry(
|
||||
settings = @Setting( name = AvailableSettings.HBM2DDL_AUTO, value = "create-drop" )
|
||||
)
|
||||
public class DynamicCompositeIdManyToOneTests {
|
||||
public class DynamicCompositeIdManyToOneBindingTests {
|
||||
@Test
|
||||
public void testBinding(ServiceRegistryScope scope) {
|
||||
final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) new MetadataSources( scope.getRegistry() )
|
||||
|
@ -49,11 +48,11 @@ public class DynamicCompositeIdManyToOneTests {
|
|||
.getMappingMetamodel()
|
||||
.findEntityDescriptor( "DynamicCompositeIdManyToOne" );
|
||||
|
||||
assertThat( entityDescriptor.getNumberOfAttributeMappings(), is( 3 ) );
|
||||
assertThat( entityDescriptor.getNumberOfAttributeMappings(), is( 1 ) );
|
||||
|
||||
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
|
||||
assertThat( identifierMapping, instanceOf( NonAggregatedIdentifierMappingImpl.class ) );
|
||||
final NonAggregatedIdentifierMappingImpl cid = (NonAggregatedIdentifierMappingImpl) identifierMapping;
|
||||
assertThat( identifierMapping, instanceOf( EmbeddedIdentifierMappingImpl.class ) );
|
||||
final EmbeddedIdentifierMappingImpl cid = (EmbeddedIdentifierMappingImpl) identifierMapping;
|
||||
assertThat( cid.getEmbeddableTypeDescriptor().getNumberOfAttributeMappings(), is( 2 ) );
|
||||
|
||||
final AttributeMapping key1 = cid.getEmbeddableTypeDescriptor().findAttributeMapping( "key1" );
|
||||
|
@ -67,17 +66,6 @@ public class DynamicCompositeIdManyToOneTests {
|
|||
final AttributeMapping attr1 = entityDescriptor.findAttributeMapping( "attr1" );
|
||||
assertThat( attr1, notNullValue() );
|
||||
assertThat( attr1, instanceOf( BasicValuedSingularAttributeMapping.class ) );
|
||||
|
||||
assertThat( entityDescriptor.getNumberOfAttributeMappings(), is( 3 ) );
|
||||
|
||||
inTransaction(
|
||||
sessionFactory,
|
||||
session -> {
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__" ).list();
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__ where e__.key1 = 1" ).list();
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__ where e__.key2.name = 'abc'" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
finally {
|
||||
sessionFactory.close();
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.bootstrap.binding.hbm.cid.nonaggregated.dynamic;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Note that this test uses a composite-id with key-many-to-one as part of a
|
||||
* dynamic model, which is the main construct needed by hibernate-envers
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@ServiceRegistry(
|
||||
settings = @Setting( name = AvailableSettings.HBM2DDL_AUTO, value = "create-drop" )
|
||||
)
|
||||
@DomainModel( xmlMappings = "org/hibernate/orm/test/bootstrap/binding/hbm/cid/nonaggregated/dynamic/DynamicCompositeIdManyToOne.hbm.xml" )
|
||||
@SessionFactory
|
||||
public class DynamicCompositeIdManyToOneUsageTests {
|
||||
@Test
|
||||
public void testFullQueryReference(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__" ).list();
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__ where e__.id.key1 = 1" ).list();
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__ where e__.id.key2.name = 'abc'" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected( reason = "Do we want to allow this?" )
|
||||
public void testEmbeddedQueryReference(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__" ).list();
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__ where e__.key1 = 1" ).list();
|
||||
session.createQuery( "select e__ from DynamicCompositeIdManyToOne e__ where e__.key2.name = 'abc'" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.cid;
|
||||
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleNaturalIdMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractCompositeIdAndNaturalIdTest {
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-10360")
|
||||
public void testNaturalIdNullability(SessionFactoryScope scope) {
|
||||
final EntityMappingType accountMapping = scope.getSessionFactory().getRuntimeMetamodels().getEntityMappingType( Account.class );
|
||||
final SingularAttributeMapping shortCodeMapping = ((SimpleNaturalIdMapping) accountMapping.getNaturalIdMapping()).getAttribute();
|
||||
final StateArrayContributorMetadata shortCodeMetadata = shortCodeMapping.getAttributeMetadataAccess().resolveAttributeMetadata( null );
|
||||
assertThat( shortCodeMetadata.isNullable(), is( false ) );
|
||||
|
||||
final EntityPersister rootEntityPersister = accountMapping.getRootEntityDescriptor().getEntityPersister();
|
||||
final int shortCodeLegacyPropertyIndex = rootEntityPersister.getEntityMetamodel().getPropertyIndex( "shortCode" );
|
||||
assertThat( shortCodeLegacyPropertyIndex, is ( 0 ) );
|
||||
assertThat( rootEntityPersister.getPropertyNullability()[ shortCodeLegacyPropertyIndex ], is( false ) );
|
||||
}
|
||||
|
||||
public static final String NATURAL_ID_VALUE = "testAcct";
|
||||
|
||||
@BeforeEach
|
||||
public void prepareTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
// prepare some test data...
|
||||
Account account = new Account( new AccountId( 1 ), NATURAL_ID_VALUE );
|
||||
session.save( account );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanUpTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.createQuery( "delete Account" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdCriteria(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final Account account = session.bySimpleNaturalId( Account.class ).load( NATURAL_ID_VALUE );
|
||||
assertThat( account, notNullValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdApi(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final Account account = session.bySimpleNaturalId( Account.class ).load( NATURAL_ID_VALUE );
|
||||
assertThat( account, notNullValue() );
|
||||
}
|
||||
);
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final Account account = session.byNaturalId( Account.class ).using( "shortCode", NATURAL_ID_VALUE ).load();
|
||||
assertThat( account, notNullValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
-->
|
||||
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
|
||||
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
|
||||
<hibernate-mapping package="org.hibernate.test.naturalid.cid" default-access="field">
|
||||
<hibernate-mapping package="org.hibernate.orm.test.mapping.naturalid.cid" default-access="field">
|
||||
<class name="Account" table="accounts" >
|
||||
<composite-id name="accountId">
|
||||
<key-property name="id"/>
|
|
@ -4,13 +4,26 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.naturalid.cid;
|
||||
package org.hibernate.orm.test.mapping.naturalid.cid;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
|
||||
/**
|
||||
* @author Donnchadh O Donnabhain
|
||||
*/
|
||||
@Entity
|
||||
@Table( name = "t_acct" )
|
||||
public class Account {
|
||||
@EmbeddedId
|
||||
private AccountId accountId;
|
||||
|
||||
@Basic( optional = false )
|
||||
@NaturalId
|
||||
private String shortCode;
|
||||
|
||||
protected Account() {
|
|
@ -4,11 +4,14 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.naturalid.cid;
|
||||
package org.hibernate.orm.test.mapping.naturalid.cid;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
/**
|
||||
* @author Donnchadh O Donnabhain
|
||||
*/
|
||||
@Embeddable
|
||||
public class AccountId implements java.io.Serializable {
|
||||
private final int id;
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.cid;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
|
||||
import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
|
||||
|
||||
/**
|
||||
* @author Donnchadh O Donnabhain
|
||||
*/
|
||||
@ServiceRegistry(
|
||||
settings = {
|
||||
@Setting( name = USE_SECOND_LEVEL_CACHE, value = "false" ),
|
||||
@Setting( name = USE_QUERY_CACHE, value = "false" ),
|
||||
@Setting( name = GENERATE_STATISTICS, value = "false" )
|
||||
}
|
||||
)
|
||||
@DomainModel( annotatedClasses = { Account.class, AccountId.class } )
|
||||
@SessionFactory
|
||||
public class AnnotatedCompositeIdAndNaturalIdTest extends AbstractCompositeIdAndNaturalIdTest {
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.cid;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
|
||||
import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
|
||||
|
||||
/**
|
||||
* @author Donnchadh O Donnabhain
|
||||
*/
|
||||
@ServiceRegistry(
|
||||
settings = {
|
||||
@Setting( name = USE_SECOND_LEVEL_CACHE, value = "false" ),
|
||||
@Setting( name = USE_QUERY_CACHE, value = "false" ),
|
||||
@Setting( name = GENERATE_STATISTICS, value = "false" )
|
||||
}
|
||||
)
|
||||
@DomainModel( xmlMappings = "org/hibernate/orm/test/mapping/naturalid/cid/Account.hbm.xml" )
|
||||
@SessionFactory
|
||||
public class HbmCompositeIdAndNaturalIdTest extends AbstractCompositeIdAndNaturalIdTest {
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.test.naturalid.composite;
|
||||
package org.hibernate.orm.test.mapping.naturalid.composite;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
|
@ -13,14 +13,13 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
|||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-11255")
|
||||
public class CompositeNaturalIdMappingTest extends BaseUnitTestCase {
|
||||
public class CompositeNaturalIdMappingTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.composite;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@DomainModel( annotatedClasses = PostalCarrier.class )
|
||||
@SessionFactory
|
||||
@TestForIssue(jiraKey = "HHH-11255")
|
||||
public class EmbeddedNaturalIdTest {
|
||||
|
||||
@BeforeEach
|
||||
public void createTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
PostalCarrier postalCarrier = new PostalCarrier();
|
||||
postalCarrier.setId( 1L );
|
||||
postalCarrier.setPostalCode( new PostalCode() );
|
||||
postalCarrier.getPostalCode().setCode( "ABC123" );
|
||||
postalCarrier.getPostalCode().setCountry( "US" );
|
||||
|
||||
session.persist( postalCarrier );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.createQuery( "delete PostalCarrier" ).executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final PostalCarrier postalCarrier = session
|
||||
.byNaturalId( PostalCarrier.class )
|
||||
.using( "postalCode", new PostalCode( "ABC123", "US" ) )
|
||||
.load();
|
||||
assertEquals( Long.valueOf( 1L ), postalCarrier.getId() );
|
||||
}
|
||||
);
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final PostalCarrier postalCarrier = session
|
||||
.bySimpleNaturalId( PostalCarrier.class )
|
||||
.load(new PostalCode( "ABC123", "US" ) );
|
||||
assertEquals( Long.valueOf( 1L ), postalCarrier.getId() );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.test.naturalid.composite;
|
||||
package org.hibernate.orm.test.mapping.naturalid.composite;
|
||||
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
|
@ -4,7 +4,7 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.test.naturalid.composite;
|
||||
package org.hibernate.orm.test.mapping.naturalid.composite;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.compound;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-11255")
|
||||
public class CompoundNaturalIdMappingTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
|
||||
.build();
|
||||
|
||||
try {
|
||||
Metadata meta = new MetadataSources( ssr )
|
||||
.addAnnotatedClass( PostalCarrier.class )
|
||||
.addAnnotatedClass( Country.class )
|
||||
.buildMetadata();
|
||||
( (MetadataImplementor) meta ).validate();
|
||||
|
||||
final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) meta.buildSessionFactory();
|
||||
|
||||
try {
|
||||
final EntityMappingType entityMappingType = sessionFactory.getRuntimeMetamodels().getEntityMappingType( PostalCarrier.class );
|
||||
final NaturalIdMapping naturalIdMapping = entityMappingType.getNaturalIdMapping();
|
||||
final List<SingularAttributeMapping> naturalIdAttributes = naturalIdMapping.getNaturalIdAttributes();
|
||||
|
||||
assertThat( naturalIdAttributes.size(), is( 2 ) );
|
||||
assertThat( naturalIdAttributes.get( 0 ).getAttributeName(), is( "code" ) );
|
||||
assertThat( naturalIdAttributes.get( 1 ).getAttributeName(), is( "country" ) );
|
||||
}
|
||||
finally {
|
||||
sessionFactory.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
StandardServiceRegistryBuilder.destroy( ssr );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.hibernate.orm.test.mapping.naturalid.compound;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity
|
||||
public class Country {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String isoCode;
|
||||
private String name;
|
||||
|
||||
public Country() {
|
||||
}
|
||||
|
||||
public Country(Integer id, String isoCode, String name) {
|
||||
this.id = id;
|
||||
this.isoCode = isoCode;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getIsoCode() {
|
||||
return isoCode;
|
||||
}
|
||||
|
||||
public void setIsoCode(String isoCode) {
|
||||
this.isoCode = isoCode;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.compound;
|
||||
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity(name = "PostalCarrier")
|
||||
public class PostalCarrier {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@NaturalId
|
||||
@ManyToOne
|
||||
private Country country;
|
||||
|
||||
@NaturalId
|
||||
private String code;
|
||||
|
||||
|
||||
public PostalCarrier() {
|
||||
}
|
||||
|
||||
public PostalCarrier(long id, String code, Country country) {
|
||||
this.id = id;
|
||||
this.code = code;
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Country getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(Country country) {
|
||||
this.country = country;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutable;
|
||||
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
|
||||
/**
|
||||
* @author Alex Burgel
|
||||
*/
|
||||
@Entity
|
||||
public class Child {
|
||||
@Id
|
||||
private Integer id;
|
||||
@NaturalId
|
||||
private String name;
|
||||
@NaturalId( )
|
||||
@ManyToOne
|
||||
private Parent parent;
|
||||
|
||||
Child() {}
|
||||
|
||||
public Child(Integer id, String name, Parent parent) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Parent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Parent parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.annotations.NaturalIdCache;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Alex Burgel
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-11330" )
|
||||
@RequiresDialectFeature( value = DialectChecks.SupportsIdentityColumns.class )
|
||||
@ServiceRegistry(
|
||||
settings = {
|
||||
@Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" ),
|
||||
@Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true" )
|
||||
}
|
||||
)
|
||||
@DomainModel( annotatedClasses = IdentityGeneratorWithNaturalIdCacheTest.Person.class )
|
||||
@SessionFactory
|
||||
public class IdentityGeneratorWithNaturalIdCacheTest {
|
||||
@BeforeEach
|
||||
public void prepareTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
Person person = new Person();
|
||||
person.setName( "John Doe" );
|
||||
session.persist( person );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.createQuery( "delete Person" ).executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10659")
|
||||
public void testNaturalIdCacheEntry(SessionFactoryScope scope) {
|
||||
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
|
||||
assertThat( statistics.getSecondLevelCacheHitCount(), Matchers.is( 0L ) );
|
||||
assertThat( statistics.getNaturalIdCacheHitCount(), Matchers.is( 0L ) );
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.bySimpleNaturalId( Person.class ).load( "John Doe" );
|
||||
assertThat( statistics.getSecondLevelCacheHitCount(), Matchers.is( 0L ) );
|
||||
assertThat( statistics.getNaturalIdCacheHitCount(), Matchers.is( 1L ) );
|
||||
}
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.bySimpleNaturalId( Person.class ).load( "John Doe" );
|
||||
assertThat( statistics.getSecondLevelCacheHitCount(), Matchers.is( 1L ) );
|
||||
assertThat( statistics.getNaturalIdCacheHitCount(), Matchers.is( 2L ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@NaturalIdCache
|
||||
@Cache( usage = CacheConcurrencyStrategy.READ_ONLY )
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@NaturalId
|
||||
private String name;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutable;
|
||||
|
||||
import org.hibernate.metamodel.RuntimeMetamodels;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@DomainModel( annotatedClasses = { Child.class, Parent.class } )
|
||||
@SessionFactory
|
||||
public class ImmutableManyToOneNaturalIdAnnotationTest {
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-10360")
|
||||
public void testNaturalIdNullability(SessionFactoryScope scope) {
|
||||
// nullability is not specified for either properties making up
|
||||
// the natural ID, so they should be nullable by annotation-specific default
|
||||
|
||||
final RuntimeMetamodels runtimeMetamodels = scope.getSessionFactory().getRuntimeMetamodels();
|
||||
final EntityMappingType childMapping = runtimeMetamodels.getEntityMappingType( Child.class.getName() );
|
||||
|
||||
final EntityPersister persister = childMapping.getEntityPersister();
|
||||
final EntityMetamodel entityMetamodel = persister.getEntityMetamodel();
|
||||
final int nameIndex = entityMetamodel.getPropertyIndex( "name" );
|
||||
final int parentIndex = entityMetamodel.getPropertyIndex( "parent" );
|
||||
|
||||
// checking alphabetic sort in relation to EntityPersister/EntityMetamodel
|
||||
assertThat( nameIndex, lessThan( parentIndex ) );
|
||||
|
||||
assertFalse( persister.getPropertyUpdateability()[ nameIndex ] );
|
||||
assertFalse( persister.getPropertyUpdateability()[ parentIndex ] );
|
||||
|
||||
assertTrue( persister.getPropertyNullability()[ nameIndex ] );
|
||||
assertTrue( persister.getPropertyNullability()[ parentIndex ] );
|
||||
|
||||
|
||||
final NaturalIdMapping naturalIdMapping = childMapping.getNaturalIdMapping();
|
||||
assertNotNull( naturalIdMapping );
|
||||
assertThat( naturalIdMapping.getNaturalIdAttributes().size(), is( 2 ) );
|
||||
|
||||
// access by list-index should again be alphabetically sorted
|
||||
final SingularAttributeMapping first = naturalIdMapping.getNaturalIdAttributes().get( 0 );
|
||||
assertThat( first.getAttributeName(), is( "name" ) );
|
||||
final StateArrayContributorMetadata firstMetadata = first.getAttributeMetadataAccess().resolveAttributeMetadata( null );
|
||||
assertFalse( firstMetadata.getMutabilityPlan().isMutable() );
|
||||
|
||||
final SingularAttributeMapping second = naturalIdMapping.getNaturalIdAttributes().get( 1 );
|
||||
assertThat( second.getAttributeName(), is( "parent" ) );
|
||||
final StateArrayContributorMetadata secondMetadata = second.getAttributeMetadataAccess().resolveAttributeMetadata( null );
|
||||
assertFalse( secondMetadata.getMutabilityPlan().isMutable() );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutable;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.metamodel.RuntimeMetamodels;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Alex Burgel
|
||||
*/
|
||||
@ServiceRegistry(
|
||||
settings = {
|
||||
@Setting( name = Environment.USE_SECOND_LEVEL_CACHE, value = "true" ),
|
||||
@Setting( name = Environment.USE_QUERY_CACHE, value = "true" ),
|
||||
@Setting( name = Environment.GENERATE_STATISTICS, value = "true" )
|
||||
}
|
||||
)
|
||||
@DomainModel( xmlMappings = "org/hibernate/orm/test/mapping/naturalid/immutable/ParentChildWithManyToOne.hbm.xml" )
|
||||
@SessionFactory
|
||||
public class ImmutableManyToOneNaturalIdHbmTest {
|
||||
|
||||
@BeforeEach
|
||||
public void createTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
Parent p = new Parent( 1, "alex" );
|
||||
Child c = new Child( 1, "billy", p );
|
||||
session.persist( p );
|
||||
session.persist( c );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.createQuery( "delete Child" ).executeUpdate();
|
||||
session.createQuery( "delete Parent" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-10360")
|
||||
public void checkingMapping(SessionFactoryScope scope) {
|
||||
|
||||
final RuntimeMetamodels runtimeMetamodels = scope.getSessionFactory().getRuntimeMetamodels();
|
||||
final EntityMappingType childMapping = runtimeMetamodels.getEntityMappingType( Child.class.getName() );
|
||||
|
||||
final EntityPersister persister = childMapping.getEntityPersister();
|
||||
final EntityMetamodel entityMetamodel = persister.getEntityMetamodel();
|
||||
final int nameIndex = entityMetamodel.getPropertyIndex( "name" );
|
||||
final int parentIndex = entityMetamodel.getPropertyIndex( "parent" );
|
||||
|
||||
// checking alphabetic sort in relation to EntityPersister/EntityMetamodel
|
||||
assertThat( nameIndex, lessThan( parentIndex ) );
|
||||
|
||||
assertFalse( persister.getPropertyUpdateability()[ nameIndex ] );
|
||||
assertFalse( persister.getPropertyUpdateability()[ parentIndex ] );
|
||||
|
||||
// nullability is not specified for either properties making up
|
||||
// the natural ID, so they should be non-nullable by hbm-specific default
|
||||
|
||||
assertFalse( persister.getPropertyNullability()[ nameIndex ] );
|
||||
assertFalse( persister.getPropertyNullability()[ parentIndex ] );
|
||||
|
||||
final NaturalIdMapping naturalIdMapping = childMapping.getNaturalIdMapping();
|
||||
assertNotNull( naturalIdMapping );
|
||||
assertThat( naturalIdMapping.getNaturalIdAttributes().size(), is( 2 ) );
|
||||
|
||||
final SingularAttributeMapping first = naturalIdMapping.getNaturalIdAttributes().get( 0 );
|
||||
assertThat( first.getAttributeName(), is( "name" ) );
|
||||
final StateArrayContributorMetadata firstMetadata = first.getAttributeMetadataAccess().resolveAttributeMetadata( null );
|
||||
assertFalse( firstMetadata.getMutabilityPlan().isMutable() );
|
||||
|
||||
final SingularAttributeMapping second = naturalIdMapping.getNaturalIdAttributes().get( 1 );
|
||||
assertThat( second.getAttributeName(), is( "parent" ) );
|
||||
final StateArrayContributorMetadata secondMetadata = second.getAttributeMetadataAccess().resolveAttributeMetadata( null );
|
||||
assertFalse( secondMetadata.getMutabilityPlan().isMutable() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdCheck(SessionFactoryScope scope) {
|
||||
final Child child = scope.fromTransaction( (s) -> s.get( Child.class, 1 ) );
|
||||
|
||||
// child is detached...
|
||||
// - change the name and attempt to reattach it, which should fail
|
||||
// because name is defined as an immutable natural-id
|
||||
child.setName( "phil" );
|
||||
|
||||
scope.inTransaction(
|
||||
(s) -> {
|
||||
try {
|
||||
s.saveOrUpdate( child );
|
||||
s.flush();
|
||||
fail( "should have failed because immutable natural ID was altered");
|
||||
}
|
||||
catch (PersistenceException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings( {"unchecked"})
|
||||
public void testSaveParentWithDetachedChildren(SessionFactoryScope scope) {
|
||||
final Parent p = scope.fromTransaction(
|
||||
(session) -> session.createQuery( "from Parent p join fetch p.children where p.name = 'alex'", Parent.class )
|
||||
.setCacheable( true )
|
||||
.uniqueResult()
|
||||
);
|
||||
|
||||
// parent and its child are detached...
|
||||
// - create a new child and associate it with the parent and reattach
|
||||
// NOTE : this fails if AbstractEntityPersister returns identifiers instead of entities from
|
||||
// AbstractEntityPersister.getNaturalIdSnapshot()
|
||||
|
||||
// todo (6.0) : ^^ this test has nothing to do with (im)mutability...
|
||||
|
||||
Child c2 = new Child( 2, "joey", p );
|
||||
p.getChildren().add( c2 );
|
||||
|
||||
scope.inTransaction( (session) -> session.update( p ) );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutable;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@ServiceRegistry(
|
||||
settings = {
|
||||
@Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true" ),
|
||||
@Setting( name = AvailableSettings.USE_QUERY_CACHE, value = "true" ),
|
||||
@Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" )
|
||||
}
|
||||
)
|
||||
@DomainModel( xmlMappings = "org/hibernate/orm/test/mapping/naturalid/immutable/User.hbm.xml" )
|
||||
@SessionFactory
|
||||
public class ImmutableNaturalIdTest {
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> session.createQuery( "delete User" ).executeUpdate() );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-10360")
|
||||
public void verifyMetamodel(SessionFactoryScope scope) {
|
||||
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
|
||||
final EntityMappingType entityMappingType = sessionFactory.getRuntimeMetamodels().getEntityMappingType( User.class );
|
||||
|
||||
final AttributeMapping userNameMapping = entityMappingType.findAttributeMapping( "userName" );
|
||||
assertFalse( userNameMapping.getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable() );
|
||||
|
||||
final EntityPersister persister = entityMappingType.getEntityPersister();
|
||||
final int propertyIndex = persister.getEntityMetamodel().getPropertyIndex( "userName" );
|
||||
// nullability is not specified, so it should be non-nullable by hbm-specific default
|
||||
assertFalse( persister.getPropertyNullability()[propertyIndex] );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdCheck(SessionFactoryScope scope) {
|
||||
final User detachedUser = scope.fromTransaction(
|
||||
(session) -> {
|
||||
final User user = new User( "steve", "superSecret" );
|
||||
session.persist( user );
|
||||
return user;
|
||||
}
|
||||
);
|
||||
|
||||
// try to change the user-name (natural-id) and re-attach... should error
|
||||
detachedUser.setUserName( "Steve" );
|
||||
try {
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.merge( detachedUser );
|
||||
}
|
||||
);
|
||||
fail();
|
||||
}
|
||||
catch (PersistenceException expected) {
|
||||
// expected outcome
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdCache(SessionFactoryScope scope) {
|
||||
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final User u = new User( "steve", "superSecret" );
|
||||
session.persist( u );
|
||||
}
|
||||
);
|
||||
|
||||
statistics.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final User steve = session.bySimpleNaturalId( User.class ).load( "steve" );
|
||||
assertNotNull( steve );
|
||||
}
|
||||
);
|
||||
|
||||
assertEquals( 0, statistics.getNaturalIdCacheHitCount() );
|
||||
assertEquals( 0, statistics.getNaturalIdCachePutCount() );
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
session.persist( new User( "gavin", "supsup" ) );
|
||||
}
|
||||
);
|
||||
|
||||
statistics.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final User steve = session.bySimpleNaturalId( User.class ).load( "steve" );
|
||||
assertNotNull( steve );
|
||||
assertEquals( 0, statistics.getNaturalIdCacheHitCount() );
|
||||
|
||||
final User steve2 = session.bySimpleNaturalId( User.class ).load( "steve" );
|
||||
assertNotNull( steve2 );
|
||||
assertEquals( 0, statistics.getNaturalIdCacheHitCount() );
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdDeleteUsingCache(SessionFactoryScope scope) {
|
||||
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final User u = new User( "steve", "superSecret" );
|
||||
session.persist( u );
|
||||
}
|
||||
);
|
||||
|
||||
statistics.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final User steve = session.bySimpleNaturalId( User.class ).load( "steve" );
|
||||
assertNotNull( steve );
|
||||
}
|
||||
);
|
||||
|
||||
assertEquals( 0, statistics.getNaturalIdQueryExecutionCount() );
|
||||
assertEquals( 0, statistics.getNaturalIdCacheHitCount() );
|
||||
assertEquals( 0, statistics.getNaturalIdCachePutCount() );
|
||||
|
||||
statistics.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final User steve = session.bySimpleNaturalId( User.class ).load( "steve" );
|
||||
session.delete( steve );
|
||||
}
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final User steve = session.bySimpleNaturalId( User.class ).load( "steve" );
|
||||
assertNull( steve );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
/**
|
||||
* @author Alex Burgel
|
||||
*/
|
||||
@Entity
|
||||
public class Parent {
|
||||
@Id
|
||||
private Integer id;
|
||||
private String name;
|
||||
@OneToMany( mappedBy = "parent" )
|
||||
private List<Child> children = new ArrayList<>();
|
||||
|
||||
Parent() {}
|
||||
|
||||
|
||||
public Parent(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
|
@ -16,13 +16,11 @@
|
|||
-->
|
||||
|
||||
<hibernate-mapping
|
||||
package="org.hibernate.test.naturalid.immutable"
|
||||
package="org.hibernate.orm.test.mapping.naturalid.immutable"
|
||||
default-access="field">
|
||||
|
||||
<class name="Parent" table="Parent">
|
||||
<id name="id">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<id name="id" />
|
||||
|
||||
<property name="name"/>
|
||||
|
||||
|
@ -34,9 +32,7 @@
|
|||
</class>
|
||||
|
||||
<class name="Child" table="Child">
|
||||
<id name="id">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<id name="id"/>
|
||||
|
||||
<natural-id mutable="false">
|
||||
<many-to-one name="parent" class="Parent" />
|
|
@ -14,7 +14,7 @@
|
|||
This mapping illustrates use of <natural-id mutable="false"/>
|
||||
|
||||
-->
|
||||
<hibernate-mapping package="org.hibernate.test.naturalid.immutable">
|
||||
<hibernate-mapping package="org.hibernate.orm.test.mapping.naturalid.immutable">
|
||||
|
||||
<class name="User" table="IMM_NAT_ID_USER" lazy="true">
|
||||
<comment>Users may bid for or sell auction items.</comment>
|
|
@ -4,7 +4,7 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.naturalid.immutable;
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutable;
|
||||
|
||||
|
||||
/**
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.test.naturalid.immutableentity;
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutableentity;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.naturalid.immutableentity;
|
||||
|
||||
import org.hibernate.NaturalIdLoadAccess;
|
||||
import org.hibernate.annotations.Immutable;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test case for NaturalId annotation on an {@link Immutable} entity
|
||||
*
|
||||
* @author Eric Dalquist
|
||||
*/
|
||||
@ServiceRegistry(
|
||||
settings = {
|
||||
@Setting( name = AvailableSettings.USE_QUERY_CACHE, value = "true" ),
|
||||
@Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true" ),
|
||||
@Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" )
|
||||
}
|
||||
)
|
||||
@DomainModel( annotatedClasses = Building.class )
|
||||
@SessionFactory
|
||||
@SuppressWarnings("unchecked")
|
||||
@TestForIssue( jiraKey = "HHH-7085" )
|
||||
public class ImmutableEntityNaturalIdTest {
|
||||
@BeforeEach
|
||||
public void createTestData(SessionFactoryScope scope) {
|
||||
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
|
||||
final StatisticsImplementor stats = sessionFactory.getStatistics();
|
||||
|
||||
sessionFactory.getCache().evictAllRegions();
|
||||
stats.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
Building b1 = new Building();
|
||||
b1.setName( "Computer Science" );
|
||||
b1.setAddress( "1210 W. Dayton St." );
|
||||
b1.setCity( "Madison" );
|
||||
b1.setState( "WI" );
|
||||
|
||||
session.persist( b1 );
|
||||
}
|
||||
);
|
||||
|
||||
assertEquals( "Cache hits should be empty", 0, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "Cache misses should be empty", 0, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "Cache put should be one after insert", 1, stats.getNaturalIdCachePutCount() );
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.createQuery( "delete Building" ).executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdMapping(SessionFactoryScope scope) {
|
||||
final EntityMappingType buildingMapping = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getEntityMappingType( Building.class );
|
||||
|
||||
final NaturalIdMapping naturalIdMapping = buildingMapping.getNaturalIdMapping();
|
||||
assertThat( naturalIdMapping, notNullValue() );
|
||||
assertThat( naturalIdMapping.getNaturalIdAttributes().size(), is( 3 ) );
|
||||
// nullability is not specified, so they should be nullable by annotations-specific default
|
||||
for ( SingularAttributeMapping attribute : naturalIdMapping.getNaturalIdAttributes() ) {
|
||||
assertThat( attribute.getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), is( true ) );
|
||||
}
|
||||
|
||||
final EntityPersister entityPersister = buildingMapping.getEntityPersister();
|
||||
assertThat(
|
||||
"Class should have a natural key",
|
||||
entityPersister.hasNaturalIdentifier(),
|
||||
is( true )
|
||||
);
|
||||
final EntityMetamodel entityMetamodel = entityPersister.getEntityMetamodel();
|
||||
assertThat(
|
||||
"Wrong number of attributes",
|
||||
entityMetamodel.getNaturalIdentifierProperties().length,
|
||||
is( 3 )
|
||||
);
|
||||
|
||||
// nullability is not specified, so they should be nullable by annotations-specific default
|
||||
assertTrue( entityPersister.getPropertyNullability()[ entityMetamodel.getPropertyIndex( "address" )] );
|
||||
assertTrue( entityPersister.getPropertyNullability()[ entityMetamodel.getPropertyIndex( "city" )] );
|
||||
assertTrue( entityPersister.getPropertyNullability()[ entityMetamodel.getPropertyIndex( "state" )] );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImmutableNaturalIdLifecycle(SessionFactoryScope scope) {
|
||||
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
|
||||
final StatisticsImplementor stats = sessionFactory.getStatistics();
|
||||
|
||||
// Clear caches and reset cache stats
|
||||
sessionFactory.getCache().evictNaturalIdData();
|
||||
stats.clear();
|
||||
|
||||
// load #1 - should result in:
|
||||
// - cache miss
|
||||
// - query
|
||||
// - cache put
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final NaturalIdLoadAccess<Building> naturalIdLoader = session.byNaturalId( Building.class );
|
||||
final Building building = naturalIdLoader
|
||||
.using( "address", "1210 W. Dayton St." )
|
||||
.using( "city", "Madison" )
|
||||
.using( "state", "WI" )
|
||||
.load();
|
||||
assertThat( building, notNullValue() );
|
||||
assertEquals( "Cache hits should be empty", 0, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "Cache misses should be one", 1, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "Cache put should be one after load", 1, stats.getNaturalIdCachePutCount() );
|
||||
assertThat( stats.getPrepareStatementCount(), is( 1L ) );
|
||||
}
|
||||
);
|
||||
|
||||
// load #2 - should result in
|
||||
// - cache hit
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final NaturalIdLoadAccess<Building> naturalIdLoader = session.byNaturalId( Building.class );
|
||||
final Building building = naturalIdLoader
|
||||
.using( "address", "1210 W. Dayton St." )
|
||||
.using( "city", "Madison" )
|
||||
.using( "state", "WI" )
|
||||
.load();
|
||||
assertThat( building, notNullValue() );
|
||||
assertEquals( "Cache hits should be one after second query", 1, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "Cache misses should be one after second query", 1, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "Cache put should be one after second query", 1, stats.getNaturalIdCachePutCount() );
|
||||
|
||||
// Try Deleting
|
||||
session.delete( building );
|
||||
|
||||
// third query
|
||||
naturalIdLoader.load();
|
||||
assertEquals( "Cache hits should be one after second query", 1, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "Cache misses should be two after second query", 2, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "Cache put should be one after second query", 1, stats.getNaturalIdCachePutCount() );
|
||||
}
|
||||
);
|
||||
|
||||
//Try three, should be db lookup and miss
|
||||
|
||||
scope.inTransaction(
|
||||
(session) -> {
|
||||
final Building building = session.byNaturalId( Building.class )
|
||||
.using( "address", "1210 W. Dayton St." )
|
||||
.using( "city", "Madison" )
|
||||
.using( "state", "WI" )
|
||||
.load();
|
||||
|
||||
// second query
|
||||
assertNull( building );
|
||||
assertEquals( "Cache hits should be one after third query", 1, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "Cache misses should be one after third query", 3, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "Cache put should be one after third query", 1, stats.getNaturalIdCachePutCount() );
|
||||
|
||||
// here, we should know that that natural-id does not exist as part of the Session...
|
||||
session.byNaturalId( Building.class )
|
||||
.using( "address", "1210 W. Dayton St." )
|
||||
.using( "city", "Madison" )
|
||||
.using( "state", "WI" )
|
||||
.load();
|
||||
|
||||
assertEquals( "Cache hits should still be one", 1, stats.getNaturalIdCacheHitCount() );
|
||||
assertEquals( "Cache misses should now be four", 4, stats.getNaturalIdCacheMissCount() );
|
||||
assertEquals( "Cache put should still be one", 1, stats.getNaturalIdCachePutCount() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-7371" )
|
||||
public void testImmutableNaturalIdLifecycle2(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(s) -> {
|
||||
final NaturalIdLoadAccess<Building> naturalIdLoader = s.byNaturalId( Building.class );
|
||||
naturalIdLoader
|
||||
.using( "address", "1210 W. Dayton St." )
|
||||
.using( "city", "Madison" )
|
||||
.using( "state", "WI" );
|
||||
|
||||
Building building = naturalIdLoader.getReference();
|
||||
assertNotNull( building );
|
||||
|
||||
s.delete( building );
|
||||
building = naturalIdLoader.load();
|
||||
//org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [org.hibernate.test.naturalid.immutableentity.Building#1]
|
||||
// at org.hibernate.internal.SessionFactoryImpl$1$1.handleEntityNotFound(SessionFactoryImpl.java:247)
|
||||
// at org.hibernate.event.internal.DefaultLoadEventListener.returnNarrowedProxy(DefaultLoadEventListener.java:282)
|
||||
// at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:248)
|
||||
// at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:148)
|
||||
// at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1079)
|
||||
// at org.hibernate.internal.SessionImpl.access$13(SessionImpl.java:1075)
|
||||
// at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2425)
|
||||
// at org.hibernate.internal.SessionImpl$NaturalIdLoadAccessImpl.load(SessionImpl.java:2586)
|
||||
// at org.hibernate.test.naturalid.immutableentity.ImmutableEntityNaturalIdTest.testImmutableNaturalIdLifecycle2(ImmutableEntityNaturalIdTest.java:188)
|
||||
|
||||
assertNull( building );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue