Skip to content

Commit 72e2da2

Browse files
beikovsebersole
authored andcommitted
HHH-18271 Introduce lazy bitset for entities and other initializer improvements
* Cache more state in initializers * Reduce mega-morphic call sites * Do more efficient state resolving for query cache entries
1 parent 55702e4 commit 72e2da2

File tree

42 files changed

+1219
-706
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1219
-706
lines changed

hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ private void handleGeneratedProperties(EntityEntry entry, GeneratedValues genera
266266
nextVersion = getVersion( state, persister );
267267
}
268268
entry.postUpdate( instance, state, nextVersion );
269+
entry.setMaybeLazySet( null );
269270
}
270271
}
271272

hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@
2727
import org.hibernate.engine.spi.SessionImplementor;
2828
import org.hibernate.engine.spi.SharedSessionContractImplementor;
2929
import org.hibernate.engine.spi.Status;
30+
import org.hibernate.internal.util.ImmutableBitSet;
3031
import org.hibernate.metamodel.mapping.AttributeMapping;
3132
import org.hibernate.persister.entity.EntityPersister;
3233
import org.hibernate.type.TypeHelper;
3334

35+
import org.checkerframework.checker.nullness.qual.Nullable;
36+
3437
import static org.hibernate.LockMode.PESSIMISTIC_FORCE_INCREMENT;
3538
import static org.hibernate.engine.internal.AbstractEntityEntry.BooleanState.EXISTS_IN_DATABASE;
3639
import static org.hibernate.engine.internal.AbstractEntityEntry.BooleanState.IS_BEING_REPLICATED;
@@ -71,6 +74,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
7174
protected transient EntityKey cachedEntityKey; // cached EntityKey (lazy-initialized)
7275
protected final transient Object rowId;
7376
protected final transient PersistenceContext persistenceContext;
77+
protected transient @Nullable ImmutableBitSet maybeLazySet;
7478
protected EntityEntryExtraState next;
7579

