@@ -378,6 +378,7 @@ public abstract class AbstractEntityPersister
378
378
private final String [] lazyPropertyNames ;
379
379
private final int [] lazyPropertyNumbers ;
380
380
private final Type [] lazyPropertyTypes ;
381
+ private final Set <String > nonLazyPropertyNames ;
381
382
382
383
//information about all properties in class hierarchy
383
384
private final String [] subclassPropertyNameClosure ;
@@ -469,6 +470,7 @@ public abstract class AbstractEntityPersister
469
470
private final boolean implementsLifecycle ;
470
471
471
472
private List <UniqueKeyEntry > uniqueKeyEntries = null ; //lazily initialized
473
+ private HashMap <String ,SingleIdArrayLoadPlan > nonLazyPropertyLoadPlansByName ;
472
474
473
475
public AbstractEntityPersister (
474
476
final PersistentClass persistentClass ,
@@ -606,6 +608,7 @@ public AbstractEntityPersister(
606
608
propertyColumnUpdateable = new boolean [hydrateSpan ][];
607
609
propertyColumnInsertable = new boolean [hydrateSpan ][];
608
610
sharedColumnNames = new HashSet <>();
611
+ nonLazyPropertyNames = new HashSet <>();
609
612
610
613
final HashSet <Property > thisClassProperties = new HashSet <>();
611
614
final ArrayList <String > lazyNames = new ArrayList <>();
@@ -663,6 +666,9 @@ public AbstractEntityPersister(
663
666
lazyNumbers .add ( i );
664
667
lazyTypes .add ( prop .getValue ().getType () );
665
668
}
669
+ else {
670
+ nonLazyPropertyNames .add ( prop .getName () );
671
+ }
666
672
667
673
propertyColumnUpdateable [i ] = prop .getValue ().getColumnUpdateability ();
668
674
propertyColumnInsertable [i ] = prop .getValue ().getColumnInsertability ();
@@ -1222,6 +1228,10 @@ private SingleIdArrayLoadPlan createLazyLoadPlan(List<LazyAttributeDescriptor> f
1222
1228
partsToSelect .add ( getAttributeMapping ( getSubclassPropertyIndex ( lazyAttributeDescriptor .getName () ) ) );
1223
1229
}
1224
1230
1231
+ return createLazyLoanPlan ( partsToSelect );
1232
+ }
1233
+
1234
+ private SingleIdArrayLoadPlan createLazyLoanPlan (List <ModelPart > partsToSelect ) {
1225
1235
if ( partsToSelect .isEmpty () ) {
1226
1236
// only one-to-one is lazily fetched
1227
1237
return null ;
@@ -1542,75 +1552,117 @@ protected Object initializeLazyPropertiesFromDatastore(
1542
1552
final EntityEntry entry ,
1543
1553
final String fieldName ,
1544
1554
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
+ }
1548
1594
}
1595
+ else {
1596
+ if ( !hasLazyProperties () ) {
1597
+ throw new AssertionFailure ( "no lazy properties" );
1598
+ }
1549
1599
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" ;
1552
1602
1553
- LOG .tracef ( "Initializing lazy properties from datastore (triggered for `%s`)" , fieldName );
1603
+ LOG .tracef ( "Initializing lazy properties from datastore (triggered for `%s`)" , fieldName );
1554
1604
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 );
1561
1611
1562
- final Set <String > initializedLazyAttributeNames = interceptor .getInitializedLazyAttributeNames ();
1612
+ final Set <String > initializedLazyAttributeNames = interceptor .getInitializedLazyAttributeNames ();
1563
1613
1564
- final SingleIdArrayLoadPlan lazySelect = getSQLLazySelectLoadPlan ( fetchGroup );
1614
+ final SingleIdArrayLoadPlan lazySelect = getSQLLazySelectLoadPlan ( fetchGroup );
1565
1615
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
+ }
1588
1639
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
+ }
1600
1652
}
1601
1653
1602
- }
1654
+ LOG . trace ( "Done initializing lazy properties" );
1603
1655
1604
- LOG . trace ( "Done initializing lazy properties" ) ;
1656
+ return result ;
1605
1657
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
+ }
1614
1666
}
1615
1667
}
1616
1668
@@ -1669,6 +1721,43 @@ protected boolean initializeLazyProperty(
1669
1721
return fieldName .equals ( lazyPropertyNames [index ] );
1670
1722
}
1671
1723
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
+
1672
1761
@ Override
1673
1762
public NavigableRole getNavigableRole () {
1674
1763
return navigableRole ;
0 commit comments