@@ -378,6 +378,7 @@ public abstract class AbstractEntityPersister
378378 private final String [] lazyPropertyNames ;
379379 private final int [] lazyPropertyNumbers ;
380380 private final Type [] lazyPropertyTypes ;
381+ private final Set <String > nonLazyPropertyNames ;
381382
382383 //information about all properties in class hierarchy
383384 private final String [] subclassPropertyNameClosure ;
@@ -469,6 +470,7 @@ public abstract class AbstractEntityPersister
469470 private final boolean implementsLifecycle ;
470471
471472 private List <UniqueKeyEntry > uniqueKeyEntries = null ; //lazily initialized
473+ private HashMap <String ,SingleIdArrayLoadPlan > nonLazyPropertyLoadPlansByName ;
472474
473475 public AbstractEntityPersister (
474476 final PersistentClass persistentClass ,
@@ -606,6 +608,7 @@ public AbstractEntityPersister(
606608 propertyColumnUpdateable = new boolean [hydrateSpan ][];
607609 propertyColumnInsertable = new boolean [hydrateSpan ][];
608610 sharedColumnNames = new HashSet <>();
611+ nonLazyPropertyNames = new HashSet <>();
609612
610613 final HashSet <Property > thisClassProperties = new HashSet <>();
611614 final ArrayList <String > lazyNames = new ArrayList <>();
@@ -663,6 +666,9 @@ public AbstractEntityPersister(
663666 lazyNumbers .add ( i );
664667 lazyTypes .add ( prop .getValue ().getType () );
665668 }
669+ else {
670+ nonLazyPropertyNames .add ( prop .getName () );
671+ }
666672
667673 propertyColumnUpdateable [i ] = prop .getValue ().getColumnUpdateability ();
668674 propertyColumnInsertable [i ] = prop .getValue ().getColumnInsertability ();
@@ -1222,6 +1228,10 @@ private SingleIdArrayLoadPlan createLazyLoadPlan(List<LazyAttributeDescriptor> f
12221228 partsToSelect .add ( getAttributeMapping ( getSubclassPropertyIndex ( lazyAttributeDescriptor .getName () ) ) );
12231229 }
12241230
1231+ return createLazyLoanPlan ( partsToSelect );
1232+ }
1233+
1234+ private SingleIdArrayLoadPlan createLazyLoanPlan (List <ModelPart > partsToSelect ) {
12251235 if ( partsToSelect .isEmpty () ) {
12261236 // only one-to-one is lazily fetched
12271237 return null ;
@@ -1542,75 +1552,117 @@ protected Object initializeLazyPropertiesFromDatastore(
15421552 final EntityEntry entry ,
15431553 final String fieldName ,
15441554 final SharedSessionContractImplementor session ) {
1545-
1546- if ( !hasLazyProperties () ) {
1547- throw new AssertionFailure ( "no lazy properties" );
1555+ if ( nonLazyPropertyNames .contains ( fieldName ) ) {
1556+ // An eager property can be lazy because of an applied EntityGraph
1557+ final List <ModelPart > partsToSelect = new ArrayList <>(1 );
1558+ int propertyIndex = getPropertyIndex ( fieldName );
1559+ partsToSelect .add ( getAttributeMapping ( propertyIndex ) );
1560+ SingleIdArrayLoadPlan lazyLoanPlan ;
1561+ if ( nonLazyPropertyLoadPlansByName == null ) {
1562+ nonLazyPropertyLoadPlansByName = new HashMap <>();
1563+ lazyLoanPlan = createLazyLoanPlan ( partsToSelect );
1564+ ;
1565+ nonLazyPropertyLoadPlansByName .put ( fieldName , lazyLoanPlan );
1566+ }
1567+ else {
1568+ lazyLoanPlan = nonLazyPropertyLoadPlansByName .get ( fieldName );
1569+ if ( lazyLoanPlan == null ) {
1570+ lazyLoanPlan = createLazyLoanPlan ( partsToSelect );
1571+ ;
1572+ nonLazyPropertyLoadPlansByName .put ( fieldName , lazyLoanPlan );
1573+ }
1574+ }
1575+ try {
1576+ final Object [] values = lazyLoanPlan .load ( id , session );
1577+ final Object selectedValue = values [0 ];
1578+ initializeLazyProperty (
1579+ entity ,
1580+ entry ,
1581+ selectedValue ,
1582+ propertyIndex ,
1583+ getPropertyTypes ()[propertyIndex ]
1584+ );
1585+ return selectedValue ;
1586+ }
1587+ catch (JDBCException ex ) {
1588+ throw session .getJdbcServices ().getSqlExceptionHelper ().convert (
1589+ ex .getSQLException (),
1590+ "could not initialize lazy properties: " + infoString ( this , id , getFactory () ),
1591+ lazyLoanPlan .getJdbcSelect ().getSqlString ()
1592+ );
1593+ }
15481594 }
1595+ else {
1596+ if ( !hasLazyProperties () ) {
1597+ throw new AssertionFailure ( "no lazy properties" );
1598+ }
15491599
1550- final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable ( entity ).$$_hibernate_getInterceptor ();
1551- assert interceptor != null : "Expecting bytecode interceptor to be non-null" ;
1600+ final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable ( entity ).$$_hibernate_getInterceptor ();
1601+ assert interceptor != null : "Expecting bytecode interceptor to be non-null" ;
15521602
1553- LOG .tracef ( "Initializing lazy properties from datastore (triggered for `%s`)" , fieldName );
1603+ LOG .tracef ( "Initializing lazy properties from datastore (triggered for `%s`)" , fieldName );
15541604
1555- final String fetchGroup = getEntityMetamodel ().getBytecodeEnhancementMetadata ()
1556- .getLazyAttributesMetadata ()
1557- .getFetchGroupName ( fieldName );
1558- final List <LazyAttributeDescriptor > fetchGroupAttributeDescriptors = getEntityMetamodel ().getBytecodeEnhancementMetadata ()
1559- .getLazyAttributesMetadata ()
1560- .getFetchGroupAttributeDescriptors ( fetchGroup );
1605+ final String fetchGroup = getEntityMetamodel ().getBytecodeEnhancementMetadata ()
1606+ .getLazyAttributesMetadata ()
1607+ .getFetchGroupName ( fieldName );
1608+ final List <LazyAttributeDescriptor > fetchGroupAttributeDescriptors = getEntityMetamodel ().getBytecodeEnhancementMetadata ()
1609+ .getLazyAttributesMetadata ()
1610+ .getFetchGroupAttributeDescriptors ( fetchGroup );
15611611
1562- final Set <String > initializedLazyAttributeNames = interceptor .getInitializedLazyAttributeNames ();
1612+ final Set <String > initializedLazyAttributeNames = interceptor .getInitializedLazyAttributeNames ();
15631613
1564- final SingleIdArrayLoadPlan lazySelect = getSQLLazySelectLoadPlan ( fetchGroup );
1614+ final SingleIdArrayLoadPlan lazySelect = getSQLLazySelectLoadPlan ( fetchGroup );
15651615
1566- try {
1567- Object result = null ;
1568- final Object [] values = lazySelect .load ( id , session );
1569- int i = 0 ;
1570- for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) {
1571- final boolean previousInitialized = initializedLazyAttributeNames .contains ( fetchGroupAttributeDescriptor .getName () );
1572-
1573- if ( previousInitialized ) {
1574- // todo : one thing we should consider here is potentially un-marking an attribute as dirty based on the selected value
1575- // we know the current value - getPropertyValue( entity, fetchGroupAttributeDescriptor.getAttributeIndex() );
1576- // we know the selected value (see selectedValue below)
1577- // we can use the attribute Type to tell us if they are the same
1578- //
1579- // assuming entity is a SelfDirtinessTracker we can also know if the attribute is
1580- // currently considered dirty, and if really not dirty we would do the un-marking
1581- //
1582- // of course that would mean a new method on SelfDirtinessTracker to allow un-marking
1583-
1584- // its already been initialized (e.g. by a write) so we don't want to overwrite
1585- i ++;
1586- continue ;
1587- }
1616+ try {
1617+ Object result = null ;
1618+ final Object [] values = lazySelect .load ( id , session );
1619+ int i = 0 ;
1620+ for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) {
1621+ final boolean previousInitialized = initializedLazyAttributeNames .contains (
1622+ fetchGroupAttributeDescriptor .getName () );
1623+
1624+ if ( previousInitialized ) {
1625+ // todo : one thing we should consider here is potentially un-marking an attribute as dirty based on the selected value
1626+ // we know the current value - getPropertyValue( entity, fetchGroupAttributeDescriptor.getAttributeIndex() );
1627+ // we know the selected value (see selectedValue below)
1628+ // we can use the attribute Type to tell us if they are the same
1629+ //
1630+ // assuming entity is a SelfDirtinessTracker we can also know if the attribute is
1631+ // currently considered dirty, and if really not dirty we would do the un-marking
1632+ //
1633+ // of course that would mean a new method on SelfDirtinessTracker to allow un-marking
1634+
1635+ // its already been initialized (e.g. by a write) so we don't want to overwrite
1636+ i ++;
1637+ continue ;
1638+ }
15881639
1589- final Object selectedValue = values [i ++];
1590- final boolean set = initializeLazyProperty (
1591- fieldName ,
1592- entity ,
1593- entry ,
1594- fetchGroupAttributeDescriptor .getLazyIndex (),
1595- selectedValue
1596- );
1597- if ( set ) {
1598- result = selectedValue ;
1599- interceptor .attributeInitialized ( fetchGroupAttributeDescriptor .getName () );
1640+ final Object selectedValue = values [i ++];
1641+ final boolean set = initializeLazyProperty (
1642+ fieldName ,
1643+ entity ,
1644+ entry ,
1645+ fetchGroupAttributeDescriptor ,
1646+ selectedValue
1647+ );
1648+ if ( set ) {
1649+ result = selectedValue ;
1650+ interceptor .attributeInitialized ( fetchGroupAttributeDescriptor .getName () );
1651+ }
16001652 }
16011653
1602- }
1654+ LOG . trace ( "Done initializing lazy properties" );
16031655
1604- LOG . trace ( "Done initializing lazy properties" ) ;
1656+ return result ;
16051657
1606- return result ;
1607- }
1608- catch ( JDBCException ex ) {
1609- throw session . getJdbcServices (). getSqlExceptionHelper (). convert (
1610- ex . getSQLException ( ),
1611- "could not initialize lazy properties: " + infoString ( this , id , getFactory () ),
1612- lazySelect . getJdbcSelect (). getSqlString ()
1613- );
1658+ }
1659+ catch ( JDBCException ex ) {
1660+ throw session . getJdbcServices (). getSqlExceptionHelper (). convert (
1661+ ex . getSQLException (),
1662+ "could not initialize lazy properties: " + infoString ( this , id , getFactory () ),
1663+ lazySelect . getJdbcSelect (). getSqlString ()
1664+ );
1665+ }
16141666 }
16151667 }
16161668
@@ -1669,6 +1721,43 @@ protected boolean initializeLazyProperty(
16691721 return fieldName .equals ( lazyPropertyNames [index ] );
16701722 }
16711723
1724+
1725+
1726+ protected boolean initializeLazyProperty (
1727+ final String fieldName ,
1728+ final Object entity ,
1729+ final EntityEntry entry ,
1730+ LazyAttributeDescriptor fetchGroupAttributeDescriptor ,
1731+ final Object propValue ) {
1732+ final String name = fetchGroupAttributeDescriptor .getName ();
1733+ initializeLazyProperty (
1734+ entity ,
1735+ entry ,
1736+ propValue ,
1737+ getPropertyIndex ( name ),
1738+ fetchGroupAttributeDescriptor .getType ()
1739+ );
1740+ return fieldName .equals ( name );
1741+ }
1742+
1743+ private void initializeLazyProperty (Object entity , EntityEntry entry , Object propValue , int index , Type type ) {
1744+ setPropertyValue ( entity , index , propValue );
1745+ if ( entry .getLoadedState () != null ) {
1746+ // object have been loaded with setReadOnly(true); HHH-2236
1747+ entry .getLoadedState ()[index ] = type .deepCopy (
1748+ propValue ,
1749+ factory
1750+ );
1751+ }
1752+ // If the entity has deleted state, then update that as well
1753+ if ( entry .getDeletedState () != null ) {
1754+ entry .getDeletedState ()[index ] = type .deepCopy (
1755+ propValue ,
1756+ factory
1757+ );
1758+ }
1759+ }
1760+
16721761 @ Override
16731762 public NavigableRole getNavigableRole () {
16741763 return navigableRole ;
0 commit comments