This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch 8.0.x-hibernate7-dev in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit c551d5bd43c60241d4ff2fe94fd8af88e6b9c6ac Author: Walter Duque de Estrada <[email protected]> AuthorDate: Tue Mar 10 21:03:52 2026 -0500 hibernate 7 ChangedColumnChangeGenerator --- .../diff/ChangedColumnChangeGenerator.java | 133 +++++++++++---------- .../diff/ChangedColumnChangeGeneratorSpec.groovy | 127 ++++++++++++++++++++ 2 files changed, 198 insertions(+), 62 deletions(-) diff --git a/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/diff/ChangedColumnChangeGenerator.java b/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/diff/ChangedColumnChangeGenerator.java index 7508e0edd1..17d5f6f65b 100644 --- a/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/diff/ChangedColumnChangeGenerator.java +++ b/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/diff/ChangedColumnChangeGenerator.java @@ -1,6 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package liquibase.ext.hibernate.diff; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; import liquibase.change.Change; import liquibase.database.Database; @@ -23,79 +44,67 @@ public class ChangedColumnChangeGenerator extends liquibase.diff.output.changelo @Override public int getPriority(Class<? extends DatabaseObject> objectType, Database database) { - if (Column.class.isAssignableFrom(objectType)) { - return PRIORITY_ADDITIONAL; - } - return PRIORITY_NONE; + return Column.class.isAssignableFrom(objectType) ? PRIORITY_ADDITIONAL : PRIORITY_NONE; } @Override - protected void handleTypeDifferences( - Column column, - ObjectDifferences differences, - DiffOutputControl control, - List<Change> changes, - Database referenceDatabase, - Database comparisonDatabase) { - if (referenceDatabase instanceof HibernateDatabase || comparisonDatabase instanceof HibernateDatabase) { - handleSizeChange(column, differences, control, changes, referenceDatabase, comparisonDatabase); + protected void handleTypeDifferences(Column column, ObjectDifferences differences, DiffOutputControl control, List<Change> changes, Database referenceDatabase, Database comparisonDatabase) { + if (isHibernateRelated(referenceDatabase, comparisonDatabase)) { + handleHibernateTypeDifferences(column, differences, control, changes, referenceDatabase, comparisonDatabase); } else { super.handleTypeDifferences(column, differences, control, changes, referenceDatabase, comparisonDatabase); } } - private void handleSizeChange( - Column column, - ObjectDifferences differences, - DiffOutputControl control, - List<Change> changes, - Database referenceDatabase, - Database comparisonDatabase) { - if (TYPES_TO_IGNORE_SIZE.stream() - .anyMatch(s -> s.equalsIgnoreCase(column.getType().getTypeName()))) { - return; - } - Difference difference = differences.getDifference("type"); - if (difference != null) { - for (Difference d : differences.getDifferences()) { - if (!(d.getReferenceValue() instanceof DataType)) { - differences.removeDifference(d.getField()); - continue; - } - Integer originalSize = ((DataType) d.getReferenceValue()).getColumnSize(); - Integer newSize = ((DataType) d.getComparedValue()).getColumnSize(); - if (newSize == null || originalSize == null || newSize.equals(originalSize)) { - differences.removeDifference(d.getField()); - } - } - super.handleTypeDifferences(column, differences, control, changes, referenceDatabase, comparisonDatabase); - } + private void handleHibernateTypeDifferences(Column column, ObjectDifferences differences, DiffOutputControl control, List<Change> changes, Database refDb, Database compDb) { + if (shouldIgnoreSize(column)) return; + + Optional.ofNullable(differences.getDifference("type")).ifPresent(diff -> { + filterIrrelevantDifferences(differences); + super.handleTypeDifferences(column, differences, control, changes, refDb, compDb); + }); + } + + private void filterIrrelevantDifferences(ObjectDifferences differences) { + new ArrayList<>(differences.getDifferences()).stream() + .filter(Predicate.not(this::isMeaningfulDifference)) + .forEach(diff -> differences.removeDifference(diff.getField())); + } + + private boolean isMeaningfulDifference(Difference diff) { + return diff.getReferenceValue() instanceof DataType refType && + diff.getComparedValue() instanceof DataType compType && + !isSizeEqualOrNull(refType.getColumnSize(), compType.getColumnSize()); } @Override - protected void handleDefaultValueDifferences( - Column column, - ObjectDifferences differences, - DiffOutputControl control, - List<Change> changes, - Database referenceDatabase, - Database comparisonDatabase) { - if (referenceDatabase instanceof HibernateDatabase || comparisonDatabase instanceof HibernateDatabase) { - Difference difference = differences.getDifference("defaultValue"); - if (difference != null - && difference.getReferenceValue() == null - && difference.getComparedValue() instanceof DatabaseFunction) { - // database sometimes adds a function default value, like for timestamp columns - return; - } - difference = differences.getDifference("defaultValue"); - if (difference != null) { - super.handleDefaultValueDifferences( - column, differences, control, changes, referenceDatabase, comparisonDatabase); - } - // do nothing, types tend to not match with hibernate + protected void handleDefaultValueDifferences(Column column, ObjectDifferences differences, DiffOutputControl control, List<Change> changes, Database referenceDatabase, Database comparisonDatabase) { + if (!isHibernateRelated(referenceDatabase, comparisonDatabase)) { + super.handleDefaultValueDifferences(column, differences, control, changes, referenceDatabase, comparisonDatabase); + return; } - super.handleDefaultValueDifferences( - column, differences, control, changes, referenceDatabase, comparisonDatabase); + + if (isFunctionDefaultAddingToNull(differences)) return; + + Optional.ofNullable(differences.getDifference("defaultValue")) + .ifPresent(d -> super.handleDefaultValueDifferences(column, differences, control, changes, referenceDatabase, comparisonDatabase)); + } + + private boolean shouldIgnoreSize(Column column) { + return TYPES_TO_IGNORE_SIZE.stream().anyMatch(type -> type.equalsIgnoreCase(column.getType().getTypeName())); + } + + private boolean isSizeEqualOrNull(Integer s1, Integer s2) { + return s1 == null || s2 == null || s1.equals(s2); + } + + private boolean isFunctionDefaultAddingToNull(ObjectDifferences differences) { + return Optional.ofNullable(differences.getDifference("defaultValue")) + .filter(d -> d.getReferenceValue() == null && d.getComparedValue() instanceof DatabaseFunction) + .isPresent(); + } + + private boolean isHibernateRelated(Database d1, Database d2) { + return d1 instanceof HibernateDatabase || d2 instanceof HibernateDatabase; } } diff --git a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/diff/ChangedColumnChangeGeneratorSpec.groovy b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/diff/ChangedColumnChangeGeneratorSpec.groovy new file mode 100644 index 0000000000..753e1d3d69 --- /dev/null +++ b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/diff/ChangedColumnChangeGeneratorSpec.groovy @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package liquibase.ext.hibernate.diff + +import liquibase.change.Change +import liquibase.database.Database +import liquibase.diff.Difference +import liquibase.diff.ObjectDifferences +import liquibase.diff.output.DiffOutputControl +import liquibase.ext.hibernate.database.HibernateDatabase +import liquibase.statement.DatabaseFunction +import liquibase.structure.core.Column +import liquibase.structure.core.DataType +import liquibase.structure.core.Table +import spock.lang.Specification + +class ChangedColumnChangeGeneratorSpec extends Specification { + + ChangedColumnChangeGenerator generator = new ChangedColumnChangeGenerator() + + def "getPriority returns correct priority for Column and others"() { + expect: + generator.getPriority(Column, Mock(Database)) == 50 // PRIORITY_ADDITIONAL + generator.getPriority(DataType, Mock(Database)) == -1 // PRIORITY_NONE + } + + def "handleTypeDifferences ignores size for TIMESTAMP and TIME for HibernateDatabase"() { + given: + Column column = new Column() + column.setType(new DataType(typeName)) + ObjectDifferences differences = Mock() + DiffOutputControl control = Mock() + List<Change> changes = [] + HibernateDatabase hibernateDatabase = Mock() + + when: + generator.handleTypeDifferences(column, differences, control, changes, hibernateDatabase, hibernateDatabase) + + then: + 0 * differences.getDifference("type") + + where: + typeName << ["TIMESTAMP", "TIME", "timestamp", "time"] + } + + def "handleTypeDifferences handles size changes for other types"() { + given: + Column column = new Column() + column.setName("myCol") + column.setRelation(new Table(name: "myTable")) + column.setType(new DataType("VARCHAR")) + ObjectDifferences differences = Mock() + DiffOutputControl control = Mock() + List<Change> changes = [] + HibernateDatabase hibernateDatabase = Mock() + + Difference diff = new Difference("type", new DataType("VARCHAR(10)"), new DataType("VARCHAR(20)")) + diff.referenceValue.setColumnSize(10) + diff.comparedValue.setColumnSize(20) + + when: + generator.handleTypeDifferences(column, differences, control, changes, hibernateDatabase, hibernateDatabase) + + then: + _ * differences.getDifference("type") >> diff + 1 * differences.getDifferences() >> [diff] + 0 * differences.removeDifference("type") + } + + def "handleTypeDifferences removes difference if size is same"() { + given: + Column column = new Column() + column.setName("myCol") + column.setRelation(new Table(name: "myTable")) + column.setType(new DataType("VARCHAR")) + ObjectDifferences differences = Mock() + DiffOutputControl control = Mock() + List<Change> changes = [] + HibernateDatabase hibernateDatabase = Mock() + + Difference diff = new Difference("type", new DataType("VARCHAR(10)"), new DataType("VARCHAR(10)")) + diff.referenceValue.setColumnSize(10) + diff.comparedValue.setColumnSize(10) + + when: + generator.handleTypeDifferences(column, differences, control, changes, hibernateDatabase, hibernateDatabase) + + then: + _ * differences.getDifference("type") >> diff + 1 * differences.getDifferences() >> [diff] + 1 * differences.removeDifference("type") + } + + def "handleDefaultValueDifferences ignores null to DatabaseFunction changes for HibernateDatabase"() { + given: + Column column = new Column() + ObjectDifferences differences = Mock() + DiffOutputControl control = Mock() + List<Change> changes = [] + HibernateDatabase hibernateDatabase = Mock() + + Difference diff = new Difference("defaultValue", null, new DatabaseFunction("now()")) + + when: + generator.handleDefaultValueDifferences(column, differences, control, changes, hibernateDatabase, hibernateDatabase) + + then: + 1 * differences.getDifference("defaultValue") >> diff + 0 * differences.getDifferences() + } +}
