diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java index 73ddc379f4f241..9b9636a16dc123 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java @@ -103,6 +103,7 @@ import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; +import io.quarkus.deployment.builditem.TransformedClassesBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; @@ -143,7 +144,9 @@ import io.quarkus.runtime.configuration.ConfigUtils; import io.quarkus.runtime.configuration.ConfigurationException; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.pool.TypePool; /** * Simulacrum of JPA bootstrap. @@ -496,6 +499,7 @@ public void defineJpaEntities( public ProxyDefinitionsBuildItem pregenProxies( JpaModelBuildItem jpaModel, JpaModelIndexBuildItem indexBuildItem, + TransformedClassesBuildItem transformedClassesBuildItem, List persistenceUnitDescriptorBuildItems, BuildProducer generatedClassBuildItemBuildProducer, LiveReloadBuildItem liveReloadBuildItem) { @@ -508,7 +512,8 @@ public ProxyDefinitionsBuildItem pregenProxies( managedClassAndPackageNames.addAll(pud.getManagedClassNames()); } PreGeneratedProxies proxyDefinitions = generatedProxies(managedClassAndPackageNames, - indexBuildItem.getIndex(), generatedClassBuildItemBuildProducer, liveReloadBuildItem); + indexBuildItem.getIndex(), transformedClassesBuildItem, + generatedClassBuildItemBuildProducer, liveReloadBuildItem); return new ProxyDefinitionsBuildItem(proxyDefinitions); } @@ -708,7 +713,6 @@ public HibernateModelClassCandidatesForFieldAccessBuildItem candidatesForFieldAc @Record(STATIC_INIT) public void build(HibernateOrmRecorder recorder, HibernateOrmConfig hibernateOrmConfig, BuildProducer jpaModelPersistenceUnitMapping, - BuildProducer buildProducer, BuildProducer syntheticBeans, List descriptors, JpaModelBuildItem jpaModel) throws Exception { @@ -1481,6 +1485,7 @@ private static MultiTenancyStrategy getMultiTenancyStrategy(Optional mul } private PreGeneratedProxies generatedProxies(Set managedClassAndPackageNames, IndexView combinedIndex, + TransformedClassesBuildItem transformedClassesBuildItem, BuildProducer generatedClassBuildItemBuildProducer, LiveReloadBuildItem liveReloadBuildItem) { ProxyCache proxyCache = liveReloadBuildItem.getContextObject(ProxyCache.class); @@ -1505,7 +1510,9 @@ private PreGeneratedProxies generatedProxies(Set managedClassAndPackageN } proxyAnnotations.put(i.target().asClass().name().toString(), proxyClass.asClass().name().toString()); } - try (ProxyBuildingHelper proxyHelper = new ProxyBuildingHelper(Thread.currentThread().getContextClassLoader())) { + TypePool transformedClassesTypePool = createTransformedClassesTypePool(transformedClassesBuildItem, + managedClassAndPackageNames); + try (ProxyBuildingHelper proxyHelper = new ProxyBuildingHelper(transformedClassesTypePool)) { for (String managedClassOrPackageName : managedClassAndPackageNames) { CachedProxy result; if (proxyCache.cache.containsKey(managedClassOrPackageName) @@ -1552,6 +1559,27 @@ private PreGeneratedProxies generatedProxies(Set managedClassAndPackageN return preGeneratedProxies; } + // Creates a TypePool that is aware of class transformations applied to entity classes, + // so that ByteBuddy can take these transformations into account. + // This is especially important when getters/setters are added to entity classes, + // because we want those methods to be overridden in proxies to trigger proxy initialization. + private TypePool createTransformedClassesTypePool(TransformedClassesBuildItem transformedClassesBuildItem, + Set entityClasses) { + Map transformedClasses = new HashMap<>(); + for (Set transformedClassSet : transformedClassesBuildItem + .getTransformedClassesByJar().values()) { + for (TransformedClassesBuildItem.TransformedClass transformedClass : transformedClassSet) { + String className = transformedClass.getClassName(); + if (entityClasses.contains(className)) { + transformedClasses.put(className, transformedClass.getData()); + } + } + } + return TypePool.Default.of(new ClassFileLocator.Compound( + new ClassFileLocator.Simple(transformedClasses), + ClassFileLocator.ForClassLoader.of(Thread.currentThread().getContextClassLoader()))); + } + private boolean isModified(String entity, Set changedClasses, IndexView index) { if (changedClasses.contains(entity)) { return true; diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java index 3419488fa8a2cd..dc6c27bd947fbf 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java @@ -1,13 +1,18 @@ package io.quarkus.hibernate.orm.deployment; -import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl; import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper; import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.description.type.TypeDefinition; +import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.matcher.ElementMatchers; +import net.bytebuddy.pool.TypePool; /** * Makes it slightly more readable to interact with the Hibernate @@ -15,22 +20,21 @@ */ final class ProxyBuildingHelper implements AutoCloseable { - private final ClassLoader contextClassLoader; + private final TypePool typePool; private ByteBuddyProxyHelper byteBuddyProxyHelper; private BytecodeProviderImpl bytecodeProvider; - public ProxyBuildingHelper(ClassLoader contextClassLoader) { - this.contextClassLoader = contextClassLoader; + public ProxyBuildingHelper(TypePool typePool) { + this.typePool = typePool; } public DynamicType.Unloaded buildUnloadedProxy(String mappedClassName, Set interfaceNames) { - final Class[] interfaces = new Class[interfaceNames.size()]; + List interfaces = new ArrayList<>(); int i = 0; for (String name : interfaceNames) { - interfaces[i++] = uninitializedClass(name); + interfaces.add(typePool.describe(name).resolve()); } - final Class mappedClass = uninitializedClass(mappedClassName); - return getByteBuddyProxyHelper().buildUnloadedProxy(mappedClass, interfaces); + return getByteBuddyProxyHelper().buildUnloadedProxy(typePool, typePool.describe(mappedClassName).resolve(), interfaces); } private ByteBuddyProxyHelper getByteBuddyProxyHelper() { @@ -43,32 +47,20 @@ private ByteBuddyProxyHelper getByteBuddyProxyHelper() { return this.byteBuddyProxyHelper; } - private Class uninitializedClass(String entity) { - try { - return Class.forName(entity, false, contextClassLoader); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - public boolean isProxiable(String managedClassOrPackageName) { - Class mappedClass; - try { - mappedClass = Class.forName(managedClassOrPackageName, false, contextClassLoader); - } catch (ClassNotFoundException e) { + TypePool.Resolution mappedClassResolution = typePool.describe(managedClassOrPackageName); + if (!mappedClassResolution.isResolved()) { // Probably a package name - consider it's not proxiable. return false; } - if (Modifier.isFinal(mappedClass.getModifiers())) { - return false; - } - try { - mappedClass.getDeclaredConstructor(); - } catch (NoSuchMethodException e) { - return false; - } - return true; + TypeDescription mappedClass = mappedClassResolution.resolve(); + + return !mappedClass.isFinal() + && !mappedClass.getDeclaredMethods() + .filter(ElementMatchers.isConstructor() + .and(ElementMatchers.takesNoArguments())) + .isEmpty(); } @Override