Skip to content

Commit d0c3521

Browse files
committed
HHH-19602 - Adjust JdbcOperation to allow more-than-one statement
1 parent 1584943 commit d0c3521

File tree

5 files changed

+107
-25
lines changed

5 files changed

+107
-25
lines changed

hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractDatabaseOperation.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.hibernate.internal.util.collections.CollectionHelper;
88
import org.hibernate.sql.exec.spi.ExecutionContext;
99
import org.hibernate.sql.exec.spi.DatabaseOperation;
10+
import org.hibernate.sql.exec.spi.JdbcOperation;
1011
import org.hibernate.sql.exec.spi.PostAction;
1112
import org.hibernate.sql.exec.spi.PreAction;
1213
import org.hibernate.sql.exec.spi.SecondaryAction;
@@ -18,13 +19,18 @@
1819
import java.util.List;
1920

2021
/**
22+
* Abstract support for DatabaseOperation implementations, mainly
23+
* managing {@linkplain PreAction pre-} and {@linkplain PostAction post-}
24+
* actions.
25+
*
2126
* @author Steve Ebersole
2227
*/
23-
@SuppressWarnings("ALL")
24-
public abstract class AbstractDatabaseOperation implements DatabaseOperation {
28+
public abstract class AbstractDatabaseOperation<P extends JdbcOperation>
29+
implements DatabaseOperation<P> {
2530
protected final PreAction[] preActions;
2631
protected final PostAction[] postActions;
2732

33+
@SuppressWarnings("unused")
2834
public AbstractDatabaseOperation() {
2935
this( null, null );
3036
}
@@ -77,7 +83,8 @@ protected abstract static class Builder<T extends Builder<T>> {
7783
protected abstract T getThis();
7884

7985
/**
80-
* Appends the {@code actions} to the growing list of pre-actions
86+
* Appends the {@code actions} to the growing list of pre-actions,
87+
* executed (in order) after all currently registered actions.
8188
*
8289
* @return {@code this}, for method chaining.
8390
*/
@@ -98,9 +105,8 @@ public T prependPreAction(PreAction... actions) {
98105
if ( preActions == null ) {
99106
preActions = new ArrayList<>();
100107
}
101-
for ( int i = actions.length - 1; i >= 0; i-- ) {
102-
preActions.add( 0, actions[i] );
103-
}
108+
// todo (DatabaseOperation) : should we invert the order of the incoming actions?
109+
Collections.addAll( preActions, actions );
104110
return getThis();
105111
}
106112

@@ -126,15 +132,17 @@ public T prependPostAction(PostAction... actions) {
126132
if ( postActions == null ) {
127133
postActions = new ArrayList<>();
128134
}
129-
for ( int i = actions.length - 1; i >= 0; i-- ) {
130-
postActions.add( 0, actions[i] );
131-
}
135+
// todo (DatabaseOperation) : should we invert the order of the incoming actions?
136+
Collections.addAll( postActions, actions );
132137
return getThis();
133138
}
134139

135140
/**
136-
* Adds a secondary action. Assumes the action implements both
137-
* {@linkplain PreAction} and {@linkplain PostAction}.
141+
* Adds a secondary action pair.
142+
* Assumes the {@code action} implements both {@linkplain PreAction} and {@linkplain PostAction}.
143+
*
144+
* @apiNote Prefer {@linkplain #addSecondaryActionPair(PreAction, PostAction)} to avoid
145+
* the casts needed here.
138146
*
139147
* @see #prependPreAction
140148
* @see #appendPostAction

hibernate-core/src/main/java/org/hibernate/sql/exec/internal/DatabaseOperationSelectImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* @author Steve Ebersole
2828
*/
2929
public class DatabaseOperationSelectImpl
30-
extends AbstractDatabaseOperation
30+
extends AbstractDatabaseOperation<JdbcOperationQuerySelect>
3131
implements DatabaseOperationSelect {
3232
private final JdbcOperationQuerySelect primaryOperation;
3333

hibernate-core/src/main/java/org/hibernate/sql/exec/spi/DatabaseOperation.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99
import java.util.Set;
1010

1111
/**
12-
* An operation against the database, comprised of a single
13-
* {@linkplain #getPrimaryOperation primary operation} and zero-or-more
14-
* before/after {@linkplain SecondaryAction secondary actions}.
12+
* An operation against the database, comprised of a
13+
* {@linkplain #getPrimaryOperation primary operation} and
14+
* zero-or-more {@linkplain SecondaryAction secondary actions}.
1515
*
1616
* @author Steve Ebersole
1717
*/
1818
@Incubating
19-
public interface DatabaseOperation {
19+
public interface DatabaseOperation<P extends JdbcOperation> {
2020
/**
2121
* The primary operation for the group.
2222
*/
23-
JdbcOperation getPrimaryOperation();
23+
P getPrimaryOperation();
2424

2525
/**
2626
* The names of tables referenced or affected by this operation.

hibernate-core/src/main/java/org/hibernate/sql/exec/spi/DatabaseOperationSelect.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,12 @@
1212
import java.sql.PreparedStatement;
1313

1414
/**
15-
* {@linkplain DatabaseOperation} whose primary operation is a selection.
15+
* {@linkplain DatabaseOperation} whose primary operation is a {@linkplain JdbcOperationQuerySelect selection}.
1616
*
1717
* @author Steve Ebersole
1818
*/
1919
@Incubating
20-
public interface DatabaseOperationSelect extends DatabaseOperation {
21-
@Override
22-
JdbcOperationQuerySelect getPrimaryOperation();
23-
20+
public interface DatabaseOperationSelect extends DatabaseOperation<JdbcOperationQuerySelect> {
2421
/**
2522
* Execute the underlying statements and return the result(s).
2623
*

hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/spi/DatabaseOperationSmokeTest.java

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
2424
import org.hibernate.sql.ast.tree.select.SelectStatement;
2525
import org.hibernate.sql.exec.internal.BaseExecutionContext;
26+
import org.hibernate.sql.exec.internal.CallbackImpl;
2627
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
2728
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
2829
import org.hibernate.sql.exec.internal.StandardStatementCreator;
@@ -35,14 +36,20 @@
3536
import org.hibernate.sql.exec.spi.PreAction;
3637
import org.hibernate.sql.exec.spi.StatementAccess;
3738
import org.hibernate.sql.results.spi.SingleResultConsumer;
39+
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
3840
import org.hibernate.testing.orm.junit.DomainModel;
41+
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
3942
import org.hibernate.testing.orm.junit.SessionFactory;
4043
import org.hibernate.testing.orm.junit.SessionFactoryScope;
4144
import org.junit.jupiter.api.AfterEach;
4245
import org.junit.jupiter.api.BeforeEach;
4346
import org.junit.jupiter.api.Test;
4447

4548
import java.sql.Connection;
49+
import java.util.ArrayList;
50+
import java.util.List;
51+
52+
import static org.assertj.core.api.Assertions.assertThat;
4653

4754

4855
/**
@@ -96,14 +103,13 @@ void testSimpleSelect(SessionFactoryScope factoryScope) {
96103
}
97104

98105
@Test
106+
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsConnectionLockTimeouts.class)
99107
void testConnectionLockTimeout(SessionFactoryScope factoryScope) {
100108
final SessionFactoryImplementor sessionFactory = factoryScope.getSessionFactory();
101109

102110
final LockingSupport lockingSupport = sessionFactory.getJdbcServices().getDialect().getLockingSupport();
103111
final ConnectionLockTimeoutStrategy lockTimeoutStrategy = lockingSupport.getConnectionLockTimeoutStrategy();
104-
if ( lockTimeoutStrategy.getSupportedLevel() == ConnectionLockTimeoutStrategy.Level.NONE ) {
105-
return;
106-
}
112+
assert lockTimeoutStrategy.getSupportedLevel() != ConnectionLockTimeoutStrategy.Level.NONE;
107113

108114
final EntityPersister entityDescriptor = sessionFactory.getMappingMetamodel().findEntityDescriptor( Person.class );
109115

@@ -138,6 +144,66 @@ void testConnectionLockTimeout(SessionFactoryScope factoryScope) {
138144
} );
139145
}
140146

147+
@Test
148+
void testFollowOnLockingParadigm(SessionFactoryScope factoryScope) {
149+
// NOTE: this just tests the principle -
150+
// for now, just collect the values loaded.
151+
// ultimately, this can be used to apply smarter follow-on locking.
152+
153+
final SessionFactoryImplementor sessionFactory = factoryScope.getSessionFactory();
154+
final EntityPersister entityDescriptor = sessionFactory.getMappingMetamodel().findEntityDescriptor( Person.class );
155+
156+
final PersonQuery personQuery = createPersonQuery( entityDescriptor, sessionFactory );
157+
final JdbcOperationQuerySelect jdbcOperation = personQuery.jdbcOperation();
158+
final JdbcParameterBindings jdbcParameterBindings = personQuery.jdbcParameterBindings();
159+
160+
factoryScope.inTransaction( (session) -> {
161+
final LoadedValueCollector loadedValueCollector = new LoadedValueCollector();
162+
163+
final Callback callback = new CallbackImpl();
164+
callback.registerAfterLoadAction( (entity, entityMappingType, session1) -> {
165+
loadedValueCollector.loadedValues.add( entity );
166+
} );
167+
168+
final SingleIdExecutionContext executionContext = new SingleIdExecutionContext(
169+
session,
170+
null,
171+
1,
172+
entityDescriptor,
173+
QueryOptions.NONE,
174+
callback
175+
);
176+
177+
178+
final DatabaseOperationSelectImpl.Builder operationBuilder = DatabaseOperationSelectImpl
179+
.builder( jdbcOperation )
180+
.appendPostAction( loadedValueCollector );
181+
182+
final ConnectionLockTimeoutStrategy lockTimeoutStrategy = session
183+
.getDialect()
184+
.getLockingSupport()
185+
.getConnectionLockTimeoutStrategy();
186+
if ( lockTimeoutStrategy.getSupportedLevel() != ConnectionLockTimeoutStrategy.Level.NONE ) {
187+
final LockTimeoutHandler lockTimeoutHandler = new LockTimeoutHandler( Timeout.seconds( 2 ), lockTimeoutStrategy );
188+
operationBuilder.addSecondaryActionPair( lockTimeoutHandler, lockTimeoutHandler );
189+
}
190+
191+
final DatabaseOperationSelectImpl databaseOperation = operationBuilder.build();
192+
final Person person = databaseOperation.execute(
193+
Person.class,
194+
1,
195+
StandardStatementCreator.getStatementCreator( ScrollMode.FORWARD_ONLY ),
196+
jdbcParameterBindings,
197+
row -> (Person) row[0],
198+
SingleResultConsumer.instance(),
199+
executionContext
200+
);
201+
202+
assertThat( loadedValueCollector.loadedValues ).hasSize( 1 );
203+
} );
204+
205+
}
206+
141207
private static class LockTimeoutHandler implements PreAction, PostAction {
142208
private final ConnectionLockTimeoutStrategy lockTimeoutStrategy;
143209
private final Timeout timeout;
@@ -172,6 +238,17 @@ public void performPostAction(StatementAccess jdbcStatementAccess, Connection jd
172238
}
173239
}
174240

241+
private static class LoadedValueCollector implements PostAction {
242+
private final List<Object> loadedValues = new ArrayList<>();
243+
244+
@Override
245+
public void performPostAction(StatementAccess jdbcStatementAccess, Connection jdbcConnection, ExecutionContext executionContext) {
246+
loadedValues.forEach( (value) -> {
247+
System.out.printf( "Loaded value: %s\n", value );
248+
} );
249+
}
250+
}
251+
175252

176253
private PersonQuery createPersonQuery(
177254
EntityPersister entityDescriptor,

0 commit comments

Comments
 (0)