SQL 序列映射 - SqlServer2012SpatialDialect、Grails 4、Hibernate 5 兼容性

SQL Sequence mapping - SqlServer2012SpatialDialect, Grails 4, Hibernate 5 compatibility

提问人:lujjas 提问时间:3/23/2023 最后编辑:lujjas 更新时间:3/31/2023 访问量:72

问:

到目前为止,在配置文件 (application.yml) 中,hibernate-dialect 已设置如下:

dataSource:
   pooled: true
   jmxExport: true
   driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
   dialect: org.hibernate.spatial.dialect.sqlserver.SqlServer2008SpatialDialect

一切正常,但是由于某些类(数据库表)的 SQL 序列映射,我们必须将方言升级到(例如 Sql Server 2014 和 Hibernate 中的序列),并且有许多文档如何在类中映射 SQL 序列(例如 https://www.linkedin.com/pulse/hibernate-sql-sequence-avital-arvivo)。SqlServer2012SpatialDialect

问题是从 hibernate-spatial 开始兼容,我无法让应用程序运行,因为每次我使用另一个 hibernate-spatial 5.* 版本进行测试时都会遇到以下错误:Grails45.4.0SqlServer2012SpatialDialect

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'hibernateDatastore': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.orm.hibernate.HibernateDatastore]: Constructor threw exception; nested exception is java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.Long (java.lang.Integer and java.lang.Long are in module java.base of loader 'bootstrap')
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:304)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:285)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1340)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1186)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1471)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1435)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1303)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1213)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:670)
... 109 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.orm.hibernate.HibernateDatastore]: Constructor threw exception; nested exception is java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.Long (java.lang.Integer and java.lang.Long are in module java.base of loader 'bootstrap')
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:184)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:117)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:300)
... 125 common frames omitted
Caused by: java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.Long (java.lang.Integer and java.lang.Long are in module java.base of loader 'bootstrap')
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.getLong(SQLServerResultSet.java:2328)
at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.resultSetStartValueSize(SequenceInformationExtractorLegacyImpl.java:129)
at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.extractMetadata(SequenceInformationExtractorLegacyImpl.java:59)
at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.initializeSequences(DatabaseInformationImpl.java:65)
at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.<init>(DatabaseInformationImpl.java:59)
at org.hibernate.tool.schema.internal.Helper.buildDatabaseInformation(Helper.java:155)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:96)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:184)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:73)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:314)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:468)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:708)
at org.grails.orm.hibernate.cfg.HibernateMappingContextConfiguration.buildSessionFactory(HibernateMappingContextConfiguration.java:287)
at org.grails.orm.hibernate.connections.HibernateConnectionSourceFactory.create(HibernateConnectionSourceFactory.java:86)
at org.grails.orm.hibernate.connections.AbstractHibernateConnectionSourceFactory.create(AbstractHibernateConnectionSourceFactory.java:39)
at org.grails.orm.hibernate.connections.AbstractHibernateConnectionSourceFactory.create(AbstractHibernateConnectionSourceFactory.java:23)
at org.grails.datastore.mapping.core.connections.AbstractConnectionSourceFactory.create(AbstractConnectionSourceFactory.java:64)
at org.grails.datastore.mapping.core.connections.AbstractConnectionSourceFactory.create(AbstractConnectionSourceFactory.java:52)
at org.grails.datastore.mapping.core.connections.ConnectionSourcesInitializer.create(ConnectionSourcesInitializer.groovy:24)
at org.grails.orm.hibernate.HibernateDatastore.<init>(HibernateDatastore.java:212)
at jdk.internal.reflect.GeneratedConstructorAccessor95.newInstance(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:172)
... 127 common frames omitted
> Task :bootRun FAILED

为了正确映射 SQL 序列,我是否缺少任何步骤,或者我应该以另一种方式扩展或分配?SqlServer2012SpatialDialect

sql server: Microsoft SQL Server 2019
gradle: 'com.microsoft.sqlserver', name: 'mssql-jdbc',version: '12.2.0.jre11'
休眠-5.x 休眠空间 圣杯-4

评论


答:

0赞 lujjas 3/27/2023 #1

在根据以下文档保存数据条目后,我能够运行应用程序并最终从 SQL Server 序列中检索数据库表 ID:https://discourse.hibernate.org/t/java-lang-classcastexception-java-math-cannot-be-cast-to-java-lang-long-while-starting-server/4074/4

application.yml如下所示:

dataSource:
   pooled: true
   jmxExport: true
   driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
   dialect:  dialect.CustomSqlServer2012SpatialDialect
   dbCreate: update

必须在应用中创建必要的 CustomDialect java 类和 SequenceInformationExtractorImpl,如下所示: CustomSqlServer2012SpatialDialect

import org.hibernate.spatial.dialect.sqlserver.SqlServer2012SpatialDialect;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;

public class CustomSqlServer2012SpatialDialect extends SqlServer2012SpatialDialect {

