Skip to content

Commit ceec437

Browse files
committed
HHH-19016 add SessionFactory.createGraphForDynamicEntity()
1 parent dc4397b commit ceec437

File tree

10 files changed

+103
-37
lines changed

10 files changed

+103
-37
lines changed

hibernate-core/src/main/java/org/hibernate/Session.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,13 @@ public interface Session extends SharedSessionContract, EntityManager {
901901
* @return a persistent instance or null
902902
*
903903
* @deprecated The semantics of this method may change in a future release.
904+
* Use {@link SessionFactory#createGraphForDynamicEntity(String)}
905+
* together with {@link #find(EntityGraph, Object, FindOption...)}
906+
* to load {@link org.hibernate.metamodel.RepresentationMode#MAP
907+
* dynamic entities}.
908+
*
909+
* @see SessionFactory#createGraphForDynamicEntity(String)
910+
* @see #find(EntityGraph, Object, FindOption...)
904911
*/
905912
@Deprecated(since = "7")
906913
Object get(String entityName, Object id);

hibernate-core/src/main/java/org/hibernate/SessionFactory.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import javax.naming.Referenceable;
1515

1616
import jakarta.persistence.EntityManager;
17+
import jakarta.persistence.FindOption;
1718
import jakarta.persistence.SynchronizationType;
1819
import org.hibernate.boot.spi.SessionFactoryOptions;
1920
import org.hibernate.engine.spi.FilterDefinition;
@@ -413,6 +414,27 @@ default <R> R fromStatelessTransaction(Function<? super StatelessSession,R> acti
413414
*/
414415
RootGraph<?> findEntityGraphByName(String name);
415416

417+
/**
418+
* Create an {@link EntityGraph} which may be used from loading a
419+
* {@linkplain org.hibernate.metamodel.RepresentationMode#MAP dynamic}
420+
* entity with {@link Session#find(EntityGraph, Object, FindOption...)}.
421+
* <p>
422+
* This allows a dynamic entity to be loaded without the need for a cast.
423+
* <pre>
424+
* var MyDynamicEntity_ = factory.createGraphForDynamicEntity("MyDynamicEntity");
425+
* Map&lt;String,?&gt; myDynamicEntity = session.find(MyDynamicEntity_, id);
426+
* </pre>
427+
*
428+
* @apiNote Dynamic entities are normally defined using XML mappings.
429+
*
430+
* @param entityName The name of the dynamic entity
431+
*
432+
* @since 7.0
433+
*
434+
* @see Session#find(EntityGraph, Object, FindOption...)
435+
*/
436+
RootGraph<Map<String,?>> createGraphForDynamicEntity(String entityName);
437+
416438
/**
417439
* Obtain the set of names of all {@link org.hibernate.annotations.FilterDef
418440
* defined filters}.

hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.hibernate.event.service.spi.EventListenerRegistry;
3939
import org.hibernate.event.spi.EntityCopyObserverFactory;
4040
import org.hibernate.event.spi.EventEngine;
41+
import org.hibernate.graph.RootGraph;
4142
import org.hibernate.graph.spi.RootGraphImplementor;
4243
import org.hibernate.event.service.spi.EventListenerGroups;
4344
import org.hibernate.metamodel.MappingMetamodel;
@@ -226,6 +227,11 @@ public SqlStringGenerationContext getSqlStringGenerationContext() {
226227
return delegate.getSqlStringGenerationContext();
227228
}
228229

230+
@Override
231+
public RootGraph<Map<String, ?>> createGraphForDynamicEntity(String entityName) {
232+
return delegate.createGraphForDynamicEntity( entityName );
233+
}
234+
229235
@Override
230236
public RootGraphImplementor<?> findEntityGraphByName(String name) {
231237
return delegate.findEntityGraphByName( name );

hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
import org.hibernate.event.spi.EventEngine;
7373
import org.hibernate.event.service.spi.EventListenerGroups;
7474
import org.hibernate.generator.Generator;
75+
import org.hibernate.graph.RootGraph;
76+
import org.hibernate.graph.internal.RootGraphImpl;
7577
import org.hibernate.graph.spi.RootGraphImplementor;
7678
import org.hibernate.integrator.spi.Integrator;
7779
import org.hibernate.integrator.spi.IntegratorService;
@@ -82,8 +84,10 @@
8284
import org.hibernate.mapping.PersistentClass;
8385
import org.hibernate.mapping.RootClass;
8486
import org.hibernate.metamodel.MappingMetamodel;
87+
import org.hibernate.metamodel.RepresentationMode;
8588
import org.hibernate.metamodel.internal.RuntimeMetamodelsImpl;
8689
import org.hibernate.metamodel.mapping.JdbcMapping;
90+
import org.hibernate.metamodel.model.domain.EntityDomainType;
8791
import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl;
8892
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
8993
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
@@ -724,6 +728,17 @@ public boolean isOpen() {
724728
return status != Status.CLOSED;
725729
}
726730

731+
@Override
732+
public RootGraph<Map<String, ?>> createGraphForDynamicEntity(String entityName) {
733+
final EntityDomainType<?> entity = getJpaMetamodel().entity( entityName );
734+
if ( entity.getRepresentationMode() != RepresentationMode.MAP ) {
735+
throw new IllegalArgumentException( "Entity '" + entityName + "' is not a dynamic entity" );
736+
}
737+
@SuppressWarnings("unchecked") //Safe, because we just checked
738+
final EntityDomainType<Map<String, ?>> dynamicEntity = (EntityDomainType<Map<String, ?>>) entity;
739+
return new RootGraphImpl<>( null, dynamicEntity );
740+
}
741+
727742
@Override
728743
public RootGraphImplementor<?> findEntityGraphByName(String name) {
729744
return getJpaMetamodel().findEntityGraphByName( name );

hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import org.hibernate.BatchSize;
2828
import org.hibernate.CacheMode;
2929
import org.hibernate.ConnectionAcquisitionMode;
30-
import org.hibernate.EntityFilterException;
3130
import org.hibernate.EnabledFetchProfile;
31+
import org.hibernate.EntityFilterException;
3232
import org.hibernate.FetchNotFoundException;
3333
import org.hibernate.FlushMode;
3434
import org.hibernate.HibernateException;
@@ -106,6 +106,7 @@
106106
import org.hibernate.event.spi.ReplicateEvent;
107107
import org.hibernate.event.spi.ReplicateEventListener;
108108
import org.hibernate.loader.internal.CacheLoadHelper;
109+
import org.hibernate.metamodel.model.domain.ManagedDomainType;
109110
import org.hibernate.resource.transaction.spi.TransactionObserver;
110111
import org.hibernate.event.monitor.spi.EventMonitor;
111112
import org.hibernate.event.monitor.spi.DiagnosticEvent;
@@ -961,8 +962,7 @@ public void load(Object object, Object id) {
961962
fireLoad( new LoadEvent( id, object, this, getReadOnlyFromLoadQueryInfluencers() ), LoadEventListener.RELOAD );
962963
}
963964

964-
private <T> MultiIdentifierLoadAccess<T> multiloadAccessWithOptions(Class<T> entityClass, FindOption[] options) {
965-
final MultiIdentifierLoadAccess<T> loadAccess = byMultipleIds( entityClass );
965+
private <T> void setMultiIdentifierLoadAccessOptions(FindOption[] options, MultiIdentifierLoadAccess<T> loadAccess) {
966966
CacheStoreMode storeMode = getCacheStoreMode();
967967
CacheRetrieveMode retrieveMode = getCacheRetrieveMode();
968968
LockOptions lockOptions = copySessionLockOptions();
@@ -1006,12 +1006,13 @@ else if ( option instanceof BatchSize batchSizeOption ) {
10061006
loadAccess.with( lockOptions )
10071007
.with( interpretCacheMode( storeMode, retrieveMode ) )
10081008
.withBatchSize( batchSize );
1009-
return loadAccess;
10101009
}
10111010

10121011
@Override
10131012
public <E> List<E> findMultiple(Class<E> entityType, List<Object> ids, FindOption... options) {
1014-
return multiloadAccessWithOptions( entityType, options ).multiLoad( ids );
1013+
final MultiIdentifierLoadAccess<E> loadAccess = byMultipleIds( entityType );
1014+
setMultiIdentifierLoadAccessOptions( options, loadAccess );
1015+
return loadAccess.multiLoad( ids );
10151016
}
10161017

10171018
@Override
@@ -2498,8 +2499,7 @@ private static <T> void logIgnoringEntityNotFound(Class<T> entityClass, Object p
24982499
}
24992500
}
25002501

2501-
private <T> IdentifierLoadAccessImpl<T> loadAccessWithOptions(Class<T> entityClass, FindOption[] options) {
2502-
final IdentifierLoadAccessImpl<T> loadAccess = byId( entityClass );
2502+
private <T> void setLoadAccessOptions(FindOption[] options, IdentifierLoadAccessImpl<T> loadAccess) {
25032503
CacheStoreMode storeMode = getCacheStoreMode();
25042504
CacheRetrieveMode retrieveMode = getCacheRetrieveMode();
25052505
LockOptions lockOptions = copySessionLockOptions();
@@ -2537,19 +2537,26 @@ else if ( option instanceof ReadOnlyMode ) {
25372537
}
25382538
}
25392539
loadAccess.with( lockOptions ).with( interpretCacheMode( storeMode, retrieveMode ) );
2540-
return loadAccess;
25412540
}
25422541

25432542
@Override
25442543
public <T> T find(Class<T> entityClass, Object primaryKey, FindOption... options) {
2545-
return loadAccessWithOptions( entityClass, options ).load( primaryKey );
2544+
final IdentifierLoadAccessImpl<T> loadAccess = byId( entityClass );
2545+
setLoadAccessOptions( options, loadAccess );
2546+
return loadAccess.load( primaryKey );
25462547
}
25472548

25482549
@Override
25492550
public <T> T find(EntityGraph<T> entityGraph, Object primaryKey, FindOption... options) {
25502551
final RootGraph<T> graph = (RootGraph<T>) entityGraph;
2551-
final Class<T> entityClass = graph.getGraphedType().getJavaType();
2552-
return loadAccessWithOptions( entityClass, options ).withLoadGraph( graph ).load( primaryKey );
2552+
final ManagedDomainType<T> type = graph.getGraphedType();
2553+
final IdentifierLoadAccessImpl<T> loadAccess =
2554+
switch ( type.getRepresentationMode() ) {
2555+
case MAP -> byId( type.getTypeName() );
2556+
case POJO -> byId( type.getJavaType() );
2557+
};
2558+
setLoadAccessOptions( options, loadAccess );
2559+
return loadAccess.withLoadGraph( graph ).load( primaryKey );
25532560
}
25542561

25552562
private void checkTransactionNeededForLock(LockMode lockMode) {

hibernate-core/src/main/java/org/hibernate/metamodel/RepresentationMode.java

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55
package org.hibernate.metamodel;
66

7-
import org.hibernate.AssertionFailure;
87

98
import java.util.Locale;
109

@@ -19,28 +18,19 @@ public enum RepresentationMode {
1918
MAP;
2019

2120
public String getExternalName() {
22-
switch (this) {
23-
case POJO:
24-
return "pojo";
25-
case MAP:
26-
return "dynamic-map";
27-
default:
28-
throw new AssertionFailure("Unknown RepresentationMode");
29-
}
21+
return switch ( this ) {
22+
case POJO -> "pojo";
23+
case MAP -> "dynamic-map";
24+
};
3025
}
3126

3227
public static RepresentationMode fromExternalName(String externalName) {
33-
if ( externalName == null ) {
34-
return POJO;
35-
}
36-
switch ( externalName.toLowerCase(Locale.ROOT) ) {
37-
case "pojo":
38-
return POJO;
39-
case "dynamic-map":
40-
case "map":
41-
return MAP;
42-
default:
43-
throw new IllegalArgumentException("Unknown RepresentationMode");
44-
}
28+
return externalName == null
29+
? POJO
30+
: switch ( externalName.toLowerCase( Locale.ROOT ) ) {
31+
case "pojo" -> POJO;
32+
case "dynamic-map", "map" -> MAP;
33+
default -> throw new IllegalArgumentException( "Unknown RepresentationMode" );
34+
};
4535
}
4636
}

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.hibernate.metamodel.model.domain.internal.AttributeContainer;
3131
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
3232
import org.hibernate.type.descriptor.java.JavaType;
33+
import org.hibernate.type.descriptor.java.spi.DynamicModelJavaType;
3334

3435
import static java.util.Collections.emptySet;
3536
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
@@ -66,10 +67,11 @@ protected AbstractManagedType(
6667
supertype.addSubType( this );
6768
}
6869

69-
// todo (6.0) : need to handle RepresentationMode#MAP as well
70-
this.representationMode = RepresentationMode.POJO;
70+
representationMode = javaType instanceof DynamicModelJavaType
71+
? RepresentationMode.MAP
72+
: RepresentationMode.POJO;
7173

72-
this.inFlightAccess = createInFlightAccess();
74+
inFlightAccess = createInFlightAccess();
7375
}
7476

7577
protected InFlightAccess<J> createInFlightAccess() {

hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@
163163
*/
164164
@Internal
165165
public abstract class AbstractCollectionPersister
166-
implements CollectionPersister, InFlightCollectionMapping, CollectionMutationTarget, PluralAttributeMappingImpl.Aware, FetchProfileAffectee, Joinable {
166+
implements CollectionPersister, InFlightCollectionMapping, CollectionMutationTarget,
167+
PluralAttributeMappingImpl.Aware, FetchProfileAffectee, Joinable {
167168

168169
private final NavigableRole navigableRole;
169170
private final CollectionSemantics<?,?> collectionSemantics;

hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@
319319
@Internal
320320
@SuppressWarnings("deprecation")
321321
public abstract class AbstractEntityPersister
322-
implements EntityPersister, InFlightEntityMappingType, EntityMutationTarget, LazyPropertyInitializer, FetchProfileAffectee, Joinable {
322+
implements EntityPersister, InFlightEntityMappingType, EntityMutationTarget, LazyPropertyInitializer,
323+
FetchProfileAffectee, Joinable {
323324

324325
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractEntityPersister.class );
325326

hibernate-core/src/test/java/org/hibernate/orm/test/mapping/inheritance/dynamic/DynamicJoinedInheritanceTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.HashMap;
88
import java.util.Map;
99

10+
import org.hibernate.graph.RootGraph;
1011
import org.hibernate.testing.orm.junit.DomainModel;
1112
import org.hibernate.testing.orm.junit.SessionFactory;
1213
import org.hibernate.testing.orm.junit.SessionFactoryScope;
@@ -33,6 +34,20 @@ public void testLoading(SessionFactoryScope scope) {
3334
} );
3435
}
3536

37+
@Test
38+
public void testLoadingNewApi(SessionFactoryScope scope) {
39+
final RootGraph<Map<String, ?>> Sub_ =
40+
scope.getSessionFactory()
41+
.createGraphForDynamicEntity( "Sub" );
42+
scope.inTransaction( (session) -> {
43+
final Map<String,?> entity = session.find( Sub_, 1 );
44+
assertThat( entity ).isNotNull();
45+
assertThat( entity.get( "name" ) ).isEqualTo( "sub" );
46+
assertThat( entity.get( TYPE_KEY ) ).isNotNull();
47+
assertThat( entity.get( TYPE_KEY ) ).isEqualTo( "Sub" );
48+
} );
49+
}
50+
3651
@BeforeEach
3752
public void createTestData(SessionFactoryScope scope) {
3853
scope.inTransaction( (session) -> {

0 commit comments

Comments
 (0)