@@ -123,6 +123,14 @@ public void __internalSet(String property, Object value) {
123123 }
124124 }
125125
126+ @ FrameworkInternal ("Mutation transaction rollback only" )
127+ public void __internalRestorePersistenceState (
128+ Long restoredVersion , EntityStatus restoredStatus , boolean versionWasLoaded ) {
129+ this .version = restoredVersion ;
130+ this .$status = restoredStatus ;
131+ restorePropertyLoaded (VERSION_PROPERTY , versionWasLoaded );
132+ }
133+
126134 @ FrameworkInternal ("Business code should use typed getXxx() methods" )
127135 public Object __internalGet (String property ) {
128136 // First try to get from entityMutationLedger if available
@@ -393,6 +401,17 @@ public void __internalHydrate(String propertyName, Object value, int loadedPrope
393401 hydratingProperty = false ;
394402 }
395403 markPropertyLoaded (loadedPropertyIndex , propertyName );
404+ captureHydratedOriginalVersion ();
405+ }
406+
407+ /**
408+ * Hydration may assign {@code id} and {@code version} in either column order.
409+ * Once both are available, retain the authoritative database version in the
410+ * entity-owned Mutation Ledger without creating a business mutation.
411+ */
412+ private void captureHydratedOriginalVersion () {
413+ if (entityMutationLedger == null || id == null || version == null ) return ;
414+ entityMutationLedger .setOriginalVersion (new EntityKey (typeName (), id ), version );
396415 }
397416
398417 private void markPropertyLoaded (int index , String propertyName ) {
@@ -404,6 +423,28 @@ private void markPropertyLoaded(int index, String propertyName) {
404423 overflowLoadedProperties .add (propertyName );
405424 }
406425
426+ private void restorePropertyLoaded (String propertyName , boolean loaded ) {
427+ Integer index = LOADED_PROPERTY_LAYOUTS .get (getClass ()).find (propertyName );
428+ if (index == null ) {
429+ if (loaded ) {
430+ if (overflowLoadedProperties == null ) overflowLoadedProperties = new java .util .HashSet <>();
431+ overflowLoadedProperties .add (propertyName );
432+ } else if (overflowLoadedProperties != null ) {
433+ overflowLoadedProperties .remove (propertyName );
434+ }
435+ return ;
436+ }
437+ if (index < Long .SIZE ) {
438+ if (loaded ) loadedPropertyBits |= 1L << index ;
439+ else loadedPropertyBits &= ~(1L << index );
440+ } else if (loaded ) {
441+ if (overflowLoadedProperties == null ) overflowLoadedProperties = new java .util .HashSet <>();
442+ overflowLoadedProperties .add (propertyName );
443+ } else if (overflowLoadedProperties != null ) {
444+ overflowLoadedProperties .remove (propertyName );
445+ }
446+ }
447+
407448 private static final class LoadedPropertyLayout {
408449 private final ConcurrentHashMap <String , Integer > indexes = new ConcurrentHashMap <>();
409450 private final java .util .concurrent .atomic .AtomicInteger nextIndex =
0 commit comments