# 从 Grails 3.3.x 迁移

# 升级 Grails 版本

你需要修改定义在 gradle.properties 文件里的 Grails 版本

比如 Grails 3 里的是:

...
grailsVersion=3.3.8
...

需要把它修改为当前版本:

...
grailsVersion=4.0.3
...

# 升级 GORM 版本

如果项目中用到 GORM,则还需修改定义在 gradle.properties 文件里的 GORM 版本

文档完善中...

比如 Grails 3 里的是:

...
gormVersion=6.1.10.RELEASE
...

需要把它修改为当前版本:

...
gormVersion=7.0.2
...

# GORM DSL 配置移至 runtime.groovy

GORM DSL 配置项应移到 runtime.groovy 文件里。例如,以下的配置项在application.groovy文件已不再支持,若不迁移则会导致应用中断:

grails.gorm.default.mapping = {
    id generator: 'identity'
}

# Spring 5 和 Spring Boot 2.1

Grails 4 是构建在 Spring 5 和 Spring Boot 2.1 之上,如果您使用了 Spring 特定的功能,请参阅它的迁移指南发布说明

# Hibernate 5.4 和 GORM 7.x

Grails 4.x 支持 Hibernate 5.4 和 GORM 7.x 的最低版本。为了支持 Hibernate 的新版本并简化 GORM 本身,GORM 也做了一些修改,详细可参考 GORM 升级文档

# Spring Boot 2.1 Actuator

若在 Grails 3.3.x 的grails-app/conf/application.yml做了如下配置:

endpoints:
    enabled: false
    jmx:
        enabled: true
        unique-names: true

你需要把它修改成:

spring:
    jmx:
        unique-names: true
management:
    endpoints:
        enabled-by-default: false

因为它与 Grails3.x 版本的 Spring Boot 1.5 有很大的不同,所以详细请查阅 Spring Boot Actuator 的文档。

# Spring Boot Developer Tools 与 Spring Loaded

以前版本的 Grails 使用 Spring Loaded 来做热加载。因为这个项目目前已不再维护,并且不支持 Java 11,所以新版的 Grails 已移除了对它的支持。

作为替代,Grails 4 的项目应在build.gradle脚本里包含 Spring Boot Developer Tools 的依赖:

configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
}

dependencies {
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    ...
}

此外,您还应该在application.yml中为 Spring Developer Tools 配置必要的排除项:

