Skip to content

Commit 4d8f26d

Browse files
committed
proof of concept for SchemaManager.resynchronizeSequences()
1 parent 881a09f commit 4d8f26d

24 files changed

+391
-44
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/relational/InitCommand.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
package org.hibernate.boot.model.relational;
66

7+
import java.util.Arrays;
8+
79
/**
810
* A general SQL command to be used while initializing a schema.
911
*
@@ -14,4 +16,15 @@ public record InitCommand(String... initCommands) {
1416
public String[] getInitCommands() {
1517
return initCommands;
1618
}
19+
20+
@Override
21+
public boolean equals(Object object) {
22+
return object instanceof InitCommand that
23+
&& Arrays.equals( this.initCommands, that.initCommands );
24+
}
25+
26+
@Override
27+
public int hashCode() {
28+
return Arrays.hashCode( initCommands );
29+
}
1730
}

hibernate-core/src/main/java/org/hibernate/generator/GeneratorCreationContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.hibernate.mapping.PersistentClass;
1313
import org.hibernate.mapping.Property;
1414
import org.hibernate.mapping.RootClass;
15+
import org.hibernate.mapping.Value;
1516
import org.hibernate.service.ServiceRegistry;
1617
import org.hibernate.type.Type;
1718

@@ -64,6 +65,11 @@ public interface GeneratorCreationContext {
6465
*/
6566
Property getProperty();
6667

68+
/**
69+
* The identifier.
70+
*/
71+
Value getValue();
72+
6773
/**
6874
* Mapping details for the identifier type.
6975
*/

hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.hibernate.MappingException;
1212
import org.hibernate.boot.model.naming.Identifier;
1313
import org.hibernate.boot.model.relational.Database;
14+
import org.hibernate.boot.model.relational.InitCommand;
1415
import org.hibernate.boot.model.relational.QualifiedName;
1516
import org.hibernate.boot.model.relational.QualifiedNameParser;
1617
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
@@ -24,6 +25,7 @@
2425
import org.hibernate.id.IdentifierGenerator;
2526
import org.hibernate.id.PersistentIdentifierGenerator;
2627
import org.hibernate.id.SequenceMismatchStrategy;
28+
import org.hibernate.mapping.Table;
2729
import org.hibernate.service.ServiceRegistry;
2830
import org.hibernate.tool.schema.Action;
2931
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
@@ -151,6 +153,7 @@ public class SequenceStyleGenerator
151153
private DatabaseStructure databaseStructure;
152154
private Optimizer optimizer;
153155
private Type identifierType;
156+
private Table table;
154157