7680
/**
@@ -459,6 +463,16 @@ public void setReadOnly(boolean readOnly, Object entity) {
459463
}
460464
}
461465

466+
@Override
467+
public @Nullable ImmutableBitSet getMaybeLazySet() {
468+
return maybeLazySet;
469+
}
470+
471+
@Override
472+
public void setMaybeLazySet(@Nullable ImmutableBitSet maybeLazySet) {
473+
this.maybeLazySet = maybeLazySet;
474+
}
475+
462476
@Override
463477
public String toString() {
464478
return "EntityEntry"

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@
99
import java.io.IOException;
1010
import java.io.ObjectOutputStream;
1111

12+
import org.hibernate.Internal;
1213
import org.hibernate.LockMode;
1314
import org.hibernate.collection.spi.PersistentCollection;
15+
import org.hibernate.internal.util.ImmutableBitSet;
1416
import org.hibernate.persister.entity.EntityPersister;
1517

18+
import org.checkerframework.checker.nullness.qual.Nullable;
19+
1620
/**
1721
* Information about the current state of a managed entity instance with respect
1822
* to its persistent state.
@@ -130,6 +134,16 @@ public interface EntityEntry {
130134

131135
void setReadOnly(boolean readOnly, Object entity);
132136

137+
/**
138+
* Has a bit set for every attribute position that is potentially lazy.
139+
* When {@code null}, no knowledge is available and every attribute must be assumed potentially lazy.
140+
*/
141+
@Internal
142+
@Nullable ImmutableBitSet getMaybeLazySet();
143+
144+
@Internal
145+
void setMaybeLazySet(@Nullable ImmutableBitSet maybeLazySet);
146+
133147
@Override
134148
String toString();
135149

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
5+
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
6+
*/
7+
8+
package org.hibernate.internal.util;
9+
10+
import java.util.Arrays;
11+
import java.util.BitSet;
12+
13+
/**
14+
* An immutable variant of the {@link BitSet} class with some additional operations.
15+
*/
16+
public class ImmutableBitSet {
17+
18+
public static final ImmutableBitSet EMPTY = new ImmutableBitSet( new long[0] );
19+
20+
private static final int ADDRESS_BITS_PER_WORD = 6;
21+
22+
private final long[] words;
23+
24+
private static int wordIndex(int bitIndex) {
25+
return bitIndex >> ADDRESS_BITS_PER_WORD;
26+
}
27+
28+
private ImmutableBitSet(long[] words) {
29+
this.words = words;
30+
}
31+
32+
public static ImmutableBitSet valueOf(BitSet bitSet) {
33+
final long[] words = bitSet.toLongArray();
34+
return words.length == 0 ? EMPTY : new ImmutableBitSet( words );
35+
}
36+
37+
public boolean get(int bitIndex) {
38+
int wordIndex = wordIndex( bitIndex );
39+
return wordIndex < words.length && ( ( words[wordIndex] & ( 1L << bitIndex ) ) != 0 );
40+
}
41+
42+
public boolean isEmpty() {
43+
return this == EMPTY;
44+
}
45+
46+
public boolean contains(ImmutableBitSet set) {
47+
if ( words.length < set.words.length ) {
48+
return false;
49+
}
50+
// We check if every word from the given set is also contained in this set
51+
for ( int i = 0; i < set.words.length; i++ ) {
52+
if ( words[i] != ( set.words[i] | words[i] ) ) {
53+
return false;
54+
}
55+
}
56+
return true;
57+
}
58+
59+
@Override
60+
public int hashCode() {
61+
return Arrays.hashCode( words );
62+
}
63+
64+
@Override
65+
public boolean equals(Object obj) {
66+
if ( !( obj instanceof ImmutableBitSet ) ) {
67+
return false;
68+
}
69+
if ( this == obj ) {
70+
return true;
71+
}
72+
73+
final ImmutableBitSet set = (ImmutableBitSet) obj;
74+
return Arrays.equals( words, set.words );
75+
}
76+
77+
}

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmMapEntryResult.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,11 @@ public <X> void forEachResultAssembler(BiConsumer<Initializer<?>, X> consumer, X
102102
keyAssembler.forEachResultAssembler( consumer, arg );
103103
valueAssembler.forEachResultAssembler( consumer, arg );
104104
}
105+
106+
@Override
107+
public void resolveState(RowProcessingState rowProcessingState) {
108+
keyAssembler.resolveState( rowProcessingState );
109+
valueAssembler.resolveState( rowProcessingState );
110+
}
105111
}
106112
}

hibernate-core/src/main/java/org/hibernate/sql/results/graph/Initializer.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ public interface Initializer<Data extends InitializerData> {
6464
ModelPart getInitializedPart();
6565

6666
default Object getResolvedInstance(Data data) {
67-
return data.getState() == State.RESOLVED || data.getState() == State.INITIALIZED ? data.getInstance() : null;
67+
assert data.getState() != State.UNINITIALIZED
68+
&& data.getState() != State.KEY_RESOLVED
69+
&& ( data.getState() != State.MISSING || data.getInstance() == null );
70+
return data.getInstance();
6871
}
6972
default Object getResolvedInstance(RowProcessingState rowProcessingState) {
7073
return getResolvedInstance( getData( rowProcessingState ) );
@@ -126,6 +129,12 @@ default void resolveInstance(RowProcessingState rowProcessingState) {
126129
resolveInstance( getData( rowProcessingState ) );
127130
}
128131

132+
void resolveState(Data data);
133+
134+
default void resolveState(RowProcessingState rowProcessingState) {
135+
resolveState( getData( rowProcessingState ) );
136+
}
137+
129138
/**
130139
* Step 2.2 - Use the given instance as resolved instance for this initializer.
131140
* Initializers are supposed to recursively call this method for sub-initializers.
@@ -206,6 +215,16 @@ static boolean isPartOfKey(NavigablePath navigablePath, InitializerParent<?> par
206215
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() );
207216
}
208217

218+
/**
219+
* Indicates whether calling resolve is needed when the object for this initializer is initialized already.
220+
*/
221+
boolean isEager();
222+
223+
/**
224+
* Indicates whether this initializers has eager sub-initializers, that could initialize lazy state of existing objects.
225+
*/
226+
boolean hasEagerSubInitializers();
227+
209228
/**
210229
* Indicates if this is a result or fetch initializer.
211230
*/

0 commit comments

Comments
 (0)