Skip to content

Commit 7fdf4fe

Browse files
committed
[#1906] Support @IdGeneratorType
1 parent 01337dd commit 7fdf4fe

22 files changed

+1181
-232
lines changed

hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.time.LocalDate;
2929
import java.time.LocalDateTime;
3030
import java.time.LocalTime;
31+
import java.util.ArrayList;
3132
import java.util.Calendar;
3233
import java.util.Collection;
3334
import java.util.Iterator;
@@ -138,6 +139,10 @@ public ResultSetAdaptor(RowSet<Row> rows, PropertyKind<Row> propertyKind, String
138139
this( rows, rows.property( propertyKind ), idColumnName, idClass );
139140
}
140141

142+
public ResultSetAdaptor(RowSet<Row> rows, PropertyKind<Row> propertyKind, List<String> generatedColumnNames, List<Class<?>> generatedColumnClasses) {
143+
this( rows, rows.property( propertyKind ), generatedColumnNames, generatedColumnClasses );
144+
}
145+
141146
public ResultSetAdaptor(RowSet<Row> rows, Collection<?> ids, String idColumnName, Class<?> idClass) {
142147
this( rows, new RowFromId( ids, idColumnName ), idColumnName, idClass );
143148
}
@@ -150,6 +155,17 @@ private ResultSetAdaptor(RowSet<Row> rows, Row row, String idColumnName, Class<?
150155
this.columnDescriptors = List.of( toColumnDescriptor( idClass, idColumnName ) );
151156
}
152157

158+
private ResultSetAdaptor(RowSet<Row> rows, Row row, List<String> columnNames, List<Class<?>> columnClasses) {
159+
requireNonNull( rows );
160+
requireNonNull( columnNames );
161+
this.iterator = List.of( row ).iterator();
162+
this.columnNames = columnNames ;
163+
this.columnDescriptors = new ArrayList<>(columnNames.size());
164+
for (int i =0; i < columnNames.size(); i++) {
165+
columnDescriptors.add( toColumnDescriptor( columnClasses.get( i ), columnNames.get(i) ) );
166+
}
167+
}
168+
153169
private static ColumnDescriptor toColumnDescriptor(Class<?> idClass, String idColumnName) {
154170
return new ColumnDescriptor() {
155171
@Override

hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityIdentityInsertAction.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class ReactiveEntityIdentityInsertAction extends EntityIdentityInsertActi
3131
private final boolean isVersionIncrementDisabled;
3232
private boolean executed;
3333
private boolean transientReferencesNullified;
34+
private Object rowId;
3435

3536
public ReactiveEntityIdentityInsertAction(
3637
Object[] state,
@@ -108,6 +109,12 @@ private CompletionStage<Void> processInsertGeneratedProperties(
108109
Object instance,
109110
GeneratedValues generatedValues,
110111
SharedSessionContractImplementor session) {
112+
if ( persister.getRowIdMapping() != null ) {
113+
rowId = generatedValues.getGeneratedValue( persister.getRowIdMapping() );
114+
if ( rowId != null && !isEarlyInsert() ) {
115+
session.getPersistenceContext().replaceEntityEntryRowId( getInstance(), rowId );
116+
}
117+
}
111118
return persister.hasInsertGeneratedProperties()
112119
? persister.reactiveProcessInsertGenerated( generatedId, instance, getState(), generatedValues, session )
113120
: voidFuture();
@@ -153,4 +160,9 @@ public boolean areTransientReferencesNullified() {
153160
public void setTransientReferencesNullified() {
154161
transientReferencesNullified = true;
155162
}
163+
164+
@Override
165+
public Object getRowId() {
166+
return rowId;
167+
}
156168
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityRegularInsertAction.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,16 @@ private CompletionStage<Void> processInsertGeneratedProperties(
111111
// setVersion( Versioning.getVersion( getState(), persister ) );
112112
}
113113
return persister.reactiveProcessInsertGenerated( id, instance, getState(), generatedValues, session )
114-
.thenAccept( v -> entry.postUpdate( instance, getState(), getVersion() ) );
115-
114+
.thenAccept( v -> {
115+
// Process row-id values when available early by replacing the entity entry
116+
if ( generatedValues != null && persister.getRowIdMapping() != null ) {
117+
final Object rowId = generatedValues.getGeneratedValue( persister.getRowIdMapping() );
118+
if ( rowId != null ) {
119+
session.getPersistenceContext().replaceEntityEntryRowId( getInstance(), rowId );
120+
}
121+
}
122+
entry.postUpdate( instance, getState(), getVersion() );
123+
} );
116124
}
117125
else {
118126
return voidFuture();

hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/impl/ReactiveEntityUpdateAction.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,16 @@ private CompletionStage<Void> processGeneratedProperties(
143143
throw new UnsupportedOperationException( "generated version attribute not supported in Hibernate Reactive" );
144144
// setNextVersion( Versioning.getVersion( getState(), persister ) );
145145
}
146-
return persister.reactiveProcessUpdateGenerated( id, instance, getState(), generatedValues, session );
146+
return persister.reactiveProcessUpdateGenerated( id, instance, getState(), generatedValues, session )
147+
.thenAccept( v -> {
148+
// Process row-id values when available early by replacing the entity entry
149+
if ( generatedValues != null && persister.getRowIdMapping() != null ) {
150+
final Object rowId = generatedValues.getGeneratedValue( persister.getRowIdMapping() );
151+
if ( rowId != null ) {
152+
session.getPersistenceContext().replaceEntityEntryRowId( getInstance(), rowId );
153+
}
154+
}
155+
} );
147156

148157
}
149158
else {

hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/jdbc/mutation/internal/ReactiveMutationExecutorSingleNonBatched.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
import java.util.concurrent.CompletionStage;
99

10-
import org.hibernate.dialect.Dialect;
11-
import org.hibernate.dialect.MariaDBDialect;
1210
import org.hibernate.engine.jdbc.mutation.OperationResultChecker;
1311
import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
1412
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
@@ -48,7 +46,7 @@ public CompletionStage<GeneratedValues> performReactiveNonBatchedOperations(
4846
boolean isIdentityInsert,
4947
String[] identifierColumnsNames) {
5048
PreparedStatementDetails singleStatementDetails = getStatementGroup().getSingleStatementDetails();
51-
if ( generatedValuesDelegate != null && !isRegularInsertWithMariaDb( session, isIdentityInsert ) ) {
49+
if ( generatedValuesDelegate != null ) {
5250
return generatedValuesDelegate.reactivePerformMutation(
5351
singleStatementDetails,
5452
getJdbcValueBindings(),
@@ -67,14 +65,6 @@ public CompletionStage<GeneratedValues> performReactiveNonBatchedOperations(
6765
).thenCompose( CompletionStages::nullFuture );
6866
}
6967

70-
private boolean isRegularInsertWithMariaDb(SharedSessionContractImplementor session, boolean isIdentityInsert) {
71-
if ( isIdentityInsert ) {
72-
return false;
73-
}
74-
Dialect dialect = session.getJdbcServices().getDialect();
75-
return dialect instanceof MariaDBDialect;
76-
}
77-
7868
@Override
7969
public void release() {
8070
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/generator/values/internal/ReactiveGeneratedValuesHelper.java

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77

88
import org.hibernate.HibernateException;
99
import org.hibernate.Internal;
10+
import org.hibernate.dialect.CockroachDialect;
1011
import org.hibernate.dialect.Dialect;
12+
import org.hibernate.dialect.MariaDBDialect;
13+
import org.hibernate.dialect.MySQLDialect;
14+
import org.hibernate.dialect.OracleDialect;
1115
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1216
import org.hibernate.generator.EventType;
1317
import org.hibernate.generator.values.GeneratedValueBasicResultBuilder;
@@ -17,15 +21,16 @@
1721
import org.hibernate.generator.values.internal.GeneratedValuesImpl;
1822
import org.hibernate.generator.values.internal.GeneratedValuesMappingProducer;
1923
import org.hibernate.id.IdentifierGeneratorHelper;
20-
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
21-
import org.hibernate.id.insert.UniqueKeySelectingDelegate;
2224
import org.hibernate.internal.CoreLogging;
2325
import org.hibernate.internal.CoreMessageLogger;
2426
import org.hibernate.metamodel.mapping.ModelPart;
27+
import org.hibernate.metamodel.mapping.SelectableMapping;
2528
import org.hibernate.persister.entity.EntityPersister;
2629
import org.hibernate.pretty.MessageHelper;
2730
import org.hibernate.query.spi.QueryOptions;
31+
import org.hibernate.reactive.id.insert.ReactiveGetGeneratedKeysDelegate;
2832
import org.hibernate.reactive.id.insert.ReactiveInsertReturningDelegate;
33+
import org.hibernate.reactive.id.insert.ReactiveUniqueKeySelectingDelegate;
2934
import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState;
3035
import org.hibernate.reactive.sql.exec.spi.ReactiveValuesResultSet;
3136
import org.hibernate.reactive.sql.results.internal.ReactiveDirectResultSetAccess;
@@ -38,7 +43,6 @@
3843
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
3944
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
4045
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
41-
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
4246
import org.hibernate.type.descriptor.WrapperOptions;
4347

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

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

77+
final boolean hasFormula =
78+
generatedProperties.stream()
79+
.anyMatch( part -> part instanceof SelectableMapping selectable
80+
&& selectable.isFormula() );
81+
82+
// Cockroach supports insert returning it but the CockroachDb#supportsInsertReturningRowId() wrongly returns false ( https://hibernate.atlassian.net/browse/HHH-19717 )
83+
boolean supportsInsertReturningRowId = dialect.supportsInsertReturningRowId() || dialect instanceof CockroachDialect;
7184
if ( hasRowId
72-
&& dialect.supportsInsertReturning()
73-
&& dialect.supportsInsertReturningRowId()
85+
&& supportsInsertReturning( dialect )
86+
&& supportsInsertReturningRowId
7487
&& noCustomSql( persister, timing ) ) {
7588
// Special case for RowId on INSERT, since GetGeneratedKeysDelegate doesn't support it
7689
// make InsertReturningDelegate the preferred method if the dialect supports it
@@ -81,26 +94,40 @@ && noCustomSql( persister, timing ) ) {
8194
return null;
8295
}
8396

84-
if ( dialect.supportsInsertReturningGeneratedKeys()
85-
&& persister.getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
86-
return new GetGeneratedKeysDelegate( persister, false, timing );
87-
}
88-
else if ( supportsReturning( dialect, timing ) && noCustomSql( persister, timing ) ) {
97+
if ( supportsReturning( dialect, timing ) && noCustomSql( persister, timing ) ) {
8998
return new ReactiveInsertReturningDelegate( persister, timing );
9099
}
91-
else if ( timing == EventType.INSERT && persister.getNaturalIdentifierProperties() != null
92-
&& !persister.getEntityMetamodel().isNaturalIdentifierInsertGenerated() ) {
93-
return new UniqueKeySelectingDelegate(
94-
persister,
95-
getNaturalIdPropertyNames( persister ),
96-
timing
97-
);
100+
else if ( !hasFormula && dialect.supportsInsertReturningGeneratedKeys() ) {
101+
return new ReactiveGetGeneratedKeysDelegate( persister, false, timing );
102+
}
103+
else if ( timing == EventType.INSERT && persister.getNaturalIdentifierProperties() != null && !persister.getEntityMetamodel()
104+
.isNaturalIdentifierInsertGenerated() ) {
105+
return new ReactiveUniqueKeySelectingDelegate( persister, getNaturalIdPropertyNames( persister ), timing );
98106
}
99107
return null;
100108
}
101109

102-
private static boolean supportsReturning(Dialect dialect, EventType timing) {
103-
return timing == EventType.INSERT ? dialect.supportsInsertReturning() : dialect.supportsUpdateReturning();
110+
public static boolean supportReactiveGetGeneratedKey(Dialect dialect, List<? extends ModelPart> generatedProperties) {
111+
return dialect instanceof OracleDialect
112+
|| (dialect instanceof MySQLDialect && generatedProperties.size() == 1 && !(dialect instanceof MariaDBDialect));
113+
}
114+
115+
public static boolean supportsReturning(Dialect dialect, EventType timing) {
116+
if ( dialect instanceof CockroachDialect ) {
117+
// Cockroach supports insert and update returning but the CockroachDb#supportsInsertReturning() wrongly returns false ( https://hibernate.atlassian.net/browse/HHH-19717 )
118+
return true;
119+
}
120+
return timing == EventType.INSERT
121+
? dialect.supportsInsertReturning()
122+
: dialect.supportsUpdateReturning();
123+
}
124+
125+
public static boolean supportsInsertReturning(Dialect dialect) {
126+
if ( dialect instanceof CockroachDialect ) {
127+
// Cockroach supports insert returning but the CockroachDb#supportsInsertReturning() wrongly returns false ( https://hibernate.atlassian.net/browse/HHH-19717 )
128+
return true;
129+
}
130+
return dialect.supportsInsertReturning();
104131
}
105132

106133
/**
@@ -181,31 +208,9 @@ private static CompletionStage<Object[]> readGeneratedValues(
181208
executionContext
182209
);
183210

184-
final JdbcValuesSourceProcessingOptions processingOptions = new JdbcValuesSourceProcessingOptions() {
185-
@Override
186-
public Object getEffectiveOptionalObject() {
187-
return null;
188-
}
189-
190-
@Override
191-
public String getEffectiveOptionalEntityName() {
192-
return null;
193-
}
194-
195-
@Override
196-
public Object getEffectiveOptionalId() {
197-
return null;
198-
}
199-
200-
@Override
201-
public boolean shouldReturnProxies() {
202-
return true;
203-
}
204-
};
205-
206211
final JdbcValuesSourceProcessingStateStandardImpl valuesProcessingState = new JdbcValuesSourceProcessingStateStandardImpl(
207212
executionContext,
208-
processingOptions
213+
NO_OPTIONS
209214
);
210215

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

218223
final ReactiveRowProcessingState rowProcessingState = new ReactiveRowProcessingState( valuesProcessingState, executionContext, rowReader, jdbcValues );
219224
return ReactiveListResultsConsumer.<Object[]>instance( NONE )
220-
.consume( jdbcValues, session, processingOptions, valuesProcessingState, rowProcessingState, rowReader )
225+
.consume( jdbcValues, session, NO_OPTIONS, valuesProcessingState, rowProcessingState, rowReader )
221226
.thenApply( results -> {
222227
if ( results.isEmpty() ) {
223228
throw new HibernateException( "The database returned no natively generated values : " + persister.getNavigableRole().getFullPath() );

hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveIdentifierGenerator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
*/
3131
@Incubating
3232
public interface ReactiveIdentifierGenerator<Id> extends IdentifierGenerator {
33+
Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );
3334

3435
/**
3536
* Returns a generated identifier, via a {@link CompletionStage}.
@@ -48,11 +49,11 @@ default Id generate(
4849
Object owner,
4950
Object currentValue,
5051
EventType eventType){
51-
throw LoggerFactory.make( Log.class, MethodHandles.lookup() ).nonReactiveMethodCall( "generate(ReactiveConnectionSupplier, Object, Object, EventType)" );
52+
throw LOG.nonReactiveMethodCall( "generate(ReactiveConnectionSupplier, Object, Object, EventType)" );
5253
}
5354

5455
@Override
5556
default Object generate(SharedSessionContractImplementor session, Object object){
56-
throw LoggerFactory.make( Log.class, MethodHandles.lookup() ).nonReactiveMethodCall( "generate(ReactiveConnectionSupplier, Object)" );
57+
throw LOG.nonReactiveMethodCall( "generate(ReactiveConnectionSupplier, Object)" );
5758
}
5859
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/ReactiveOnExecutionGenerator.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,38 @@
66
package org.hibernate.reactive.id;
77

88
import org.hibernate.dialect.Dialect;
9+
import org.hibernate.engine.spi.SessionFactoryImplementor;
910
import org.hibernate.generator.OnExecutionGenerator;
1011
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
1112
import org.hibernate.persister.entity.EntityPersister;
13+
import org.hibernate.reactive.id.insert.ReactiveGetGeneratedKeysDelegate;
1214
import org.hibernate.reactive.id.insert.ReactiveInsertReturningDelegate;
15+
import org.hibernate.reactive.id.insert.ReactiveUniqueKeySelectingDelegate;
16+
17+
import static org.hibernate.generator.EventType.INSERT;
18+
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.noCustomSql;
19+
import static org.hibernate.reactive.generator.values.internal.ReactiveGeneratedValuesHelper.supportReactiveGetGeneratedKey;
20+
import static org.hibernate.reactive.generator.values.internal.ReactiveGeneratedValuesHelper.supportsReturning;
1321

1422
public interface ReactiveOnExecutionGenerator extends OnExecutionGenerator {
1523

1624
@Override
1725
default InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(EntityPersister persister) {
18-
Dialect dialect = persister.getFactory().getJdbcServices().getDialect();
26+
final SessionFactoryImplementor factory = persister.getFactory();
27+
final Dialect dialect = factory.getJdbcServices().getDialect();
1928
// Hibernate ORM allows the selection of different strategies based on the property `hibernate.jdbc.use_get_generated_keys`.
2029
// But that's a specific JDBC property and with Vert.x we only have one viable option for each supported database.
21-
return new ReactiveInsertReturningDelegate( persister, dialect );
30+
final boolean supportsInsertReturning = supportsReturning( dialect, INSERT );
31+
if ( supportsInsertReturning && noCustomSql( persister, INSERT ) ) {
32+
return new ReactiveInsertReturningDelegate( persister, INSERT );
33+
}
34+
else if ( supportReactiveGetGeneratedKey( dialect, persister.getGeneratedProperties( INSERT ) ) ) {
35+
return new ReactiveGetGeneratedKeysDelegate( persister, false, INSERT );
36+
}
37+
else {
38+
// let's just hope the entity has a @NaturalId!
39+
return new ReactiveUniqueKeySelectingDelegate( persister, getUniqueKeyPropertyNames( persister ), INSERT );
40+
}
2241
}
2342

2443
}

0 commit comments

Comments
 (0)