155158
/**
156159
* Getter for property 'databaseStructure'.
@@ -190,6 +193,7 @@ public void configure(GeneratorCreationContext creationContext, Properties param
190193
final var dialect = jdbcEnvironment.getDialect();
191194

192195
identifierType = creationContext.getType();
196+
table = creationContext.getValue().getTable();
193197

194198
final var sequenceName = determineSequenceName( parameters, jdbcEnvironment, serviceRegistry );
195199
final int initialValue = determineInitialValue( parameters );
@@ -329,6 +333,17 @@ private boolean isSchemaToBeRecreated(String contributor, ConfigurationService c
329333
@Override
330334
public void registerExportables(Database database) {
331335
databaseStructure.registerExportables( database );
336+
table.addResyncCommand( context -> {
337+
final String sequenceName = context.format( databaseStructure.getPhysicalName() );
338+
final String tableName = context.format( table.getQualifiedTableName() );
339+
final String primaryKeyColumnName = table.getPrimaryKey().getColumn( 0 ).getName();
340+
final String sequenceCurrentValue =
341+
database.getDialect().getSequenceSupport()
342+
.getSelectSequenceNextValString( sequenceName );
343+
final int increment = databaseStructure.getIncrementSize();
344+
return new InitCommand( "alter sequence %s restart with select greatest(max(%s)+%d,%s) from %s"
345+
.formatted( sequenceName, primaryKeyColumnName, increment, sequenceCurrentValue, tableName ) );
346+
} );
332347
}
333348

334349
@Override
@@ -380,7 +395,7 @@ private static QualifiedName sequenceName(
380395

381396
/**
382397
* Determine the name of the column used to store the generator value in
383-
* the db.
398+
* the database.
384399
* <p>
385400
* Called during {@linkplain #configure configuration} <b>when resolving to a
386401
* physical table</b>.
@@ -409,8 +424,8 @@ protected int determineInitialValue(Properties params) {
409424
}
410425

411426
/**
412-
* Determine the increment size to be applied. The exact implications of
413-
* this value depends on the {@linkplain #getOptimizer() optimizer} being used.
427+
* Determine the increment size to be applied. The exact implications of
428+
* this value depend on the {@linkplain #getOptimizer() optimizer} in use.
414429
* <p>
415430
* Called during {@linkplain #configure configuration}.
416431
*
@@ -438,7 +453,7 @@ protected OptimizerDescriptor determineOptimizationStrategy(Properties params, i
438453

439454
/**
440455
* In certain cases we need to adjust the increment size based on the
441-
* selected optimizer. This is the hook to achieve that.
456+
* selected optimizer. This is the hook to achieve that.
442457
*
443458
* @param optimizationStrategy The optimizer strategy (name)
444459
* @param incrementSize The {@link #determineIncrementSize determined increment size}

hibernate-core/src/main/java/org/hibernate/mapping/Property.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,11 @@ public Property getProperty() {
602602
return Property.this;
603603
}
604604

605+
@Override
606+
public Value getValue() {
607+
return value;
608+
}
609+
605610
@Override
606611
public SqlStringGenerationContext getSqlStringGenerationContext() {
607612
return context.getSqlStringGenerationContext();

hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ public Generator createGenerator(
422422
Property property,
423423
GeneratorSettings defaults) {
424424
if ( customIdGeneratorCreator != null ) {
425-
final var context = new IdGeneratorCreationContext( rootClass, property, defaults );
425+
final var context = new IdGeneratorCreationContext( this, rootClass, property, defaults );
426426
final var generator = customIdGeneratorCreator.createGenerator( context );
427427
if ( generator.allowAssignedIdentifiers() && nullValue == null ) {
428428
setNullValueUndefined();
@@ -1074,11 +1074,13 @@ public Long[] getColumnLengths() {
10741074
}
10751075

10761076
private class IdGeneratorCreationContext implements GeneratorCreationContext {
1077+
private final SimpleValue identifier;
10771078
private final RootClass rootClass;
10781079
private final Property property;
10791080
private final GeneratorSettings defaults;
10801081

1081-
public IdGeneratorCreationContext(RootClass rootClass, Property property, GeneratorSettings defaults) {
1082+
public IdGeneratorCreationContext(SimpleValue identifier, RootClass rootClass, Property property, GeneratorSettings defaults) {
1083+
this.identifier = identifier;
10821084
this.rootClass = rootClass;
10831085
this.property = property;
10841086
this.defaults = defaults;
@@ -1124,6 +1126,11 @@ public Property getProperty() {
11241126
return property;
11251127
}
11261128

1129+
@Override
1130+
public Value getValue() {
1131+
return identifier;
1132+
}
1133+
11271134
@Override
11281135
public Type getType() {
11291136
return SimpleValue.this.getType();

hibernate-core/src/main/java/org/hibernate/mapping/Table.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public class Table implements Serializable, ContributableDatabaseObject {
7272
private String options;
7373

7474
private List<Function<SqlStringGenerationContext, InitCommand>> initCommandProducers;
75+
private List<Function<SqlStringGenerationContext, InitCommand>> resyncCommandProducers;
7576

7677
@Deprecated(since="6.2", forRemoval = true)
7778
public Table() {
@@ -808,16 +809,28 @@ public void addInitCommand(Function<SqlStringGenerationContext, InitCommand> com
808809
}
809810

810811
public List<InitCommand> getInitCommands(SqlStringGenerationContext context) {
811-
if ( initCommandProducers == null ) {
812-
return emptyList();
813-
}
814-
else {
815-
final List<InitCommand> initCommands = new ArrayList<>();
816-
for ( Function<SqlStringGenerationContext, InitCommand> producer : initCommandProducers ) {
817-
initCommands.add( producer.apply( context ) );
818-
}
819-
return unmodifiableList( initCommands );
812+
return initCommandProducers == null
813+
? emptyList()
814+
: initCommandProducers.stream()
815+
.map( producer -> producer.apply( context ) )
816+
.toList();
817+
}
818+
819+
public void addResyncCommand(Function<SqlStringGenerationContext, InitCommand> commandProducer) {
820+
if ( resyncCommandProducers == null ) {
821+
resyncCommandProducers = new ArrayList<>();
820822
}
823+
resyncCommandProducers.add( commandProducer );
824+
}
825+
826+
public List<InitCommand> getResyncCommands(SqlStringGenerationContext context) {
827+
return resyncCommandProducers == null
828+
? emptyList()
829+
: resyncCommandProducers.stream()
830+
.map( producer -> producer.apply( context ) )
831+
.distinct()
832+
.toList();
833+
821834
}
822835

823836
public String getOptions() {

hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,16 @@ public interface SchemaManager extends jakarta.persistence.SchemaManager {
9595
@Incubating
9696
void populate();
9797

98+
/**
99+
* Resynchronize sequences after importing test data.
100+
* <p>
101+
* Programmatic way to run {@link org.hibernate.tool.schema.spi.SequenceSynchronizer}.
102+
*
103+
* @since 7.2
104+
*/
105+
@Incubating
106+
void resynchronizeSequences();
107+
98108
/**
99109
* Obtain an instance which targets the given schema.
100110
* <p>

hibernate-core/src/main/java/org/hibernate/relational/internal/SchemaManagerImpl.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,20 @@ public void populate() {
142142
);
143143
}
144144

145+
@Override
146+
public void resynchronizeSequences() {
147+
Map<String, Object> properties = new HashMap<>( sessionFactory.getProperties() );
148+
properties.put( JAKARTA_HBM2DDL_DATABASE_ACTION, Action.SYNCHRONIZE );
149+
properties.put( JAKARTA_HBM2DDL_SCRIPTS_ACTION, Action.NONE );
150+
addSchemaAndCatalog( properties );
151+
SchemaManagementToolCoordinator.process(
152+
metadata,
153+
sessionFactory.getServiceRegistry(),
154+
properties,
155+
action -> {}
156+
);
157+
}
158+
145159
@Override
146160
public void create(boolean createSchemas) {
147161
exportMappedObjects( createSchemas );

hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,17 @@ public enum Action {
116116
*
117117
* @since 7.0
118118
*/
119-
POPULATE;
119+
POPULATE,
120+
/**
121+
* Synchronize sequences with the data held in tables.
122+
*
123+
* @apiNote This action is not defined by JPA.
124+
*
125+
* @see org.hibernate.tool.schema.spi.SchemaPopulator
126+
*
127+
* @since 7.2
128+
*/
129+
SYNCHRONIZE;
120130

