Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
Expand Down Expand Up @@ -134,20 +135,27 @@ public ResultSetAdaptor(RowSet<Row> rows) {
this.columnDescriptors = rows.columnDescriptors();
}

public ResultSetAdaptor(RowSet<Row> rows, PropertyKind<Row> propertyKind, String idColumnName, Class<?> idClass) {
this( rows, rows.property( propertyKind ), idColumnName, idClass );
public ResultSetAdaptor(RowSet<Row> rows, PropertyKind<Row> propertyKind, List<String> generatedColumnNames, List<Class<?>> generatedColumnClasses) {
this( rows, rows.property( propertyKind ), generatedColumnNames, generatedColumnClasses );
}

public ResultSetAdaptor(RowSet<Row> rows, Collection<?> ids, String idColumnName, Class<?> idClass) {
this( rows, new RowFromId( ids, idColumnName ), idColumnName, idClass );
}

private ResultSetAdaptor(RowSet<Row> rows, Row row, String idColumnName, Class<?> idClass) {
this( rows, row, List.of( idColumnName ), List.of( idClass ) );
}

private ResultSetAdaptor(RowSet<Row> rows, Row row, List<String> columnNames, List<Class<?>> columnClasses) {
requireNonNull( rows );
requireNonNull( idColumnName );
requireNonNull( columnNames );
this.iterator = List.of( row ).iterator();
this.columnNames = List.of( idColumnName );
this.columnDescriptors = List.of( toColumnDescriptor( idClass, idColumnName ) );
this.columnNames = columnNames ;
this.columnDescriptors = new ArrayList<>(columnNames.size());
for (int i =0; i < columnNames.size(); i++) {
columnDescriptors.add( toColumnDescriptor( columnClasses.get( i ), columnNames.get(i) ) );
}
}

private static ColumnDescriptor toColumnDescriptor(Class<?> idClass, String idColumnName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.hibernate.event.spi.EventSource;
import org.hibernate.generator.values.GeneratedValues;
import org.hibernate.internal.util.NullnessUtil;
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister;
import org.hibernate.stat.spi.StatisticsImplementor;
Expand All @@ -31,6 +32,7 @@ public class ReactiveEntityIdentityInsertAction extends EntityIdentityInsertActi
private final boolean isVersionIncrementDisabled;
private boolean executed;
private boolean transientReferencesNullified;
private Object rowId;

public ReactiveEntityIdentityInsertAction(
Object[] state,
Expand Down Expand Up @@ -108,6 +110,13 @@ private CompletionStage<Void> processInsertGeneratedProperties(
Object instance,
GeneratedValues generatedValues,
SharedSessionContractImplementor session) {
final EntityRowIdMapping rowIdMapping = persister.getRowIdMapping();
if ( rowIdMapping != null ) {
rowId = generatedValues.getGeneratedValue( rowIdMapping );
if ( rowId != null && !isEarlyInsert() ) {
session.getPersistenceContext().replaceEntityEntryRowId( getInstance(), rowId );
}
}
return persister.hasInsertGeneratedProperties()
? persister.reactiveProcessInsertGenerated( generatedId, instance, getState(), generatedValues, session )
: voidFuture();
Expand Down Expand Up @@ -153,4 +162,9 @@ public boolean areTransientReferencesNullified() {
public void setTransientReferencesNullified() {
transientReferencesNullified = true;
}

@Override
public Object getRowId() {
return rowId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.generator.values.GeneratedValues;
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister;
import org.hibernate.stat.spi.StatisticsImplementor;
Expand Down Expand Up @@ -111,8 +112,19 @@ private CompletionStage<Void> processInsertGeneratedProperties(
// setVersion( Versioning.getVersion( getState(), persister ) );
}
return persister.reactiveProcessInsertGenerated( id, instance, getState(), generatedValues, session )
.thenAccept( v -> entry.postUpdate( instance, getState(), getVersion() ) );

.thenAccept( v -> {
// Process row-id values when available early by replacing the entity entry
if ( generatedValues != null ) {
final EntityRowIdMapping rowIdMapping = persister.getRowIdMapping();
if ( rowIdMapping != null ) {
final Object rowId = generatedValues.getGeneratedValue( rowIdMapping );
if ( rowId != null ) {
session.getPersistenceContext().replaceEntityEntryRowId( getInstance(), rowId );
}
}
}
entry.postUpdate( instance, getState(), getVersion() );
} );
}
else {
return voidFuture();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.engine.spi.Status;
import org.hibernate.event.spi.EventSource;
import org.hibernate.generator.values.GeneratedValues;
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.reactive.engine.ReactiveExecutable;
import org.hibernate.reactive.persister.entity.impl.ReactiveEntityPersister;
Expand Down Expand Up @@ -143,7 +144,19 @@ private CompletionStage<Void> processGeneratedProperties(
throw new UnsupportedOperationException( "generated version attribute not supported in Hibernate Reactive" );
// setNextVersion( Versioning.getVersion( getState(), persister ) );
}
return persister.reactiveProcessUpdateGenerated( id, instance, getState(), generatedValues, session );
return persister.reactiveProcessUpdateGenerated( id, instance, getState(), generatedValues, session )
.thenAccept( v -> {
// Process row-id values when available early by replacing the entity entry
if ( generatedValues != null ) {
final EntityRowIdMapping rowIdMapping = persister.getRowIdMapping();
if ( rowIdMapping != null ) {
final Object rowId = generatedValues.getGeneratedValue( rowIdMapping );
if ( rowId != null ) {
session.getPersistenceContext().replaceEntityEntryRowId( getInstance(), rowId );
}
}
}
} );

}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

import java.util.concurrent.CompletionStage;

import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.engine.jdbc.mutation.OperationResultChecker;
import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
Expand Down Expand Up @@ -48,7 +46,7 @@ public CompletionStage<GeneratedValues> performReactiveNonBatchedOperations(
boolean isIdentityInsert,
String[] identifierColumnsNames) {
PreparedStatementDetails singleStatementDetails = getStatementGroup().getSingleStatementDetails();
if ( generatedValuesDelegate != null && !isRegularInsertWithMariaDb( session, isIdentityInsert ) ) {
if ( generatedValuesDelegate != null ) {
return generatedValuesDelegate.reactivePerformMutation(
singleStatementDetails,
getJdbcValueBindings(),
Expand All @@ -67,14 +65,6 @@ public CompletionStage<GeneratedValues> performReactiveNonBatchedOperations(
).thenCompose( CompletionStages::nullFuture );
}

private boolean isRegularInsertWithMariaDb(SharedSessionContractImplementor session, boolean isIdentityInsert) {
if ( isIdentityInsert ) {
return false;
}
Dialect dialect = session.getJdbcServices().getDialect();
return dialect instanceof MariaDBDialect;
}

@Override
public void release() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;

/**
* @deprecated No longer used
*/
@Deprecated(since = "7.1", forRemoval = true)
public class GeneratedValuesMutationDelegateAdaptor implements ReactiveGeneratedValuesMutationDelegate {
private final ReactiveGeneratedValuesMutationDelegate delegate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

import org.hibernate.HibernateException;
import org.hibernate.Internal;
import org.hibernate.dialect.CockroachDialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.EventType;
import org.hibernate.generator.values.GeneratedValueBasicResultBuilder;
Expand All @@ -17,15 +21,16 @@
import org.hibernate.generator.values.internal.GeneratedValuesImpl;
import org.hibernate.generator.values.internal.GeneratedValuesMappingProducer;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
import org.hibernate.id.insert.UniqueKeySelectingDelegate;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.reactive.id.insert.ReactiveGetGeneratedKeysDelegate;
import org.hibernate.reactive.id.insert.ReactiveInsertReturningDelegate;
import org.hibernate.reactive.id.insert.ReactiveUniqueKeySelectingDelegate;
import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState;
import org.hibernate.reactive.sql.exec.spi.ReactiveValuesResultSet;
import org.hibernate.reactive.sql.results.internal.ReactiveDirectResultSetAccess;
Expand All @@ -38,7 +43,6 @@
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.type.descriptor.WrapperOptions;

import java.sql.PreparedStatement;
Expand All @@ -51,6 +55,7 @@
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.noCustomSql;
import static org.hibernate.internal.NaturalIdHelper.getNaturalIdPropertyNames;
import static org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer.UniqueSemantic.NONE;
import static org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions.NO_OPTIONS;

/**
* @see org.hibernate.generator.values.internal.GeneratedValuesHelper
Expand All @@ -64,13 +69,21 @@ public class ReactiveGeneratedValuesHelper {
* @see GeneratedValuesHelper#getGeneratedValuesDelegate(EntityPersister, EventType)
*/
public static GeneratedValuesMutationDelegate getGeneratedValuesDelegate(EntityPersister persister, EventType timing) {
final boolean hasGeneratedProperties = !persister.getGeneratedProperties( timing ).isEmpty();
final List<? extends ModelPart> generatedProperties = persister.getGeneratedProperties( timing );
final boolean hasGeneratedProperties = !generatedProperties.isEmpty();
final boolean hasRowId = timing == EventType.INSERT && persister.getRowIdMapping() != null;
final Dialect dialect = persister.getFactory().getJdbcServices().getDialect();

final boolean hasFormula =
generatedProperties.stream()
.anyMatch( part -> part instanceof SelectableMapping selectable
&& selectable.isFormula() );

// Cockroach supports insert returning it but the CockroachDb#supportsInsertReturningRowId() wrongly returns false ( https://hibernate.atlassian.net/browse/HHH-19717 )
boolean supportsInsertReturningRowId = dialect.supportsInsertReturningRowId() || dialect instanceof CockroachDialect;
if ( hasRowId
&& dialect.supportsInsertReturning()
&& dialect.supportsInsertReturningRowId()
&& supportsInsertReturning( dialect )
&& supportsInsertReturningRowId
&& noCustomSql( persister, timing ) ) {
// Special case for RowId on INSERT, since GetGeneratedKeysDelegate doesn't support it
// make InsertReturningDelegate the preferred method if the dialect supports it
Expand All @@ -81,26 +94,40 @@ && noCustomSql( persister, timing ) ) {
return null;
}

if ( dialect.supportsInsertReturningGeneratedKeys()
&& persister.getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
return new GetGeneratedKeysDelegate( persister, false, timing );
}
else if ( supportsReturning( dialect, timing ) && noCustomSql( persister, timing ) ) {
if ( supportsReturning( dialect, timing ) && noCustomSql( persister, timing ) ) {
return new ReactiveInsertReturningDelegate( persister, timing );
}
else if ( timing == EventType.INSERT && persister.getNaturalIdentifierProperties() != null
&& !persister.getEntityMetamodel().isNaturalIdentifierInsertGenerated() ) {
return new UniqueKeySelectingDelegate(
persister,
getNaturalIdPropertyNames( persister ),
timing
);
else if ( !hasFormula && dialect.supportsInsertReturningGeneratedKeys() ) {
return new ReactiveGetGeneratedKeysDelegate( persister, false, timing );
}
else if ( timing == EventType.INSERT && persister.getNaturalIdentifierProperties() != null && !persister.getEntityMetamodel()
.isNaturalIdentifierInsertGenerated() ) {
return new ReactiveUniqueKeySelectingDelegate( persister, getNaturalIdPropertyNames( persister ), timing );
}
return null;
}

private static boolean supportsReturning(Dialect dialect, EventType timing) {
return timing == EventType.INSERT ? dialect.supportsInsertReturning() : dialect.supportsUpdateReturning();
public static boolean supportReactiveGetGeneratedKey(Dialect dialect, List<? extends ModelPart> generatedProperties) {
return dialect instanceof OracleDialect
|| (dialect instanceof MySQLDialect && generatedProperties.size() == 1 && !(dialect instanceof MariaDBDialect));
}

public static boolean supportsReturning(Dialect dialect, EventType timing) {
if ( dialect instanceof CockroachDialect ) {
// Cockroach supports insert and update returning but the CockroachDb#supportsInsertReturning() wrongly returns false ( https://hibernate.atlassian.net/browse/HHH-19717 )
return true;
}
return timing == EventType.INSERT
? dialect.supportsInsertReturning()
: dialect.supportsUpdateReturning();
}

public static boolean supportsInsertReturning(Dialect dialect) {
if ( dialect instanceof CockroachDialect ) {
// Cockroach supports insert returning but the CockroachDb#supportsInsertReturning() wrongly returns false ( https://hibernate.atlassian.net/browse/HHH-19717 )
return true;
}
return dialect.supportsInsertReturning();
}

/**
Expand Down Expand Up @@ -181,31 +208,9 @@ private static CompletionStage<Object[]> readGeneratedValues(
executionContext
);

final JdbcValuesSourceProcessingOptions processingOptions = new JdbcValuesSourceProcessingOptions() {
@Override
public Object getEffectiveOptionalObject() {
return null;
}

@Override
public String getEffectiveOptionalEntityName() {
return null;
}

@Override
public Object getEffectiveOptionalId() {
return null;
}

@Override
public boolean shouldReturnProxies() {
return true;
}
};

final JdbcValuesSourceProcessingStateStandardImpl valuesProcessingState = new JdbcValuesSourceProcessingStateStandardImpl(
executionContext,
processingOptions
NO_OPTIONS
);

final ReactiveRowReader<Object[]> rowReader = ReactiveResultsHelper.createRowReader(
Expand All @@ -217,7 +222,7 @@ public boolean shouldReturnProxies() {

final ReactiveRowProcessingState rowProcessingState = new ReactiveRowProcessingState( valuesProcessingState, executionContext, rowReader, jdbcValues );
return ReactiveListResultsConsumer.<Object[]>instance( NONE )
.consume( jdbcValues, session, processingOptions, valuesProcessingState, rowProcessingState, rowReader )
.consume( jdbcValues, session, NO_OPTIONS, valuesProcessingState, rowProcessingState, rowReader )
.thenApply( results -> {
if ( results.isEmpty() ) {
throw new HibernateException( "The database returned no natively generated values : " + persister.getNavigableRole().getFullPath() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
package org.hibernate.reactive.id;

import org.hibernate.Incubating;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.EventType;
import org.hibernate.generator.Generator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.reactive.logging.impl.Log;
import org.hibernate.reactive.logging.impl.LoggerFactory;
import org.hibernate.reactive.session.ReactiveConnectionSupplier;

import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletionStage;

/**
Expand All @@ -26,7 +29,8 @@
* @see IdentifierGenerator
*/
@Incubating
public interface ReactiveIdentifierGenerator<Id> extends Generator {
public interface ReactiveIdentifierGenerator<Id> extends IdentifierGenerator {
Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );

/**
* Returns a generated identifier, via a {@link CompletionStage}.
Expand All @@ -38,4 +42,18 @@
default CompletionStage<Id> generate(ReactiveConnectionSupplier session, Object owner, Object currentValue, EventType eventType) {
return generate( session, owner );
}

@Override
default Id generate(

Check notice

Code scanning / CodeQL

Confusing overloading of methods Note

Method ReactiveIdentifierGenerator.generate(..) could be confused with overloaded method
generate
, since dispatch depends on static types.
SharedSessionContractImplementor session,
Object owner,
Object currentValue,
EventType eventType){
throw LOG.nonReactiveMethodCall( "generate(ReactiveConnectionSupplier, Object, Object, EventType)" );
}

@Override
default Object generate(SharedSessionContractImplementor session, Object object){

Check notice

Code scanning / CodeQL

Confusing overloading of methods Note

Method ReactiveIdentifierGenerator.generate(..) could be confused with overloaded method
generate
, since dispatch depends on static types.
throw LOG.nonReactiveMethodCall( "generate(ReactiveConnectionSupplier, Object)" );
}
}
Loading
Loading