spring:
    devtools:
        restart:
            exclude:
                - grails-app/views/**
                - grails-app/i18n/**
                - grails-app/conf/**

当 views 或 i18n 的内容被更改时,上述配置可防止服务器重新启动。

TIP

你可以安装浏览器插件 Chrome LiveReload extension 和 Spring Developer Tools 一起用,它的作用是当你的应用有所改变时会自动刷新浏览器。

# Spring Boot Gradle 插件的变化

Grails 3 是基于 Spring Boot 1.x,Grails 4 是基于 Spring Boot 2.1。

你的 Grails 3 应用的build.gradle中应该有一下配置:


bootRun {
    addResources = true
    ...
}

但是自 Spring Boot 2.0 起,addResources属性就没有了。作为替代,你需要设置sourceResources属性指向你需要使用的源码集合(通常是sourceSets.main):

bootRun {
    sourceResources sourceSets.main
    ...
}

详细可查阅 Spring Boot Gradle plugin 的文档。

# 为 Grails 插件构建可执行的 Jar 包

Spring Boot 的新 Gradle 插件

任务 bootRepackage 已被替换为 bootJar 和 bootWar 任务,分别用于构建可执行 jar 和 war。这两个任务分别继承了标准的 Gradle jar 或 war 任务,它使您能够访问所有常见的配置选项和行为。

如果在 Grails 3 的 build.gradle 有如下配置:

// 激活表示希望把该插件打包成一个标准的应用
bootRepackage.enabled = false

把它替换成:

// 激活表示希望把该插件打包成一个标准的应用
bootJar.enabled = false

# 升级到 Gradle 5

Grails 3 应用默认使用 Gradle 3.5,而 Grails 4 应用已使用 Gradle 5。

要升级到 Gradle 5,请执行以下命令:

> ./gradlew wrapper --gradle-version 5.0

由于 Gradle 5 的变化,已不再为插件解析传递依赖。如果您的项目中使用具有可传递依赖的插件,则需要将这些依赖显式添加到build.gradle文件中。

如果你对应用的构建做了其它更改,则可能需要额外的迁移操作。请参考 Gradle 的升级文档

# H2 Web 控制台

优雅 Spring Boot 2.1 已原生支持 H2 数据库的 Web 控制台,所以 Grails 中与其等价的部分就拿掉啦,因此访问这个 Web 控制台的路径也不再是原来的/dbconsole,而是/h2-console。具体和查阅Spring Boot 文档的 Using H2's Web Console 部分。

# 升级 Hibernate

如果在 Grails 3 应用中使用了 Hibernate 实现的 GORM,你需要将它升级到 Hibernate 5.4。

Grails 3 中的build.gradle文件:

dependencies {
  ...
  compile "org.grails.plugins:hibernate5"
  compile "org.hibernate:hibernate-core:5.1.5.Final"
}

需要修改为:

dependencies {
  ...
  compile "org.grails.plugins:hibernate5"
  compile "org.hibernate:hibernate-core:5.4.0.Final"
}

# 升级 Geb 到 2.3

Grails 3 默认使用的 Geb1.1.x 是一个 JDK 1.7 的版本,因为 Grails 4 已不再支持 Java 1.7,所以你需要把它升级到 Geb 2.3。

Grails 3 中 的build.gradle是这样:

dependencies {
  testCompile "org.grails.plugins:geb:1.1.2"
  testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
  testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
}

在 Grails 4 中需要修改为:

buildscript {
    repositories {
       ...
    }
    dependencies {
        ...
        classpath "gradle.plugin.com.energizedwork.webdriver-binaries:webdriver-binaries-gradle-plugin:$webdriverBinariesVersion" 
    }
}
...

repositories {
  ...
}

apply plugin:"idea"
...

apply plugin:"com.energizedwork.webdriver-binaries" 


dependencies {
...
    testCompile "org.grails.plugins:geb" 
    testRuntime "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion"  
    testRuntime "org.seleniumhq.selenium:selenium-firefox-driver:$seleniumVersion" 
    testRuntime "org.seleniumhq.selenium:selenium-safari-driver:$seleniumSafariDriverVersion" 

    testCompile "org.seleniumhq.selenium:selenium-remote-driver:$seleniumVersion" 
    testCompile "org.seleniumhq.selenium:selenium-api:$seleniumVersion" 
    testCompile "org.seleniumhq.selenium:selenium-support:$seleniumVersion" 
}

webdriverBinaries {
    chromedriver "$chromeDriverVersion" 
    geckodriver "$geckodriverVersion" 
}

tasks.withType(Test) {
    systemProperty "geb.env", System.getProperty('geb.env')
    systemProperty "geb.build.reportsDir", reporting.file("geb/integrationTest")
    systemProperty "webdriver.chrome.driver", System.getProperty('webdriver.chrome.driver')
    systemProperty "webdriver.gecko.driver", System.getProperty('webdriver.gecko.driver')
}

然后gradle.properties文件中应加入:

gebVersion=2.3
seleniumVersion=3.12.0
webdriverBinariesVersion=1.4
hibernateCoreVersion=5.1.5.Final
chromeDriverVersion=2.44 
geckodriverVersion=0.23.0 
seleniumSafariDriverVersion=3.14.0

最后还需要在路径src/integration-test/resources/GebConfig.groovy建一个 Geb 配置 文件,它的内容如下:

import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.firefox.FirefoxOptions
import org.openqa.selenium.safari.SafariDriver

environments {

    // You need to configure in Safari -> Develop -> Allowed Remote Automation
    safari {
        driver = { new SafariDriver() }
    }

    // run via “./gradlew -Dgeb.env=chrome iT”
    chrome {
        driver = { new ChromeDriver() }
    }

    // run via “./gradlew -Dgeb.env=chromeHeadless iT”
    chromeHeadless {
        driver = {
            ChromeOptions o = new ChromeOptions()
            o.addArguments('headless')
            new ChromeDriver(o)
        }
    }

    // run via “./gradlew -Dgeb.env=firefoxHeadless iT”
    firefoxHeadless {
        driver = {
            FirefoxOptions o = new FirefoxOptions()
            o.addArguments('-headless')
            new FirefoxDriver(o)
        }
    }

    // run via “./gradlew -Dgeb.env=firefox iT”
    firefox {
        driver = { new FirefoxDriver() }
    }
}

# 不推荐的类

下表中的类,在 Grails 3.x 中就已不推荐使用,在 Grails 4 中,他们已被移除:

已移除的类 可替代的类
org.grails.datastore.gorm.validation.constraints.UniqueConstraint org.grails.datastore.gorm.validation.constraints.builtin.UniqueConstraint
grails.util.BuildScope
grails.transaction.GrailsTransactionTemplate grails.gorm.transactions.GrailsTransactionTemplate
org.grails.transaction.transform.RollbackTransform org.grails.datastore.gorm.transactions.transform.RollbackTransform
grails.transaction.NotTransactional grails.gorm.transactions.NotTransactional
grails.transaction.Rollback grails.gorm.transactions.Rollback
grails.transaction.Transactional grails.gorm.transactions.Transactional
org.grails.config.FlatConfig
org.grails.core.metaclass.MetaClassEnhancer Use traits instead.
org.grails.core.util.ClassPropertyFetcher org.grails.datastore.mapping.reflect.ClassPropertyFetcher
org.grails.transaction.transform.TransactionalTransform org.grails.datastore.gorm.transactions.transform.TransactionalTransform
grails.core.ComponentCapableDomainClass
grails.core.GrailsDomainClassProperty Use the org.grails.datastore.mapping.model.MappingContext API instead
org.grails.core.DefaultGrailsDomainClassProperty
org.grails.core.MetaGrailsDomainClassProperty
org.grails.core.support.GrailsDomainConfigurationUtil Use the org.grails.datastore.mapping.model.MappingContext and org.grails.datastore.mapping.model.MappingFactory APIs instead
org.grails.plugins.domain.DomainClassPluginSupport
org.grails.plugins.domain.support.GormApiSupport
org.grails.plugins.domain.support.GrailsDomainClassCleaner Handled by org.grails.datastore.mapping.model.MappingContext now
grails.validation.AbstractConstraint Use org.grails.datastore.gorm.validation.constraints.AbstractConstraint instead
grails.validation.AbstractVetoingConstraint org.grails.datastore.gorm.validation.constraints.AbstractVetoingConstraint
grails.validation.CascadingValidator grails.gorm.validation.CascadingValidator
grails.validation.ConstrainedProperty grails.gorm.validation.ConstrainedProperty
grails.validation.Constraint grails.gorm.validation.Constraint
grails.validation.ConstraintFactory org.grails.datastore.gorm.validation.constraints.factory.ConstraintFactory
grails.validation.VetoingConstraint grails.gorm.validation.VetoingConstraint
grails.validation.ConstraintException
org.grails.validation.BlankConstraint org.grails.datastore.gorm.validation.constraints.BlankConstraint
org.grails.validation.ConstrainedPropertyBuilder org.grails.datastore.gorm.validation.constraints.builder.ConstrainedPropertyBuilder
org.grails.validation.ConstraintDelegate
org.grails.validation.ConstraintsEvaluatorFactoryBean org.grails.datastore.gorm.validation.constraints.eval.ConstraintsEvaluator
org.grails.validation.CreditCardConstraint org.grails.datastore.gorm.validation.constraints.CreditCardConstraint
org.grails.validation.DefaultConstraintEvaluator org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator
org.grails.validation.DomainClassPropertyComparator
org.grails.validation.EmailConstraint org.grails.datastore.gorm.validation.constraints.EmailConstraint
org.grails.validation.GrailsDomainClassValidator grails.gorm.validation.PersistentEntityValidator
org.grails.validation.InListConstraint org.grails.datastore.gorm.validation.constraints.InListConstraint
org.grails.validation.MatchesConstraint org.grails.datastore.gorm.validation.constraints.MatchesConstraint
org.grails.validation.MaxConstraint org.grails.datastore.gorm.validation.constraints.MaxConstraint
org.grails.validation.MaxSizeConstraint org.grails.datastore.gorm.validation.constraints.MaxSizeConstraint
org.grails.validation.MinConstraint org.grails.datastore.gorm.validation.constraints.MinConstraint
org.grails.validation.MinSizeConstraint org.grails.datastore.gorm.validation.constraints.MinSizeConstraint
org.grails.validation.NotEqualConstraint org.grails.datastore.gorm.validation.constraints.NotEqualConstraint
org.grails.validation.NullableConstraint org.grails.datastore.gorm.validation.constraints.NullableConstraint
org.grails.validation.RangeConstraint org.grails.datastore.gorm.validation.constraints.RangeConstraint
org.grails.validation.ScaleConstraint org.grails.datastore.gorm.validation.constraints.ScaleConstraint
org.grails.validation.SizeConstraint org.grails.datastore.gorm.validation.constraints.SizeConstraint
org.grails.validation.UrlConstraint org.grails.datastore.gorm.validation.constraints.UrlConstraint
org.grails.validation.ValidatorConstraint org.grails.datastore.gorm.validation.constraints.ValidatorConstraint
org.grails.validation.routines.DomainValidator Replaced by newer version of commons-validation
org.grails.validation.routines.InetAddressValidator Replaced by newer version of commons-validation
org.grails.validation.routines.RegexValidator Replaced by newer version of commons-validation
org.grails.validation.routines.ResultPair Replaced by newer version of commons-validation
org.grails.validation.routines.UrlValidator Replaced by newer version of commons-validation
grails.web.JSONBuilder groovy.json.StreamingJsonBuilder

# Grails-Java8

对于那些在grails-java8插件上添加了依赖项的人,您只需要简单地删除依赖项。插件中的所有类都已移到各自的项目中。

# 不推荐的 Profiles

Grails 3.x 支持的一些 Profiles 将不再继续维护,因此当它们以速记形式出现时,就不再能创建应用程序。升级现有项目时,必须提供这些配置文件的版本。

  • org.grails.profiles:angularjsorg.grails.profiles:angularjs:1.1.2
  • org.grails.profiles:webpackorg.grails.profiles:webpack:1.1.6
  • org.grails.profiles:react-webpackorg.grails.profiles:react-webpack:1.0.8

# 调度方法

在 Grails 3 中,不需要做额外的配置就可以使用 Spring 的@Scheduled注解。但在 Grails 4 中,你必须要先在 Application 类上添加@EnableScheduling注解来使调度生效。