Skip to content

Commit c6fa2b1

Browse files
barreirosebersole
authored andcommitted
HHH-8558 - Bytecode enhancer: lazy loading support
1 parent 750d6fb commit c6fa2b1

File tree

10 files changed

+275
-27
lines changed

10 files changed

+275
-27
lines changed

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SimpleDirtyTracker.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
/**
1212
* small low memory class to keep track of changed fields
13-
*
13+
* <p/>
1414
* uses an array as a set (under the assumption that the number of elements will be low) to avoid having to instantiate an HashSet.
1515
* if the assumption does not, hold the array can be kept ordered to reduce the cost of verifying duplicates
1616
*
@@ -25,13 +25,19 @@ public SimpleDirtyTracker() {
2525
}
2626

2727
public void add(String name) {
28-
for (String existing : names) {
28+
if ( !contains( name ) ) {
29+
names = Arrays.copyOf( names, names.length + 1 );
30+
names[names.length - 1] = name;
31+
}
32+
}
33+
34+
public boolean contains(String name) {
35+
for ( String existing : names ) {
2936
if ( existing.equals( name ) ) {
30-
return;
37+
return true;
3138
}
3239
}
33-
names = Arrays.copyOf( names, names.length + 1 );
34-
names[names.length - 1] = name;
40+
return false;
3541
}
3642

3743
public void clear() {

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/tracker/SortedDirtyTracker.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ public void add(String name) {
2626
int insert = 0;
2727
for ( int low = 0, high = names.length - 1; low <= high; ) {
2828
final int middle = low + ( ( high - low ) / 2 );
29-
if ( names[middle].compareTo( name ) > 0 ) {
29+
final int compare = names[middle].compareTo( name );
30+
if ( compare > 0 ) {
3031
// bottom half: higher bound in (middle - 1) and insert position in middle
3132
high = middle - 1;
3233
insert = middle;
3334
}
34-
else if( names[middle].compareTo( name ) < 0 ) {
35+
else if( compare < 0 ) {
3536
// top half: lower bound in (middle + 1) and insert position after middle
3637
insert = low = middle + 1;
3738
}
@@ -46,6 +47,25 @@ else if( names[middle].compareTo( name ) < 0 ) {
4647
names = newNames;
4748
}
4849

50+
public boolean contains(String name) {
51+
for ( int low = 0, high = names.length - 1; low <= high; ) {
52+
final int middle = low + ( ( high - low ) / 2 );
53+
final int compare = names[middle].compareTo( name );
54+
if ( compare > 0 ) {
55+
// bottom half: higher bound in (middle - 1) and insert position in middle
56+
high = middle - 1;
57+
}
58+
else if( compare < 0 ) {
59+
// top half: lower bound in (middle + 1) and insert position after middle
60+
low = middle + 1;
61+
}
62+
else {
63+
return true;
64+
}
65+
}
66+
return false;
67+
}
68+
4969
public void clear() {
5070
names = new String[0];
5171
}

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/Enhancer.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,17 @@
66
*/
77
package org.hibernate.bytecode.enhance.spi;
88

9+
import java.io.ByteArrayInputStream;
10+
import java.io.ByteArrayOutputStream;
11+
import java.io.DataOutputStream;
12+
import java.io.File;
13+
import java.io.IOException;
14+
import javax.tools.JavaFileObject;
15+
916
import javassist.ClassPool;
1017
import javassist.CtClass;
1118
import javassist.LoaderClassPath;
19+
1220
import org.hibernate.HibernateException;
1321
import org.hibernate.bytecode.enhance.internal.CompositeEnhancer;
1422
import org.hibernate.bytecode.enhance.internal.EntityEnhancer;
@@ -21,13 +29,6 @@
2129
import org.hibernate.internal.CoreLogging;
2230
import org.hibernate.internal.CoreMessageLogger;
2331

