GORM prevent creation of a foreign key constraint for a Domain

前端 未结 3 1894
一个人的身影
一个人的身影 2021-01-15 13:25

I am developing a web based application in Grails. I have come across a situation where I would like to try and suppress GORM from creating a foreign key constraint on a fie

3条回答
  •  醉话见心
    2021-01-15 13:33

    For those struggling with this problem using Grails 3 with gorm-hibernate5, I found a solution based on Graeme's comment on grails-data-mapping #880.

    I implemented a custom SchemaManagementTool and added it to the application configuration:

    hibernate.schema_management_tool = CustomSchemaManagementTool
    

    The Hibernate SchemaManagementTool ultimately delegates the raw SQL commands to a GenerationTarget (typically GenerationTargetToDatabase), so our goal is to provide our own GenerationTarget.

    This would be easiest if we could override HibernateSchemaManagementTool.buildGenerationTargets, but this is unfortunately not exposed. Instead, we need to develop our own SchemaCreator and SchemaDropper and return them in the CustomSchemaManagementTool:

    class CustomSchemaManagementTool extends HibernateSchemaManagementTool {
        @Override
        SchemaCreator getSchemaCreator(Map options) {
            return new CustomSchemaCreator(this, getSchemaFilterProvider(options).getCreateFilter())
        }
    
        @Override
        SchemaDropper getSchemaDropper(Map options) {
            return new CustomSchemaDropper(this, getSchemaFilterProvider(options).getDropFilter())
        }
    
        // We unfortunately copy this private method from HibernateSchemaManagementTool
        private SchemaFilterProvider getSchemaFilterProvider(Map options) {
            final Object configuredOption = (options == null) ? null : options.get(AvailableSettings.HBM2DDL_FILTER_PROVIDER)
            return serviceRegistry.getService(StrategySelector.class).resolveDefaultableStrategy(
                SchemaFilterProvider.class,
                configuredOption,
                DefaultSchemaFilterProvider.INSTANCE
            )
        }
    }
    

    For the SchemaCreator and SchemaDropper implementation, we can override doCreation and doDrop respectively. These are essentially copied from the Hibernate implementations, but with a CustomGenerationTarget instead of GenerationTargetToDatabase:

    class CustomSchemaCreator extends SchemaCreatorImpl {
        private final HibernateSchemaManagementTool tool
    
        CustomSchemaCreator(HibernateSchemaManagementTool tool, SchemaFilter schemaFilter) {
            super(tool, schemaFilter)
            this.tool = tool
        }
    
        @Override
        void doCreation(Metadata metadata, ExecutionOptions options, SourceDescriptor sourceDescriptor, TargetDescriptor targetDescriptor) {
            final JdbcContext jdbcContext = tool.resolveJdbcContext( options.getConfigurationValues() )
            final GenerationTarget[] targets = new GenerationTarget[ targetDescriptor.getTargetTypes().size() ]
            targets[0] = new CustomGenerationTarget(tool.getDdlTransactionIsolator(jdbcContext), true)
            super.doCreation(metadata, jdbcContext.getDialect(), options, sourceDescriptor, targets)
        }
    }
    
    class CustomSchemaDropper extends SchemaDropperImpl {
        private final HibernateSchemaManagementTool tool
    
        CustomSchemaDropper(HibernateSchemaManagementTool tool, SchemaFilter schemaFilter) {
            super(tool, schemaFilter)
            this.tool = tool
        }
    
        @Override
        void doDrop(Metadata metadata, ExecutionOptions options, SourceDescriptor sourceDescriptor, TargetDescriptor targetDescriptor) {
            final JdbcContext jdbcContext = tool.resolveJdbcContext( options.getConfigurationValues() )
            final GenerationTarget[] targets = new GenerationTarget[ targetDescriptor.getTargetTypes().size() ]
            targets[0] = new CustomGenerationTarget(tool.getDdlTransactionIsolator(jdbcContext), true)
            super.doDrop(metadata, options, jdbcContext.getDialect(), sourceDescriptor, targets)
        }
    }
    

    In this case I use the same CustomGenerationTarget for both create and drop, but you could easily split this up into different classes. Now we finally get the payoff by extending GenerationTargetToDatabase and overriding the accept method. By only calling super.accept on SQL statements to keep, you can filter out undesired DDL statements.

    class CustomGenerationTarget extends GenerationTargetToDatabase {
        CustomGenerationTarget(DdlTransactionIsolator ddlTransactionIsolator, boolean releaseAfterUse) {
            super(ddlTransactionIsolator, releaseAfterUse)
        }
    
        @Override
        void accept(String command) {
            if (shouldAccept(command))
                super.accept(command)
        }
    
        boolean shouldAccept(String command) {
            // Custom filtering logic here, e.g.:
            if (command =~ /references legacy\.xyz/)
                return false
            return true
        }
    }
    

    It's not the most elegant solution, but you can get the job done.

    I also tried providing my own SchemaFilterProvider (and a custom SchemaFilter). This unfortunately only allows filtering of tables/namespaces - not foreign keys.

提交回复
热议问题