Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ org-hibernate-orm-hibernate-core = { group = "org.hibernate.orm", name = "hibern
org-hibernate-orm-hibernate-jcache = { group = "org.hibernate.orm", name = "hibernate-jcache", version.ref = "hibernateOrmVersion" }
org-hibernate-orm-hibernate-jpamodelgen = { group = "org.hibernate.orm", name = "hibernate-jpamodelgen", version.ref = "hibernateOrmVersion" }
org-hibernate-validator-hibernate-validator = { group = "org.hibernate.validator", name = "hibernate-validator", version = "8.0.3.Final" }
org-hibernate-models = { group = "org.hibernate.models", name = "hibernate-models", version = "1.0.1" }
org-jboss-logging-jboss-logging = { group = "org.jboss.logging", name = "jboss-logging", version.ref = "jbossLoggingVersion" }
org-jboss-logging-jboss-logging-annotations = { group = "org.jboss.logging", name = "jboss-logging-annotations", version.ref = "jbossLoggingAnnotationVersion" }
org-jboss-logging-jboss-logging-processor = { group = "org.jboss.logging", name = "jboss-logging-processor", version.ref = "jbossLoggingAnnotationVersion" }
Expand Down
1 change: 1 addition & 0 deletions hibernate-reactive-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ apply from: publishScript
dependencies {

api(libs.org.hibernate.orm.hibernate.core)
compileOnly(libs.org.hibernate.models)

api(libs.io.smallrye.reactive.mutiny)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.reactive.boot.spi;

import org.hibernate.boot.CacheRegionDefinition;
import org.hibernate.boot.archive.scan.spi.ScanEnvironment;
import org.hibernate.boot.archive.scan.spi.ScanOptions;
import org.hibernate.boot.archive.spi.ArchiveDescriptorFactory;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.ClassLoaderAccess;
import org.hibernate.boot.spi.ClassmateContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.jpa.spi.MutableJpaCompliance;
import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver;
import org.hibernate.models.spi.ModelsContext;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.reactive.metamodel.spi.ReactiveManagedTypeRepresentationResolver;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.type.BasicType;
import org.hibernate.type.spi.TypeConfiguration;

import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
* Adapt {@link BootstrapContext#getRepresentationStrategySelector()} to return a {@link ReactiveManagedTypeRepresentationResolver}
*/
public class ReactiveBootstrapContextAdapter implements BootstrapContext {

private final BootstrapContext delegate;

public ReactiveBootstrapContextAdapter(BootstrapContext bootstrapContext) {
this.delegate = bootstrapContext;
}

@Override
public StandardServiceRegistry getServiceRegistry() {
return delegate.getServiceRegistry();
}

@Override
public MutableJpaCompliance getJpaCompliance() {
return delegate.getJpaCompliance();
}

@Override
public TypeConfiguration getTypeConfiguration() {
return delegate.getTypeConfiguration();
}

@Override
public ModelsContext getModelsContext() {
return delegate.getModelsContext();
}

@Override
public SqmFunctionRegistry getFunctionRegistry() {
return delegate.getFunctionRegistry();
}

@Override
public BeanInstanceProducer getCustomTypeProducer() {
return delegate.getCustomTypeProducer();
}

@Override
public MetadataBuildingOptions getMetadataBuildingOptions() {
return delegate.getMetadataBuildingOptions();
}

@Override
public ClassLoaderService getClassLoaderService() {
return delegate.getClassLoaderService();
}

@Override
public ManagedBeanRegistry getManagedBeanRegistry() {
return delegate.getManagedBeanRegistry();
}

@Override
public ConfigurationService getConfigurationService() {
return delegate.getConfigurationService();
}

@Override
public boolean isJpaBootstrap() {
return delegate.isJpaBootstrap();
}

@Override
public void markAsJpaBootstrap() {
delegate.markAsJpaBootstrap();
}

@Override
public ClassLoader getJpaTempClassLoader() {
return delegate.getJpaTempClassLoader();
}

@Override
public ClassLoaderAccess getClassLoaderAccess() {
return delegate.getClassLoaderAccess();
}

@Override
public ClassmateContext getClassmateContext() {
return delegate.getClassmateContext();
}

@Override
public ArchiveDescriptorFactory getArchiveDescriptorFactory() {
return delegate.getArchiveDescriptorFactory();
}

@Override
public ScanOptions getScanOptions() {
return delegate.getScanOptions();
}

@Override
public ScanEnvironment getScanEnvironment() {
return delegate.getScanEnvironment();
}

@Override
public Object getScanner() {
return delegate.getScanner();
}

@Override
public Object getJandexView() {
return delegate.getJandexView();
}

@Override
public Map<String, SqmFunctionDescriptor> getSqlFunctions() {
return delegate.getSqlFunctions();
}

@Override
public Collection<AuxiliaryDatabaseObject> getAuxiliaryDatabaseObjectList() {
return List.of();
}

@Override
public Collection<ConverterDescriptor<?, ?>> getAttributeConverters() {
return delegate.getAttributeConverters();
}

@Override
public Collection<CacheRegionDefinition> getCacheRegionDefinitions() {
return delegate.getCacheRegionDefinitions();
}

@Override
public ManagedTypeRepresentationResolver getRepresentationStrategySelector() {
return ReactiveManagedTypeRepresentationResolver.INSTANCE;
}

@Override
public void release() {
delegate.release();
}

@Override
public void registerAdHocBasicType(BasicType<?> basicType) {
delegate.registerAdHocBasicType( basicType );
}

@Override
public <T> BasicType<T> resolveAdHocBasicType(String key) {
return delegate.resolveAdHocBasicType( key );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.reactive.bythecode.enhance.spi.interceptor;

import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.reactive.logging.impl.Log;
import org.hibernate.reactive.logging.impl.LoggerFactory;

import java.lang.invoke.MethodHandles;

/**
* Reactive version of {@link EnhancementAsProxyLazinessInterceptor}.
*
* It throws a {@link org.hibernate.LazyInitializationException} when a lazy attribute
* is not fetched using {@link org.hibernate.reactive.mutiny.Mutiny#fetch(Object)}
* or {@link org.hibernate.reactive.stage.Stage#fetch(Object)} but transparently
*/
public class ReactiveEnhancementAsProxyLazinessInterceptor extends EnhancementAsProxyLazinessInterceptor {
private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );

public ReactiveEnhancementAsProxyLazinessInterceptor(
EntityRelatedState meta,
EntityKey entityKey,
SharedSessionContractImplementor session) {
super( meta, entityKey, session );
}

@Override
protected Object handleRead(Object target, String attributeName, Object value) {
if ( isIdentifier( attributeName ) ) {
return super.handleRead( target, attributeName, value );
}
else {
throw LOG.lazyFieldInitializationException( attributeName, getEntityName() );
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.reactive.bythecode.enhance.spi.internal;

import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.reactive.logging.impl.Log;
import org.hibernate.reactive.logging.impl.LoggerFactory;

import java.lang.invoke.MethodHandles;

/**
* Reactive version of {@link LazyAttributeLoadingInterceptor}.
*
* It throws a {@link org.hibernate.LazyInitializationException} when a lazy attribute
* is not fetched using {@link org.hibernate.reactive.mutiny.Mutiny#fetch(Object)}
* or {@link org.hibernate.reactive.stage.Stage#fetch(Object)} but transparently
*/
public class ReactiveLazyAttributeLoadingInterceptor extends LazyAttributeLoadingInterceptor {

private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );

public ReactiveLazyAttributeLoadingInterceptor(
EntityRelatedState entityMeta,
Object identifier,
SharedSessionContractImplementor session) {
super( entityMeta, identifier, session );
}

@Override
protected Object handleRead(Object target, String attributeName, Object value) {
if ( !isAttributeLoaded( attributeName ) ) {
throw LOG.lazyFieldInitializationException( attributeName, getEntityName() );
}
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.reactive.bythecode.spi;

import org.hibernate.boot.Metadata;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata;
import org.hibernate.bytecode.internal.BytecodeEnhancementMetadataPojoImpl;
import org.hibernate.bytecode.spi.NotInstrumentedException;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.reactive.bythecode.enhance.spi.interceptor.ReactiveEnhancementAsProxyLazinessInterceptor;
import org.hibernate.reactive.bythecode.enhance.spi.internal.ReactiveLazyAttributeLoadingInterceptor;
import org.hibernate.type.CompositeType;

import java.util.Set;

import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptableType;

/**
* Extends {@link BytecodeEnhancementMetadataPojoImpl} to inject Reactive versions of {@link BytecodeLazyAttributeInterceptor}
*/
public class ReactiveBytecodeEnhancementMetadataPojoImplAdapter extends BytecodeEnhancementMetadataPojoImpl {

public static BytecodeEnhancementMetadataPojoImpl from(
PersistentClass persistentClass,
Set<String> identifierAttributeNames,
CompositeType nonAggregatedCidMapper,
boolean collectionsInDefaultFetchGroupEnabled,
Metadata metadata) {
final Class<?> mappedClass = persistentClass.getMappedClass();
final boolean enhancedForLazyLoading = isPersistentAttributeInterceptableType( mappedClass );
final LazyAttributesMetadata lazyAttributesMetadata = enhancedForLazyLoading
? LazyAttributesMetadata.from( persistentClass, true, collectionsInDefaultFetchGroupEnabled, metadata )
: LazyAttributesMetadata.nonEnhanced( persistentClass.getEntityName() );

return new ReactiveBytecodeEnhancementMetadataPojoImplAdapter(
persistentClass.getEntityName(),
mappedClass,
identifierAttributeNames,
nonAggregatedCidMapper,
enhancedForLazyLoading,
lazyAttributesMetadata
);
}

ReactiveBytecodeEnhancementMetadataPojoImplAdapter(String entityName, Class<?> mappedClass, Set<String> identifierAttributeNames, CompositeType nonAggregatedCidMapper, boolean enhancedForLazyLoading, LazyAttributesMetadata lazyAttributesMetadata) {
super(
entityName,
mappedClass,
identifierAttributeNames,
nonAggregatedCidMapper,
enhancedForLazyLoading,
lazyAttributesMetadata
);
}

@Override
public LazyAttributeLoadingInterceptor injectInterceptor(
Object entity,
Object identifier,
SharedSessionContractImplementor session) throws NotInstrumentedException {
if ( !isEnhancedForLazyLoading() ) {
throw new NotInstrumentedException( "Entity class [" + getEntityClass()
.getName() + "] is not enhanced for lazy loading" );
}

if ( !getEntityClass().isInstance( entity ) ) {
throw new IllegalArgumentException(
String.format(
"Passed entity instance [%s] is not of expected type [%s]",
entity,
getEntityName()
)
);
}
final LazyAttributeLoadingInterceptor interceptor = new ReactiveLazyAttributeLoadingInterceptor(
getLazyAttributeLoadingInterceptorState(),
identifier,
session
);

injectInterceptor( entity, interceptor, session );

return interceptor;
}

@Override
public void injectEnhancedEntityAsProxyInterceptor(
Object entity,
EntityKey entityKey,
SharedSessionContractImplementor session) {
final EnhancementAsProxyLazinessInterceptor.EntityRelatedState meta =
getEnhancementAsProxyLazinessInterceptorMetastate( session );
injectInterceptor(
entity,
new ReactiveEnhancementAsProxyLazinessInterceptor( meta, entityKey, session ),
session
);
}
}
Loading
Loading