@@ -397,6 +397,7 @@ public abstract class AbstractEntityPersister
397397 private final int [] lazyPropertyNumbers ;
398398 private final Type [] lazyPropertyTypes ;
399399 private final String [][] lazyPropertyColumnAliases ;
400+ private final Set <String > nonLazyPropertyNames ;
400401
401402 //information about all properties in class hierarchy
402403 private final String [] subclassPropertyNameClosure ;
@@ -495,6 +496,7 @@ public abstract class AbstractEntityPersister
495496 private final boolean implementsLifecycle ;
496497
497498 private List <UniqueKeyEntry > uniqueKeyEntries = null ; //lazily initialized
499+ private HashMap <String ,SingleIdArrayLoadPlan > nonLazyPropertyLoadPlansByName ;
498500
499501 @ Deprecated (since = "6.0" )
500502 public AbstractEntityPersister (
@@ -645,6 +647,7 @@ public AbstractEntityPersister(
645647 propertyColumnUpdateable = new boolean [hydrateSpan ][];
646648 propertyColumnInsertable = new boolean [hydrateSpan ][];
647649 sharedColumnNames = new HashSet <>();
650+ nonLazyPropertyNames = new HashSet <>();
648651
649652 final HashSet <Property > thisClassProperties = new HashSet <>();
650653 final ArrayList <String > lazyNames = new ArrayList <>();
@@ -710,6 +713,9 @@ public AbstractEntityPersister(
710713 lazyTypes .add ( prop .getValue ().getType () );
711714 lazyColAliases .add ( colAliases );
712715 }
716+ else {
717+ nonLazyPropertyNames .add ( prop .getName () );
718+ }
713719
714720 propertyColumnUpdateable [i ] = prop .getValue ().getColumnUpdateability ();
715721 propertyColumnInsertable [i ] = prop .getValue ().getColumnInsertability ();
@@ -1330,6 +1336,10 @@ private SingleIdArrayLoadPlan createLazyLoadPlan(List<LazyAttributeDescriptor> f
13301336 partsToSelect .add ( getAttributeMapping ( getSubclassPropertyIndex ( lazyAttributeDescriptor .getName () ) ) );
13311337 }
13321338
1339+ return createLazyLoanPlan ( partsToSelect );
1340+ }
1341+
1342+ private SingleIdArrayLoadPlan createLazyLoanPlan (List <ModelPart > partsToSelect ) {
13331343 if ( partsToSelect .isEmpty () ) {
13341344 // only one-to-one is lazily fetched
13351345 return null ;
@@ -1655,75 +1665,117 @@ protected Object initializeLazyPropertiesFromDatastore(
16551665 final EntityEntry entry ,
16561666 final String fieldName ,
16571667 final SharedSessionContractImplementor session ) {
1658-
1659- if ( !hasLazyProperties () ) {
1660- throw new AssertionFailure ( "no lazy properties" );
1668+ if ( nonLazyPropertyNames .contains ( fieldName ) ) {
1669+ // An eager property can be lazy because of an applied EntityGraph
1670+ final List <ModelPart > partsToSelect = new ArrayList <>(1 );
1671+ int propertyIndex = getPropertyIndex ( fieldName );
1672+ partsToSelect .add ( getAttributeMapping ( propertyIndex ) );
1673+ SingleIdArrayLoadPlan lazyLoanPlan ;
1674+ if ( nonLazyPropertyLoadPlansByName == null ) {
1675+ nonLazyPropertyLoadPlansByName = new HashMap <>();
1676+ lazyLoanPlan = createLazyLoanPlan ( partsToSelect );
1677+ ;
1678+ nonLazyPropertyLoadPlansByName .put ( fieldName , lazyLoanPlan );
1679+ }
1680+ else {
1681+ lazyLoanPlan = nonLazyPropertyLoadPlansByName .get ( fieldName );
1682+ if ( lazyLoanPlan == null ) {
1683+ lazyLoanPlan = createLazyLoanPlan ( partsToSelect );
1684+ ;
1685+ nonLazyPropertyLoadPlansByName .put ( fieldName , lazyLoanPlan );
1686+ }
1687+ }
1688+ try {
1689+ final Object [] values = lazyLoanPlan .load ( id , session );
1690+ final Object selectedValue = values [0 ];
1691+ initializeLazyProperty (
1692+ entity ,
1693+ entry ,
1694+ selectedValue ,
1695+ propertyIndex ,
1696+ getPropertyTypes ()[propertyIndex ]
1697+ );
1698+ return selectedValue ;
1699+ }
1700+ catch (JDBCException ex ) {
1701+ throw session .getJdbcServices ().getSqlExceptionHelper ().convert (
1702+ ex .getSQLException (),
1703+ "could not initialize lazy properties: " + infoString ( this , id , getFactory () ),
1704+ lazyLoanPlan .getJdbcSelect ().getSqlString ()
1705+ );
1706+ }
16611707 }
1708+ else {
1709+ if ( !hasLazyProperties () ) {
1710+ throw new AssertionFailure ( "no lazy properties" );
1711+ }
16621712
1663- final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable ( entity ).$$_hibernate_getInterceptor ();
1664- assert interceptor != null : "Expecting bytecode interceptor to be non-null" ;
1713+ final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable ( entity ).$$_hibernate_getInterceptor ();
1714+ assert interceptor != null : "Expecting bytecode interceptor to be non-null" ;
16651715
1666- LOG .tracef ( "Initializing lazy properties from datastore (triggered for `%s`)" , fieldName );
1716+ LOG .tracef ( "Initializing lazy properties from datastore (triggered for `%s`)" , fieldName );
16671717
1668- final String fetchGroup = getEntityMetamodel ().getBytecodeEnhancementMetadata ()
1669- .getLazyAttributesMetadata ()
1670- .getFetchGroupName ( fieldName );
1671- final List <LazyAttributeDescriptor > fetchGroupAttributeDescriptors = getEntityMetamodel ().getBytecodeEnhancementMetadata ()
1672- .getLazyAttributesMetadata ()
1673- .getFetchGroupAttributeDescriptors ( fetchGroup );
1718+ final String fetchGroup = getEntityMetamodel ().getBytecodeEnhancementMetadata ()
1719+ .getLazyAttributesMetadata ()
1720+ .getFetchGroupName ( fieldName );
1721+ final List <LazyAttributeDescriptor > fetchGroupAttributeDescriptors = getEntityMetamodel ().getBytecodeEnhancementMetadata ()
1722+ .getLazyAttributesMetadata ()
1723+ .getFetchGroupAttributeDescriptors ( fetchGroup );
16741724
1675- final Set <String > initializedLazyAttributeNames = interceptor .getInitializedLazyAttributeNames ();
1725+ final Set <String > initializedLazyAttributeNames = interceptor .getInitializedLazyAttributeNames ();
16761726
1677- final SingleIdArrayLoadPlan lazySelect = getSQLLazySelectLoadPlan ( fetchGroup );
1727+ final SingleIdArrayLoadPlan lazySelect = getSQLLazySelectLoadPlan ( fetchGroup );
16781728
1679- try {
1680- Object result = null ;
1681- final Object [] values = lazySelect .load ( id , session );
1682- int i = 0 ;
1683- for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) {
1684- final boolean previousInitialized = initializedLazyAttributeNames .contains ( fetchGroupAttributeDescriptor .getName () );
1685-
1686- if ( previousInitialized ) {
1687- // todo : one thing we should consider here is potentially un-marking an attribute as dirty based on the selected value
1688- // we know the current value - getPropertyValue( entity, fetchGroupAttributeDescriptor.getAttributeIndex() );
1689- // we know the selected value (see selectedValue below)
1690- // we can use the attribute Type to tell us if they are the same
1691- //
1692- // assuming entity is a SelfDirtinessTracker we can also know if the attribute is
1693- // currently considered dirty, and if really not dirty we would do the un-marking
1694- //
1695- // of course that would mean a new method on SelfDirtinessTracker to allow un-marking
1696-
1697- // its already been initialized (e.g. by a write) so we don't want to overwrite
1698- i ++;
1699- continue ;
1700- }
1729+ try {
1730+ Object result = null ;
1731+ final Object [] values = lazySelect .load ( id , session );
1732+ int i = 0 ;
1733+ for ( LazyAttributeDescriptor fetchGroupAttributeDescriptor : fetchGroupAttributeDescriptors ) {
1734+ final boolean previousInitialized = initializedLazyAttributeNames .contains (
1735+ fetchGroupAttributeDescriptor .getName () );
1736+
1737+ if ( previousInitialized ) {
1738+ // todo : one thing we should consider here is potentially un-marking an attribute as dirty based on the selected value
1739+ // we know the current value - getPropertyValue( entity, fetchGroupAttributeDescriptor.getAttributeIndex() );
1740+ // we know the selected value (see selectedValue below)
1741+ // we can use the attribute Type to tell us if they are the same
1742+ //
1743+ // assuming entity is a SelfDirtinessTracker we can also know if the attribute is
1744+ // currently considered dirty, and if really not dirty we would do the un-marking
1745+ //
1746+ // of course that would mean a new method on SelfDirtinessTracker to allow un-marking
1747+
1748+ // its already been initialized (e.g. by a write) so we don't want to overwrite
1749+ i ++;
1750+ continue ;
1751+ }
17011752
1702- final Object selectedValue = values [i ++];
1703- final boolean set = initializeLazyProperty (
1704- fieldName ,
1705- entity ,
1706- entry ,
1707- fetchGroupAttributeDescriptor .getLazyIndex (),
1708- selectedValue
1709- );
1710- if ( set ) {
1711- result = selectedValue ;
1712- interceptor .attributeInitialized ( fetchGroupAttributeDescriptor .getName () );
1753+ final Object selectedValue = values [i ++];
1754+ final boolean set = initializeLazyProperty (
1755+ fieldName ,
1756+ entity ,
1757+ entry ,
1758+ fetchGroupAttributeDescriptor ,
1759+ selectedValue
1760+ );
1761+ if ( set ) {
1762+ result = selectedValue ;
1763+ interceptor .attributeInitialized ( fetchGroupAttributeDescriptor .getName () );
1764+ }
17131765 }
17141766
1715- }
1767+ LOG . trace ( "Done initializing lazy properties" );
17161768
1717- LOG . trace ( "Done initializing lazy properties" ) ;
1769+ return result ;
17181770
1719- return result ;
1720- }
1721- catch ( JDBCException ex ) {
1722- throw session . getJdbcServices (). getSqlExceptionHelper (). convert (
1723- ex . getSQLException ( ),
1724- "could not initialize lazy properties: " + infoString ( this , id , getFactory () ),
1725- lazySelect . getJdbcSelect (). getSqlString ()
1726- );
1771+ }
1772+ catch ( JDBCException ex ) {
1773+ throw session . getJdbcServices (). getSqlExceptionHelper (). convert (
1774+ ex . getSQLException (),
1775+ "could not initialize lazy properties: " + infoString ( this , id , getFactory () ),
1776+ lazySelect . getJdbcSelect (). getSqlString ()
1777+ );
1778+ }
17271779 }
17281780 }
17291781
@@ -1782,6 +1834,43 @@ protected boolean initializeLazyProperty(
17821834 return fieldName .equals ( lazyPropertyNames [index ] );
17831835 }
17841836
1837+
1838+
1839+ protected boolean initializeLazyProperty (
1840+ final String fieldName ,
1841+ final Object entity ,
1842+ final EntityEntry entry ,
1843+ LazyAttributeDescriptor fetchGroupAttributeDescriptor ,
1844+ final Object propValue ) {
1845+ final String name = fetchGroupAttributeDescriptor .getName ();
1846+ initializeLazyProperty (
1847+ entity ,
1848+ entry ,
1849+ propValue ,
1850+ getPropertyIndex ( name ),
1851+ fetchGroupAttributeDescriptor .getType ()
1852+ );
1853+ return fieldName .equals ( name );
1854+ }
1855+
1856+ private void initializeLazyProperty (Object entity , EntityEntry entry , Object propValue , int index , Type type ) {
1857+ setPropertyValue ( entity , index , propValue );
1858+ if ( entry .getLoadedState () != null ) {
1859+ // object have been loaded with setReadOnly(true); HHH-2236
1860+ entry .getLoadedState ()[index ] = type .deepCopy (
1861+ propValue ,
1862+ factory
1863+ );
1864+ }
1865+ // If the entity has deleted state, then update that as well
1866+ if ( entry .getDeletedState () != null ) {
1867+ entry .getDeletedState ()[index ] = type .deepCopy (
1868+ propValue ,
1869+ factory
1870+ );
1871+ }
1872+ }
1873+
17851874 @ Override
17861875 public NavigableRole getNavigableRole () {
17871876 return navigableRole ;
@@ -3227,7 +3316,7 @@ else if ( discriminatorValue == NOT_NULL_DISCRIMINATOR ) {
32273316 return new NullnessPredicate ( sqlExpression , true );
32283317 }
32293318 else if ( hasNull ) {
3230- junction .add ( new NullnessPredicate ( sqlExpression ) );
3319+ junction .add ( new NullnessPredicate ( sqlExpression ) );
32313320 }
32323321
32333322 junction .add ( predicate );
0 commit comments