121131
/**
122132
* @see #NONE
@@ -150,6 +160,10 @@ public enum Action {
150160
* @see #POPULATE
151161
*/
152162
public static final String ACTION_POPULATE = "populate";
163+
/**
164+
* @see #SYNCHRONIZE
165+
*/
166+
public static final String ACTION_SYNCHRONIZE = "synchronize";
153167

154168
/**
155169
* @see #NONE
@@ -197,6 +211,7 @@ public String getExternalHbm2ddlName() {
197211
case VALIDATE -> ACTION_VALIDATE;
198212
case UPDATE -> ACTION_UPDATE;
199213
case POPULATE -> ACTION_POPULATE;
214+
case SYNCHRONIZE -> ACTION_SYNCHRONIZE;
200215
default -> null;
201216
};
202217
}

hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaMigrator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ private void performMigration(
179179
final Set<String> exportIdentifiers = CollectionHelper.setOfSize( 50 );
180180

181181
final Database database = metadata.getDatabase();
182-
Exporter<AuxiliaryDatabaseObject> auxiliaryExporter = dialect.getAuxiliaryDatabaseObjectExporter();
182+
final Exporter<AuxiliaryDatabaseObject> auxiliaryExporter = dialect.getAuxiliaryDatabaseObjectExporter();
183183

184184
// Drop all AuxiliaryDatabaseObjects
185185
for ( AuxiliaryDatabaseObject auxiliaryDatabaseObject : database.getAuxiliaryDatabaseObjects() ) {
@@ -255,7 +255,7 @@ private void performMigration(
255255
}
256256
}
257257

258-
//NOTE : Foreign keys must be created *after* all tables of all namespaces for cross namespace fks. see HHH-10420
258+
//NOTE: Foreign keys must be created *after* all tables of all namespaces for cross-namespace fks. see HHH-10420
259259
for ( Namespace namespace : database.getNamespaces() ) {
260260
if ( schemaFilter.includeNamespace( namespace ) ) {
261261
final NameSpaceTablesInformation nameSpaceTablesInformation = tablesInformation.get( namespace );

0 commit comments

Comments
 (0)