Skip to content

Commit

Permalink
HHH-16531 ColumnDefinitions should respect @column(columnDefinition)
Browse files Browse the repository at this point in the history
  • Loading branch information
quaff committed Apr 10, 2024
1 parent ce97a5d commit 4eb1925
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 3 deletions.
29 changes: 26 additions & 3 deletions hibernate-core/src/main/java/org/hibernate/mapping/Column.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
* of a relational database {@linkplain Table table}.
*
* @author Gavin King
* @author Yanming Zhou
*/
public class Column implements Selectable, Serializable, Cloneable, ColumnTypeInformation {

Expand Down Expand Up @@ -435,9 +436,31 @@ public Size getColumnSize(Dialect dialect, Mapping mapping) {

Size calculateColumnSize(Dialect dialect, Mapping mapping) {
Type type = getValue().getType();
Long lengthToUse = getLength();
Integer precisionToUse = getPrecision();
Integer scaleToUse = getScale();
Long lengthToUse = null;
Integer precisionToUse = null;
Integer scaleToUse = null;
if ( sqlTypeName == null ) {
lengthToUse = getLength();
precisionToUse = getPrecision();
scaleToUse = getScale();
}
else {
// @Column(columnDefinition) present
int start = sqlTypeName.indexOf( '(' );
int end = sqlTypeName.indexOf( ')' );
if ( start > 0 && end > start + 1 && !sqlTypeName.toLowerCase( Locale.ROOT ).startsWith( "enum" ) ) {
String[] array = sqlTypeName.substring( start + 1, end ).split( ",", 2 );
try {
lengthToUse = Long.valueOf( array[0].trim() );
precisionToUse = Integer.valueOf( array[0].trim() );
if ( array.length == 2 ) {
scaleToUse = Integer.valueOf( array[1].trim() );
}
}
catch ( NumberFormatException ignore ) {
}
}
}
if ( type instanceof EntityType ) {
type = getTypeForEntityValue( mapping, type, getTypeIndex() );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,29 @@

import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

import static org.hibernate.type.SqlTypes.isNumericOrDecimal;
import static org.hibernate.type.SqlTypes.isStringType;

/**
* Util class for comparing required vs actual column definition
*
* @author Gavin King
* @author Cedomir Igaly
* @author Steve Ebersole
* @author Andrea Boriero
* @author Yanming Zhou
*/
class ColumnDefinitions {

static boolean hasMatchingType(Column column, ColumnInformation columnInformation, Metadata metadata, Dialect dialect) {
if (column.getTypeName() != null) {
// @Column(columnDefinition) present
String required = new StringTokenizer( normalize( column.getTypeName() ), "( " ).nextToken();
String actual = normalize( columnInformation.getTypeName() );
return actual.equals( required );
}
boolean typesMatch = dialect.equivalentTypes( column.getSqlTypeCode(metadata), columnInformation.getTypeCode() )
|| normalize( stripArgs( column.getSqlType( metadata ) ) ).equals( normalize( columnInformation.getTypeName() ) );
if ( typesMatch ) {
Expand Down Expand Up @@ -248,6 +264,8 @@ private static String normalize(String typeName) {
else {
final String lowerCaseTypName = typeName.toLowerCase(Locale.ROOT);
switch (lowerCaseTypName) {
case "int":
return "integer";
case "character":
return "char";
case "character varying":
Expand Down
2 changes: 2 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/type/SqlTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* @see org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry
*
* @author Christian Beikov
* @author Yanming Zhou
*/
public class SqlTypes {
/**
Expand Down Expand Up @@ -684,6 +685,7 @@ public static boolean isStringType(int typeCode) {
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
return true;
default:
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.tool.schema.internal;

import org.hibernate.boot.model.TruthValue;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Value;
import org.hibernate.tool.schema.extract.internal.ColumnInformationImpl;
import org.hibernate.tool.schema.extract.spi.ColumnInformation;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.SqlTypes;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;

/**
* @author Yanming Zhou
*/
public class ColumnDefinitionsTest {

@Test
public void matchIntegerType() {
assertHasMatchingType("integer", "integer", true);
assertHasMatchingType("integer", "int", true);
assertHasMatchingType("int", "integer", true);
assertHasMatchingType("integer not null", "integer", true);
assertHasMatchingType("integer", "bigint", false);
assertHasMatchingType("bigint", "integer", false);
}

@Test
public void matchDecimalType() {
assertHasMatchingType("decimal(10,2) not null", "decimal", true);
assertHasMatchingType("decimal( 10 , 2 ) not null", "decimal", true);
}

@Test
public void matchVarcharLength() {
assertHasMatchingLength("varchar(255)", SqlTypes.VARCHAR, 255, true);
assertHasMatchingLength("varchar( 255 )", SqlTypes.VARCHAR, 255, true);
assertHasMatchingLength("varchar(250)", SqlTypes.VARCHAR, 255, false);
}

@Test
public void matchDecimalLength() {
assertHasMatchingLength("decimal(12,2)", SqlTypes.DECIMAL, 12, 2, true);
assertHasMatchingLength("decimal( 12 , 2 )", SqlTypes.DECIMAL, 12, 2, true);
assertHasMatchingLength("decimal(10,2)", SqlTypes.DECIMAL, 12, 2, false);
}

private void assertHasMatchingType(String columnDefinition, String actualTypeName, boolean matching) {
Column column = new Column();
column.setSqlType(columnDefinition);

ColumnInformation columnInformation = new ColumnInformationImpl(
null,
null,
0,
actualTypeName,
255,
0,
TruthValue.TRUE
);

assertThat(ColumnDefinitions.hasMatchingType(column, columnInformation, null, null),
is(matching));
}

private void assertHasMatchingLength(String columnDefinition, int actualTypeCode, int columnSize, boolean matching) {
ColumnInformation columnInformation = new ColumnInformationImpl(
null,
null,
actualTypeCode,
null,
columnSize,
0,
TruthValue.TRUE
);
assertHasMatchingLength(columnDefinition, columnInformation, matching);
}

private void assertHasMatchingLength(String columnDefinition, int actualTypeCode, int columnSize, int decimalDigits, boolean matching) {
ColumnInformation columnInformation = new ColumnInformationImpl(
null,
null,
actualTypeCode,
null,
columnSize,
decimalDigits,
TruthValue.TRUE
);
assertHasMatchingLength(columnDefinition, columnInformation, matching);
}

private void assertHasMatchingLength(String columnDefinition, ColumnInformation columnInformation, boolean matching) {
Column column = new Column();
Value value = mock();
given(value.getType()).willReturn(JavaObjectType.INSTANCE);
column.setValue(value);
column.setSqlType(columnDefinition);
assertThat(ColumnDefinitions.hasMatchingLength(column, columnInformation, null, new H2Dialect()),
is(matching));
}
}

0 comments on commit 4eb1925

Please sign in to comment.