Migration of dialect to Hibernate 6

For this, columnTypeInformation throws Null Pointer, what should we set it to ?

java.lang.NullPointerException: Cannot invoke "org.hibernate.tool.schema.extract.spi.ColumnTypeInformation.getTypeName()" because "columnTypeInformation" is null
	at org.hibernate.dialect.OracleArrayJdbcType.resolveType(OracleArrayJdbcType.java:65)
	at com.cox.config.utils.Oracle12cCustomDialect.resolveSqlTypeDescriptor(Oracle12cCustomDialect.java:27)
	at org.hibernate.sql.results.jdbc.internal.ResultSetAccess.resolveType(ResultSetAccess.java:93)

Update: I just tried ColumnTypeInformation.EMPTY for that field which results in following error

Caused by: java.sql.SQLException: Invalid column type
	at oracle.jdbc.driver.NamedTypeAccessor.getSQLXML(NamedTypeAccessor.java:340)
	at oracle.jdbc.driver.GeneratedStatement.getSQLXML(GeneratedStatement.java:280)
	at oracle.jdbc.driver.GeneratedScrollableResultSet.getSQLXML(GeneratedScrollableResultSet.java:361)
	at com.zaxxer.hikari.pool.HikariProxyResultSet.getSQLXML(HikariProxyResultSet.java)

Resolved:
Hey @beikov A bigggggggg thank youuuuuu for the speedy resolution for my issue.
I got it working by slightly modifying your code suggestion. I just want you to verify, for ColumnTypeInformation implementation below whether there is any inconsistency or any obvious error which may cause issues in future.

    @Override
    public JdbcType resolveSqlTypeDescriptor(String columnTypeName, int jdbcTypeCode, int precision, int scale, JdbcTypeRegistry jdbcTypeRegistry) {
        final JdbcType jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
        if ( jdbcTypeCode == Types.ARRAY && "MDSYS.SDO_ORDINATE_ARRAY".equals( columnTypeName ) && jdbcType instanceof ArrayJdbcType ) {
            return ( (ArrayJdbcType) jdbcType ).resolveType(
                    jdbcTypeRegistry.getTypeConfiguration(),
                    this,
                    jdbcTypeRegistry.getDescriptor( SqlTypes.NUMERIC),
                    new ColumnTypeInformation() {
                        public TruthValue getNullable() {
                            return TruthValue.UNKNOWN;
                        }

                        public int getTypeCode() {
                            return 2003;
                        }

                        public String getTypeName() {
                            return "MDSYS.SDO_ORDINATE_ARRAY";
                        }

                        public int getColumnSize() {
                            return 0;
                        }

                        public int getDecimalDigits() {
                            return 0;
                        }
                    }
            );
        }
        return super.resolveSqlTypeDescriptor(columnTypeName, jdbcTypeCode, precision, scale, jdbcTypeRegistry);
    }
}

It’s fine, thanks for sharing the troubles you were running into.

1 Like

@beikov : I have a postgreSQL dialect with

public class CustomPostgreSQLDialect extends PostgreSQLDialect {

  public CustomPostgreSQLDialect() {
    super();
    registerFunction(
        "interval_in_minutes",
        new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "(?1 * interval '1 minute')"));
  }
}

Wanted to covert this function to hibernate 6, and I did Like this

@Override
  public void contributeFunctions(FunctionContributions functionContributions) {
    functionContributions
        .getFunctionRegistry()
        .registerPattern(
            "interval_in_minutes",
            "(?1 * interval '1 minute')",
            functionContributions
                .getTypeConfiguration()
                .getBasicTypeRegistry()
                .resolve(StandardBasicTypes.TIMESTAMP));
  }

Getting error saying

Operand of + is of type ‘java.sql.Timestamp’ which is not a temporal amount (it is not an instance of ‘java.time.TemporalAmount’)

You’ll also have to share the query and the entity model. What is the purpose of that function anyway? According to your function definition, the result type of that function is supposed to be Timestamp, but the only valid multiplication operand with interval is a number and that produces an interval again as well. So the return type should rather be StandardBasicTypes.DURATION.

Apart from that, you can actually use Hibernates native support for durations now in HQL and don’t need custom functions anymore. Also see Hibernate ORM User Guide

Now you can just use entityManager.createQuery("select e.timestamp + 1 minute from MyEntity e")

About the function contributor: I have a custom dialect class and want to add the “match” function to MariaDB, which is basically the same requirement as the initial post.
It seems it is possible without a “org.hibernate.boot.model.FunctionContributor” file. I could do it by overriding “initializeFunctionRegistry”:


public class MariaDBDialectCustom extends org.hibernate.dialect.MariaDBDialect
{
    ...

    @Override
    public void initializeFunctionRegistry(FunctionContributions functionContributions) {
        super.initializeFunctionRegistry(functionContributions);
        
        functionContributions.getFunctionRegistry().registerPattern("match", "match (?1) against (?2 in boolean mode)", 
                functionContributions.getTypeConfiguration().getBasicTypeRegistry().resolve(StandardBasicTypes.DOUBLE));
    }
}

But according to this thread, custom dialects are discouraged?

But according to this thread, custom dialects are discouraged?

Correct, it is discouraged. Use the FunctionContributor instead.

Thanks for the clarification! Is there a good cause not to use a custom dialect, or can I stick to it as long as this feature is not removed ;-)?

You can keep on using a custom dialect if you prefer, just make sure you expose all the constructors that the superclass does to enjoy auto-configuration based on the database version.

Good point, this is something I should add ;-).