    @Override
    public SequenceInformationExtractor getSequenceInformationExtractor() {
        if ( getQuerySequencesString() == null ) {
            return SequenceInformationExtractorNoOpImpl.INSTANCE;
        }
        else {
            return SequenceInformationExtractorImpl.INSTANCE;
        }
    }

}

SequenceInformationExtractorImpl

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.boot.model.relational.QualifiedSequenceName;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationImpl;
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;

/**
 * @author Steve Ebersole
 */
public class SequenceInformationExtractorImpl implements SequenceInformationExtractor {
    /**
     * Singleton access
     */
    public static final SequenceInformationExtractorImpl INSTANCE = new SequenceInformationExtractorImpl();

    @Override
    public Iterable<SequenceInformation> extractMetadata(final ExtractionContext extractionContext) throws SQLException {
        final String lookupSql = extractionContext.getJdbcEnvironment().getDialect().getQuerySequencesString();

        // *should* never happen, but to be safe in the interest of performance...
        if ( lookupSql == null ) {
            return SequenceInformationExtractorNoOpImpl.INSTANCE.extractMetadata( extractionContext );
        }

        final IdentifierHelper identifierHelper = extractionContext.getJdbcEnvironment().getIdentifierHelper();
        final Statement statement = extractionContext.getJdbcConnection().createStatement();
        try {
            final ResultSet resultSet = statement.executeQuery( lookupSql );
            try {
                final List<SequenceInformation> sequenceInformationList = new ArrayList<>();
                while ( resultSet.next() ) {
                    sequenceInformationList.add(
                            new SequenceInformationImpl(
                                    new QualifiedSequenceName(
                                            identifierHelper.toIdentifier(
                                                    resultSetCatalogName( resultSet )
                                            ),
                                            identifierHelper.toIdentifier(
                                                    resultSetSchemaName( resultSet )
                                            ),
                                            identifierHelper.toIdentifier(
                                                    resultSetSequenceName( resultSet )
                                            )
                                    ),
                                    resultSetStartValueSize( resultSet ),
                                    resultSetMinValue( resultSet ),
                                    resultSetMaxValue( resultSet ),
                                    resultSetIncrementValue( resultSet )
                            )
                    );
                }
                return sequenceInformationList;
            }
            finally {
                try {
                    resultSet.close();
                }
                catch (final SQLException ignore) {
                }
            }
        }
        finally {
            try {
                statement.close();
            }
            catch (final SQLException ignore) {
            }
        }
    }

    protected String sequenceNameColumn() {
        return "sequence_name";
    }

    protected String sequenceCatalogColumn() {
        return "sequence_catalog";
    }

    protected String sequenceSchemaColumn() {
        return "sequence_schema";
    }

    protected String sequenceStartValueColumn() {
        return "start_value";
    }

    protected String sequenceMinValueColumn() {
        return "minimum_value";
    }

    protected String sequenceMaxValueColumn() {
        return "maximum_value";
    }

    protected String sequenceIncrementColumn() {
        return "increment";
    }

    protected String resultSetSequenceName(final ResultSet resultSet) throws SQLException {
        return resultSet.getString( sequenceNameColumn() );
    }

    protected String resultSetCatalogName(final ResultSet resultSet) throws SQLException {
        final String column = sequenceCatalogColumn();
        return column != null ? resultSet.getString( column ) : null;
    }

    protected String resultSetSchemaName(final ResultSet resultSet) throws SQLException {
        final String column = sequenceSchemaColumn();
        return column != null ? resultSet.getString( column ) : null;
    }

    protected Long resultSetStartValueSize(final ResultSet resultSet) throws SQLException {
        final String column = sequenceStartValueColumn();
        return column != null ? resultSetGetAsLong( resultSet, column ) : null;
    }

    protected Long resultSetMinValue(final ResultSet resultSet) throws SQLException {
        final String column = sequenceMinValueColumn();
        return column != null ? resultSetGetAsLong( resultSet, column ) : null;
    }

    protected Long resultSetMaxValue(final ResultSet resultSet) throws SQLException {
        final String column = sequenceMaxValueColumn();
        return column != null ? resultSetGetAsLong( resultSet, column ) : null;
    }

    protected Long resultSetIncrementValue(final ResultSet resultSet) throws SQLException {
        final String column = sequenceIncrementColumn();
        return column != null ? resultSetGetAsLong( resultSet, column ) : null;
    }

    private Long resultSetGetAsLong(final ResultSet resultSet, final String column) throws SQLException {
        final Object value = resultSet.getObject(column);
        if (value == null || !(value instanceof Number)) {
            return null;
        }
        return ((Number)value).longValue();
    }
}

则应按如下方式配置实体类中的映射:

static mapping = {
    id column: "ObjectId" // the column name in the database table
    id generator: "sequence",params: [sequence:"dbo.seq_NAME",sequenceName:"dbo.seq_NAME"]
}

我在build.gradle中将jdbc dependency:替换为以下内容'com.microsoft.sqlserver', name: 'mssql-jdbc',version: '12.2.0.jre11''com.microsoft.sqlserver', name: 'mssql-jdbc',version: '7.2.2.jre11'

休眠版本设置如下: 5.4.25