24-
import javax.tools.JavaFileObject;
25-
import java.io.ByteArrayInputStream;
26-
import java.io.ByteArrayOutputStream;
27-
import java.io.DataOutputStream;
28-
import java.io.File;
29-
import java.io.IOException;
30-
3132
/**
3233
* Class responsible for performing enhancement.
3334
*
@@ -161,10 +162,8 @@ private byte[] getByteCode(CtClass managedCtClass) {
161162
}
162163

163164
protected void addInterceptorHandling(CtClass managedCtClass) {
164-
// interceptor handling is only needed if either:
165-
// a) in-line dirty checking has *not* been requested
166-
// b) class has lazy-loadable attributes
167-
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) && !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) {
165+
// interceptor handling is only needed if class has lazy-loadable attributes
166+
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) {
168167
return;
169168
}
170169
log.debugf( "Weaving in PersistentAttributeInterceptable implementation on [%s]", managedCtClass.getName() );
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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.bytecode.enhance.spi.interceptor;
9+
10+
import java.util.Set;
11+
12+
import org.hibernate.LazyInitializationException;
13+
import org.hibernate.bytecode.enhance.internal.tracker.SimpleDirtyTracker;
14+
import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer;
15+
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
16+
import org.hibernate.engine.spi.SessionImplementor;
17+
18+
/**
19+
* Interceptor that loads attributes lazily
20+
*
21+
* @author Luis Barreiro
22+
*/
23+
public class LazyAttributeLoader implements PersistentAttributeInterceptor {
24+
25+
private final transient SessionImplementor session;
26+
private final Set<String> lazyFields;
27+
private final String entityName;
28+
29+
private final SimpleDirtyTracker initializedFields = new SimpleDirtyTracker();
30+
31+
public LazyAttributeLoader(SessionImplementor session, Set<String> lazyFields, String entityName) {
32+
this.session = session;
33+
this.lazyFields = lazyFields;
34+
this.entityName = entityName;
35+
}
36+
37+
protected final Object intercept(Object target, String fieldName, Object value) {
38+
if ( lazyFields != null && lazyFields.contains( fieldName ) && initializedFields.contains( fieldName ) ) {
39+
if ( session == null ) {
40+
throw new LazyInitializationException( "entity with lazy properties is not associated with a session" );
41+
}
42+
else if ( !session.isOpen() || !session.isConnected() ) {
43+
throw new LazyInitializationException( "session is not connected" );
44+
}
45+
46+
Object loadedValue = ( (LazyPropertyInitializer) session.getFactory()
47+
.getEntityPersister( entityName ) ).initializeLazyProperty(
48+
fieldName,
49+
target,
50+
session
51+
);
52+
53+
initializedFields.add( fieldName );
54+
return loadedValue;
55+
}
56+
else {
57+
return value;
58+
}
59+
}
60+
61+
@Override
62+
public String toString() {
63+
return "LazyAttributeLoader(entityName=" + entityName + " ,lazyFields=" + lazyFields + ')';
64+
}
65+
66+
/* --- */
67+
68+
@Override
69+
public boolean readBoolean(Object obj, String name, boolean oldValue) {
70+
return (Boolean) intercept( obj, name, oldValue );
71+
}
72+
73+
@Override
74+
public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) {
75+
if ( lazyFields != null && lazyFields.contains( name ) ) {
76+
initializedFields.add( name );
77+
}
78+
return newValue;
79+
}
80+
81+
@Override
82+
public byte readByte(Object obj, String name, byte oldValue) {
83+
return (Byte) intercept( obj, name, oldValue );
84+
}
85+
86+
@Override
87+
public byte writeByte(Object obj, String name, byte oldValue, byte newValue) {
88+
if ( lazyFields != null && lazyFields.contains( name ) ) {
89+
initializedFields.add( name );
90+
}
91+
return newValue;
92+
}
93+
94+
@Override
95+
public char readChar(Object obj, String name, char oldValue) {
96+
return (Character) intercept( obj, name, oldValue );
97+
}
98+
99+
@Override
100+
public char writeChar(Object obj, String name, char oldValue, char newValue) {
101+
if ( lazyFields != null && lazyFields.contains( name ) ) {
102+
initializedFields.add( name );
103+
}
104+
return newValue;
105+
}
106+
107+
@Override
108+
public short readShort(Object obj, String name, short oldValue) {
109+
return (Short) intercept( obj, name, oldValue );
110+
}
111+
112+
@Override
113+
public short writeShort(Object obj, String name, short oldValue, short newValue) {
114+
if ( lazyFields != null && lazyFields.contains( name ) ) {
115+
initializedFields.add( name );
116+
}
117+
return newValue;
118+
}
119+
120+
@Override
121+
public int readInt(Object obj, String name, int oldValue) {
122+
return (Integer) intercept( obj, name, oldValue );
123+
}
124+
125+
@Override
126+
public int writeInt(Object obj, String name, int oldValue, int newValue) {
127+
if ( lazyFields != null && lazyFields.contains( name ) ) {
128+
initializedFields.add( name );
129+
}
130+
return newValue;
131+
}
132+
133+
@Override
134+
public float readFloat(Object obj, String name, float oldValue) {
135+
return (Float) intercept( obj, name, oldValue );
136+
}
137+
138+
@Override
139+
public float writeFloat(Object obj, String name, float oldValue, float newValue) {
140+
if ( lazyFields != null && lazyFields.contains( name ) ) {
141+
initializedFields.add( name );
142+
}
143+
return newValue;
144+
}
145+
146+
@Override
147+
public double readDouble(Object obj, String name, double oldValue) {
148+
return (Double) intercept( obj, name, oldValue );
149+
}
150+
151+
@Override
152+
public double writeDouble(Object obj, String name, double oldValue, double newValue) {
153+
if ( lazyFields != null && lazyFields.contains( name ) ) {
154+
initializedFields.add( name );
155+
}
156+
return newValue;
157+
}
158+
159+
@Override
160+
public long readLong(Object obj, String name, long oldValue) {
161+
return (Long) intercept( obj, name, oldValue );
162+
}
163+
164+
@Override
165+
public long writeLong(Object obj, String name, long oldValue, long newValue) {
166+
if ( lazyFields != null && lazyFields.contains( name ) ) {
167+
initializedFields.add( name );
168+
}
169+
return newValue;
170+
}
171+
172+
@Override
173+
public Object readObject(Object obj, String name, Object oldValue) {
174+
return intercept( obj, name, oldValue );
175+
}
176+
177+
@Override
178+
public Object writeObject(Object obj, String name, Object oldValue, Object newValue) {
179+
if ( lazyFields != null && lazyFields.contains( name ) ) {
180+
initializedFields.add( name );
181+
}
182+
return newValue;
183+
}
184+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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+
/**
9+
* interceptor implementations
10+
*/
11+
package org.hibernate.bytecode.enhance.spi.interceptor;

hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ public void bind() {
525525
if ( cascadeStrategy != null && cascadeStrategy.indexOf( "delete-orphan" ) >= 0 ) {
526526
collection.setOrphanDelete( true );
527527
}
528+
binder.setLazy( collection.isLazy() );
528529
binder.setAccessType( accessType );
529530
binder.setProperty( property );
530531
binder.setInsertable( insertable );

hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,8 @@ protected void dirtyCheck(final FlushEntityEvent event) throws HibernateExceptio
485485
if ( entity instanceof SelfDirtinessTracker ) {
486486
if ( ( (SelfDirtinessTracker) entity ).$$_hibernate_hasDirtyAttributes() ) {
487487
dirtyProperties = persister.resolveAttributeIndexes( ( (SelfDirtinessTracker) entity ).$$_hibernate_getDirtyAttributes() );
488+
} else {
489+
dirtyProperties = new int[0];
488490
}
489491
}
490492
else {

hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ public AbstractEntityPersister(
568568

569569
// PROPERTIES
570570

571-
final boolean lazyAvailable = isInstrumented();
571+
final boolean lazyAvailable = isInstrumented() || entityMetamodel.isLazyLoadingBytecodeEnhanced();
572572

573573
int hydrateSpan = entityMetamodel.getPropertySpan();
574574
propertyColumnSpans = new int[hydrateSpan];
@@ -4305,7 +4305,8 @@ public boolean hasSubclasses() {
43054305
}
43064306

43074307
public boolean hasProxy() {
4308-
return entityMetamodel.isLazy();
4308+
// skip proxy instantiation if entity is bytecode enhanced
4309+
return entityMetamodel.isLazy() && !entityMetamodel.isLazyLoadingBytecodeEnhanced();
43094310
}
43104311

43114312
public IdentifierGenerator getIdentifierGenerator() throws HibernateException {

0 commit comments

Comments
 (0)