(快速参考)

4 配置

版本 6.2.0

4 配置

在一个提倡“约定优于配置”的框架中,我们现在要讨论这个主题,这似乎有些奇怪。使用 Grails 的默认设置,您实际上可以开发一个不需要任何配置的应用程序,正如快速入门所演示的那样,但重要的是要了解在需要时在哪里以及如何覆盖这些约定。用户指南的后面部分将提到可以使用哪些配置设置,但不会说明如何设置它们。假设您至少已经阅读了本章的第一部分!

4.1 基本配置

Grails 中的配置通常分为两个方面:构建配置和运行时配置。

构建配置通常通过 Gradle 和 `build.gradle` 文件完成。默认情况下,运行时配置在 `grails-app/conf/application.yml` 文件中以 YAML 格式指定。

如果您更喜欢使用 Grails 2.0 风格的 Groovy 配置,则可以使用 Groovy 的 ConfigSlurper 语法来指定配置。有两个 Groovy 配置文件可用:`grails-app/conf/application.groovy` 和 `grails-app/conf/runtime.groovy`

  1. 使用 `application.groovy` 进行不依赖于应用程序类的配置

  2. 使用 `runtime.groovy` 进行依赖于应用程序类的配置

这种分离是必要的,因为 `application.groovy` 中定义的配置值对 Grails CLI 可用,而 Grails CLI 需要能够在编译应用程序之前加载 `application.groovy`。当 CLI 执行这些命令时,`application.groovy` 中对应用程序类的引用将导致异常。

Error occurred running Grails CLI:
startup failed:script14738267015581837265078.groovy: 13: unable to resolve class com.foo.Bar

对于 Groovy 配置,以下变量可用于配置脚本

变量 说明

userHome

运行 Grails 应用程序的帐户的主目录位置。

grailsHome

安装 Grails 的目录的位置。如果设置了 `GRAILS_HOME` 环境变量,则使用该变量。

appName

应用程序名称,如 `build.gradle` 中所示。

appVersion

应用程序版本,如 `build.gradle` 中所示。

例如

my.tmp.dir = "${userHome}/.grails/tmp"

使用 GrailsApplication 访问配置

如果要读取运行时配置设置(即在 `application.yml` 中定义的设置),请使用 grailsApplication 对象,该对象在控制器和标签库中作为变量可用。

class MyController {
    def hello() {
        def recipient = grailsApplication.config.getProperty('foo.bar.hello')

        render "Hello ${recipient}"
    }
}

`grailsApplication` 对象的 `config` 属性是 Config 接口的实例,并提供了一些有用的方法来读取应用程序的配置。

特别是,`getProperty` 方法(如上所示)对于有效检索配置属性非常有用,同时可以指定属性类型(默认类型为 String)和/或提供默认回退值。

class MyController {

    def hello(Recipient recipient) {
        //Retrieve Integer property 'foo.bar.max.hellos', otherwise use value of 5
        def max = grailsApplication.config.getProperty('foo.bar.max.hellos', Integer, 5)

        //Retrieve property 'foo.bar.greeting' without specifying type (default is String), otherwise use value "Hello"
        def greeting = grailsApplication.config.getProperty('foo.bar.greeting', "Hello")

        def message = (recipient.receivedHelloCount >= max) ?
          "Sorry, you've been greeted the max number of times" :  "${greeting}, ${recipient}"
        }

        render message
    }
}

请注意,`Config` 实例是基于 Spring 的 PropertySource 概念的合并配置,它从环境、系统属性和本地应用程序配置中读取配置,并将它们合并到一个对象中。

`GrailsApplication` 可以轻松注入到服务和其他 Grails 工件中

import grails.core.*

class MyService {
    GrailsApplication grailsApplication

    String greeting() {
        def recipient = grailsApplication.config.getProperty('foo.bar.hello')
        return "Hello ${recipient}"
    }
}

GrailsConfigurationAware 接口

在运行时动态访问配置可能会对应用程序性能产生轻微影响。另一种方法是实现 GrailsConfigurationAware 接口,该接口提供了一个 `setConfiguration` 方法,该方法在类初始化时接受应用程序配置作为参数。然后,您可以将相关的配置属性分配给类的实例属性,以便以后使用。

`Config` 实例与注入的 `GrailsApplication` 配置对象具有相同的属性和用法。以下是前面示例中的服务类,使用 `GrailsConfigurationAware` 而不是注入 `GrailsApplication`

import grails.core.support.GrailsConfigurationAware

class MyService implements GrailsConfigurationAware {

    String recipient

    String greeting() {
        return "Hello ${recipient}"
    }

    void setConfiguration(Config config) {
        recipient = config.getProperty('foo.bar.hello')
    }

}

Spring @Value 注解

您可以使用 Spring 的 Value 注解来注入配置值。

import org.springframework.beans.factory.annotation.*

class MyController {
    @Value('${foo.bar.hello}')
    String recipient

    def hello() {
        render "Hello ${recipient}"
    }
}
在 Groovy 代码中,您必须在 `Value` 注解的值周围使用单引号,否则它将被解释为 GString 而不是 Spring 表达式。

如您所见,在访问配置设置时,您使用的点符号与定义它们时相同。

4.1.1 YML 格式配置选项

`application.yml` 文件是在 Grails 3.0 中引入的,YAML 现在是配置文件的首选格式。

使用系统属性/命令行参数

假设您正在使用 `JDBC_CONNECTION_STRING` 命令行参数,并且希望在 yml 文件中访问相同的参数,则可以通过以下方式完成

production:
    dataSource:
        url: '${JDBC_CONNECTION_STRING}'

类似地,也可以访问系统参数。

如果使用 `./gradlew bootRun` 启动应用程序,则需要在 `build.gradle` 中进行以下操作来修改 `bootRun` 任务

bootRun {
    systemProperties = System.properties
}

对于测试,以下内容需要更改 `test` 任务,如下所示

test {
    systemProperties = System.properties
}

外部配置

默认情况下,Grails 将从 `./config` 或当前目录读取 `application.(properties|yml)`。由于 Grails 是 Spring Boot,因此也可以使用配置选项,有关文档,请参阅:https://docs.springframework.org.cn/spring-boot/docs/2.7.16/reference/html/features.html#features.external-config.files

4.1.2 内置选项

Grails 有一组值得了解的核心设置。它们的默认值适用于大多数项目,但了解它们的作用非常重要,因为您以后可能需要其中一个或多个设置。

运行时设置

在运行时方面,即 `grails-app/conf/application.yml`,还有很多核心设置

  • `grails.enable.native2ascii` - 如果您不需要对 Grails i18n 属性文件进行 native2ascii 转换,请将其设置为 false(默认值:true)。

  • `grails.views.default.codec` - 设置 GSP 的默认编码方案 - 可以是“none”、“html”或“base64”之一(默认值:“none”)。为了降低 XSS 攻击的风险,请将其设置为“html”。

  • `grails.views.gsp.encoding` - 用于 GSP 源文件的编码(默认值:“utf-8”)。

  • `grails.mime.file.extensions` - 是否使用文件扩展名来决定 内容协商 中的 MIME 类型(默认值:true)。

  • `grails.mime.types` - 用于 内容协商 的受支持 MIME 类型的映射。

  • `grails.serverURL` - 指定绝对链接的服务器 URL 部分的字符串,包括服务器名称,例如 grails.serverURL="http://my.yourportal.com"。请参阅 createLink。也由重定向使用。

  • `grails.views.gsp.sitemesh.preprocess` - 确定 SiteMesh 预处理是否发生。禁用此选项会降低页面渲染速度,但如果您需要 SiteMesh 解析 GSP 视图生成的 HTML,则禁用它是正确的选择。如果您不了解此高级属性,请不要担心:将其设置为 true。

  • `grails.reload.excludes` 和 `grails.reload.includes` - 配置这些指令将确定项目特定源文件的重新加载行为。每个指令都接受一个字符串列表,这些字符串是在使用 `bootRun` 任务在开发环境中运行应用程序时应从重新加载行为中排除的项目源文件的类名,或者应包含在重新加载行为中的类名。如果配置了 `grails.reload.includes` 指令,则只会重新加载该列表中的类。

4.1.3 日志记录

从 Grails 3.0 开始,日志记录由 Logback 日志记录框架 处理,可以使用 `grails-app/conf/logback.xml` 文件进行配置。

自 Grails 5.1.2 起,已移除对 Groovy 配置(grails-app/conf/logback.groovy)的支持(由 logback 1.2.9 移除)。可以通过将 logback-groovy-config 库添加到您的项目中来重新添加 Groovy 配置。

有关配置日志记录的更多信息,请参阅有关该主题的 Logback 文档

4.1.3.1 记录器名称

Grails 工件(控制器、服务等)会自动注入一个 log 属性。

在 Grails 3.3.0 之前,Grails 工件的记录器名称遵循约定 grails.app.<type>.<className>,其中 type 是工件的类型,例如 controllersservices,而 className 是工件的完全限定名称。

Grails 3.3.x 简化了记录器名称。以下示例说明了这些更改

位于 grails-app/controllers/com/companyBookController.groovy 未使用 @Slf4j 进行注释

记录器名称(Grails 3.3.x 或更高版本)

记录器名称(Grails 3.2.x 或更低版本)

com.company.BookController

grails.app.controllers.com.company.BookController

位于 grails-app/controllers/com/companyBookController.groovy 使用 @Slf4j 进行注释

记录器名称(Grails 3.3.x 或更高版本)

记录器名称(Grails 3.2.x 或更低版本)

com.company.BookController

com.company.BookController

位于 grails-app/services/com/companyBookService.groovy 未使用 @Slf4j 进行注释

记录器名称(Grails 3.3.x 或更高版本)

记录器名称(Grails 3.2.x 或更低版本)

com.company.BookService

grails.app.services.com.company.BookService

位于 grails-app/services/com/companyBookService.groovy 使用 @Slf4j 进行注释

记录器名称(Grails 3.3.x 或更高版本)

记录器名称(Grails 3.2.x 或更低版本)

com.company.BookService

com.company.BookService

位于 src/main/groovy/com/companyBookDetail.groovy 使用 @Slf4j 进行注释

记录器名称(Grails 3.3.x 或更高版本)

记录器名称(Grails 3.2.x 或更低版本)

com.company.BookDetail

com.company.BookDetail

4.1.3.2 从堆栈跟踪日志中屏蔽请求参数

当 Grails 记录堆栈跟踪时,日志消息可能包含当前请求的所有请求参数的名称和值。要屏蔽安全请求参数的值,请在 grails.exceptionresolver.params.exclude 配置属性中指定参数名称

grails-app/conf/application.yml
grails:
    exceptionresolver:
        params:
            exclude:
                - password
                - creditCard

可以通过将 grails.exceptionresolver.logRequestParameters 配置属性设置为 false 来完全关闭请求参数日志记录。当应用程序在 DEVELOPMENT 模式下运行时,默认值为 true,对于所有其他环境,默认值为 false。

grails-app/conf/application.yml
grails:
    exceptionresolver:
        logRequestParameters: false

4.1.3.3 外部配置文件

如果设置了配置属性 logging.config,则可以指示 Logback 使用外部配置文件。

grails-app/conf/application.yml
logging:
    config: /Users/me/config/logback.groovy

或者,可以使用系统属性提供配置文件位置

$ ./gradlew -Dlogging.config=/Users/me/config/logback.groovy bootRun

或者,可以使用环境变量

$ export LOGGING_CONFIG=/Users/me/config/logback.groovy
$ ./gradlew bootRun

4.1.4 GORM

Grails 提供以下 GORM 配置选项

  • grails.gorm.failOnError - 如果设置为 true,则如果在保存期间 验证 失败,则会导致域类上的 save() 方法抛出 grails.validation.ValidationException。此选项也可以分配一个表示包名称的字符串列表。如果该值为字符串列表,则 failOnError 行为将仅应用于这些包(包括子包)中的域类。有关更多信息,请参阅 save 方法文档。

例如,要为所有域类启用 failOnError

grails:
    gorm:
        failOnError: true

并按包为域类启用 failOnError

grails:
    gorm:
        failOnError:
            - com.companyname.somepackage
            - com.companyname.someotherpackage

4.1.5 配置 HTTP 代理

要将 Grails 设置为使用 HTTP 代理,需要执行两个步骤。首先,如果您希望使用 grails CLI 创建应用程序等,则需要将其配置为识别代理。这可以使用 GRAILS_OPTS 环境变量来完成,例如在 Unix 系统上

export GRAILS_OPTS="-Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=3128 -Dhttp.proxyUser=test -Dhttp.proxyPassword=test"
默认配置文件存储库是通过 HTTPS 解析的,因此使用 https.proxyPorthttps.proxyUser,但是用户名和密码使用 http.proxyUserhttp.proxyPassword 指定

对于 Windows 系统,可以在“我的电脑/高级/环境变量”下配置环境变量。

完成此配置后,grails 命令可以通过代理连接和身份验证。

其次,由于 Grails 使用 Gradle 作为构建系统,因此您需要将 Gradle 配置为通过代理进行身份验证。有关如何执行此操作的说明,请参阅 Gradle 用户指南中有关该主题的部分

4.2 应用程序类

每个新的 Grails 应用程序在 grails-app/init 目录中都有一个 Application 类。

Application 类是 GrailsAutoConfiguration 类的子类,并具有 static void main 方法,这意味着它可以作为常规应用程序运行。

4.2.1 执行应用程序类

有几种方法可以执行 Application 类,如果您使用的是 IDE,则只需右键单击该类并直接从 IDE 运行它,即可启动 Grails 应用程序。

这对于调试也很有用,因为您可以直接从 IDE 进行调试,而无需在从命令行使用 ./gradlew bootRun --debug-jvm 命令时连接远程调试器。

您还可以将应用程序打包到可运行的 WAR 文件中,例如

$ ./gradlew bootWar
$ java -jar build/libs/myapp-0.1.war

如果您计划使用无容器方法部署应用程序,这将非常有用。

4.2.2 自定义应用程序类

您可以通过多种方式自定义 Application 类。

自定义扫描

默认情况下,Grails 会扫描所有已知的源目录以查找控制器、域类等,但是,如果要扫描其他 JAR 文件中的包,可以通过覆盖 Application 类的 packageNames() 方法来实现

class Application extends GrailsAutoConfiguration {
    @Override
    Collection<String> packageNames() {
        super.packageNames() + ['my.additional.package']
    }

    ...
}

注册其他 Bean

Application 类也可以用作 Spring bean 定义的来源,只需定义一个使用 Bean 注释的方法,返回的对象将成为 Spring bean。该方法的名称用作 bean 名称

class Application extends GrailsAutoConfiguration {
    @Bean
    MyType myBean() {
        return new MyType()
    }

    ...
}

4.2.3 应用程序生命周期

Application 类还实现了 GrailsApplicationLifeCycle 接口,所有插件都实现了该接口。

这意味着 Application 类可以用来执行与插件相同的功能。您可以通过覆盖适当的方法来覆盖 常规插件钩子,例如 doWithSpringdoWithApplicationContext

class Application extends GrailsAutoConfiguration {
    @Override
    Closure doWithSpring() {
        {->
            mySpringBean(MyType)
        }
    }

    ...
}

4.3 环境

每个环境的配置

Grails 支持每个环境配置的概念。grails-app/conf 目录中的 application.ymlapplication.groovy 文件可以使用 YAML 或 ConfigSlurper 提供的语法来使用每个环境配置。例如,考虑 Grails 提供的以下默认 application.yml 定义

environments:
    development:
        dataSource:
            dbCreate: create-drop
            url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    test:
        dataSource:
            dbCreate: update
            url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    production:
        dataSource:
            dbCreate: update
            url: jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
            properties:
               jmxEnabled: true
               initialSize: 5
        ...

以上内容可以使用 Groovy 语法在 application.groovy 中表示如下

dataSource {
    pooled = false
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
}
environments {
    development {
        dataSource {
            dbCreate = "create-drop"
            url = "jdbc:h2:mem:devDb"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:prodDb"
            properties {
               jmxEnabled = true
               initialSize = 5
            }
        }
    }
}

请注意,如何在顶层提供通用配置,然后 environments 块指定 DataSourcedbCreateurl 属性的每个环境设置。

针对不同环境进行打包和运行

Grails 的 命令行 具有在特定环境的上下文中执行任何命令的内置功能。格式为

grails <<environment>> <<command name>>

此外,Grails 还有 3 个预设环境:devprodtest,分别用于 开发生产测试。例如,要为 test 环境创建 WAR,您可以运行

grails test war

要针对其他环境,可以将 grails.env 变量传递给任何命令

./gradlew bootRun -Dgrails.env=UAT

以编程方式检测环境

在您的代码中,例如在 Gant 脚本或引导类中,可以使用 Environment 类检测环境

import grails.util.Environment

...

switch (Environment.current) {
    case Environment.DEVELOPMENT:
        configureForDevelopment()
        break
    case Environment.PRODUCTION:
        configureForProduction()
        break
}

每个环境的引导

通常希望在应用程序启动时针对每个环境运行代码。为此,您可以使用 grails-app/init/BootStrap.groovy 文件对每个环境执行的支持

def init = { ServletContext ctx ->
    environments {
        production {
            ctx.setAttribute("env", "prod")
        }
        development {
            ctx.setAttribute("env", "dev")
        }
    }
    ctx.setAttribute("foo", "bar")
}

通用的每个环境执行

前面的 BootStrap 示例在内部使用 grails.util.Environment 类来执行。您也可以自己使用此类来执行特定于环境的逻辑

Environment.executeForCurrentEnvironment {
    production {
        // do something in production
    }
    development {
        // do something only in development
    }
}

4.4 数据源

由于 Grails 构建在 Java 技术之上,因此设置数据源需要一些 JDBC(代表 Java 数据库连接性的技术)的知识。

如果您使用 H2 以外的数据库,则需要 JDBC 驱动程序。例如,对于 MySQL,您需要 Connector/J

驱动程序通常以 JAR 存档的形式提供。如果 JAR 在 Maven 存储库中可用,则最好使用依赖项解析来解析它,例如,您可以像这样添加 MySQL 驱动程序的依赖项

dependencies {
    runtimeOnly 'mysql:mysql-connector-java:5.1.29'
}

解析 JAR 后,您需要熟悉 Grails 如何管理其数据库配置。可以在 grails-app/conf/application.groovygrails-app/conf/application.yml 中维护配置。这些文件包含 dataSource 定义,其中包括以下设置

  • driverClassName - JDBC 驱动程序的类名

  • username - 用于建立 JDBC 连接的用户名

  • password - 用于建立 JDBC 连接的密码

  • url - 数据库的 JDBC URL

  • dbCreate - 是否根据域模型自动生成数据库 - 可选值为 'create-drop', 'create', 'update', 'validate', 或 'none'

  • pooled - 是否使用连接池(默认为 true)

  • logSql - 启用 SQL 日志记录到标准输出

  • formatSql - 格式化记录的 SQL

  • dialect - 一个表示 Hibernate 方言的字符串或类,用于与数据库通信。有关可用的方言,请参阅 org.hibernate.dialect 包。

  • readOnly - 如果为 true,则使数据源只读,这将导致连接池在每个 Connection 上调用 setReadOnly(true)

  • transactional - 如果为 false,则将数据源的 transactionManager bean 保留在链式 BE1PC 事务管理器实现之外。这仅适用于其他数据源。

  • persistenceInterceptor - 默认数据源自动连接到持久化拦截器,其他数据源不会自动连接,除非将其设置为 true

  • properties - 要在数据源 bean 上设置的额外属性。有关更多信息,请参阅 Tomcat 连接池 文档。还有一个 Javadoc 格式的 属性文档

  • jmxExport - 如果为 false,将禁用所有数据源的 JMX MBean 注册。默认情况下,会为属性中 jmxEnabled = true 的数据源添加 JMX MBean。

  • type - 如果您想在有多个连接池可用时强制 Grails 使用它,则可以使用该连接池类。

application.groovy 中,MySQL 的典型配置可能如下所示

dataSource {
    pooled = true
    dbCreate = "update"
    url = "jdbc:mysql://localhost:3306/my_database"
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    username = "username"
    password = "password"
    type = "com.zaxxer.hikari.HikariDataSource"
    properties {
       jmxEnabled = true
       initialSize = 5
       maxActive = 50
       minIdle = 5
       maxIdle = 25
       maxWait = 10000
       maxAge = 10 * 60000
       timeBetweenEvictionRunsMillis = 5000
       minEvictableIdleTimeMillis = 60000
       validationQuery = "SELECT 1"
       validationQueryTimeout = 3
       validationInterval = 15000
       testOnBorrow = true
       testWhileIdle = true
       testOnReturn = false
       jdbcInterceptors = "ConnectionState;StatementCache(max=200)"
       defaultTransactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED
    }
}
配置数据源时,请勿在任何配置设置之前包含类型或 def 关键字,因为 Groovy 会将它们视为局部变量定义,并且不会处理它们。例如,以下内容无效
dataSource {
    boolean pooled = true // type declaration results in ignored local variable
    ...
}

使用额外属性进行高级配置的示例

dataSource {
    pooled = true
    dbCreate = "update"
    url = "jdbc:mysql://localhost:3306/my_database"
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    username = "username"
    password = "password"
    type = "com.zaxxer.hikari.HikariDataSource"
    properties {
       // Documentation for Tomcat JDBC Pool
       // https://tomcat.net.cn/tomcat-7.0-doc/jdbc-pool.html#Common_Attributes
       // https://tomcat.net.cn/tomcat-7.0-doc/api/org/apache/tomcat/jdbc/pool/PoolConfiguration.html
       jmxEnabled = true
       initialSize = 5
       maxActive = 50
       minIdle = 5
       maxIdle = 25
       maxWait = 10000
       maxAge = 10 * 60000
       timeBetweenEvictionRunsMillis = 5000
       minEvictableIdleTimeMillis = 60000
       validationQuery = "SELECT 1"
       validationQueryTimeout = 3
       validationInterval = 15000
       testOnBorrow = true
       testWhileIdle = true
       testOnReturn = false
       ignoreExceptionOnPreLoad = true
       // https://tomcat.net.cn/tomcat-7.0-doc/jdbc-pool.html#JDBC_interceptors
       jdbcInterceptors = "ConnectionState;StatementCache(max=200)"
       defaultTransactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED // safe default
       // controls for leaked connections
       abandonWhenPercentageFull = 100 // settings are active only when pool is full
       removeAbandonedTimeout = 120
       removeAbandoned = true
       // use JMX console to change this setting at runtime
       logAbandoned = false // causes stacktrace recording overhead, use only for debugging
       // JDBC driver properties
       // Mysql as example
       dbProperties {
           // Mysql specific driver properties
           // https://dev.mysqlserver.cn/doc/connector-j/en/connector-j-reference-configuration-properties.html
           // let Tomcat JDBC Pool handle reconnecting
           autoReconnect=false
           // truncation behaviour
           jdbcCompliantTruncation=false
           // mysql 0-date conversion
           zeroDateTimeBehavior='convertToNull'
           // Tomcat JDBC Pool's StatementCache is used instead, so disable mysql driver's cache
           cachePrepStmts=false
           cacheCallableStmts=false
           // Tomcat JDBC Pool's StatementFinalizer keeps track
           dontTrackOpenResources=true
           // performance optimization: reduce number of SQLExceptions thrown in mysql driver code
           holdResultsOpenOverStatementClose=true
           // enable MySQL query cache - using server prep stmts will disable query caching
           useServerPrepStmts=false
           // metadata caching
           cacheServerConfiguration=true
           cacheResultSetMetadata=true
           metadataCacheSize=100
           // timeouts for TCP/IP
           connectTimeout=15000
           socketTimeout=120000
           // timer tuning (disable)
           maintainTimeStats=false
           enableQueryTimeouts=false
           // misc tuning
           noDatetimeStringSync=true
       }
    }
}

更多关于 dbCreate

Hibernate 可以自动创建域模型所需的数据库表。您可以通过 dbCreate 属性控制何时以及如何执行此操作,该属性可以采用以下值

  • create - 删除现有模式并在启动时创建模式,首先删除现有表、索引等。

  • create-drop - 与 create 相同,但也会在应用程序正常关闭时删除表。

  • update - 创建缺少的表和索引,并在不删除任何表或数据的情况下更新当前模式。请注意,这不能正确处理许多架构更改,例如列重命名(您将保留包含现有数据的旧列)。

  • validate - 不对数据库进行任何更改。将配置与现有数据库架构进行比较并报告警告。

  • 任何其他值 - 不执行任何操作

一旦您的架构相对稳定,并且当您的应用程序和数据库部署到生产环境中时,建议将 dbCreate 设置设置为 "none"。然后,通过适当的迁移管理数据库更改,可以使用 SQL 脚本或迁移工具(如 FlywayLiquibase)。数据库迁移 插件使用 Liquibase。

4.4.1 数据源和环境

前面的示例配置假定您希望所有环境(生产、测试、开发等)使用相同的配置。

但是,Grails 的数据源定义是“环境感知”的,因此您可以执行以下操作

dataSource {
    pooled = true
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    // other common settings here
}

environments {
    production {
        dataSource {
            url = "jdbc:mysql://liveip.com/liveDb"
            // other environment-specific settings here
        }
    }
}

4.4.2 自动数据库迁移

DataSource 定义的 dbCreate 属性很重要,因为它指示 Grails 在运行时应如何根据 GORM 类自动生成数据库表。选项在 数据源 部分中进行了描述

  • create

  • create-drop

  • update

  • validate

  • 无值

开发 模式下,默认情况下 dbCreate 设置为 "create-drop",但在开发过程中的某个时候(当然,一旦您进入生产环境),您需要停止在每次启动服务器时都删除并重新创建数据库。

切换到 update 似乎很诱人,这样您就可以保留现有数据,并且只在代码更改时更新模式,但 Hibernate 的更新支持非常保守。它不会进行任何可能导致数据丢失的更改,也不会检测重命名的列或表,因此您将保留旧的列或表,并且还会拥有新的列或表。

Grails 通过插件支持使用 Liquibase 或 Flyway 进行迁移。

4.4.3 事务感知数据源代理

实际的 dataSource bean 被包装在事务感知代理中,因此如果您有一个活动的 Hibernate Session,您将获得当前事务或 Hibernate Session 正在使用的连接。

如果不是这种情况,那么从 dataSource 检索连接将是一个新连接,并且您将无法看到尚未提交的更改(假设您有一个合理的交易隔离设置,例如 READ_COMMITTED 或更好)。

4.4.4 数据库控制台

H2 数据库控制台 是 H2 的一项便捷功能,它为任何您拥有 JDBC 驱动程序的数据库提供基于 Web 的界面,并且它对于查看您正在开发的数据库非常有用。当针对内存数据库运行时,它尤其有用。

您可以在浏览器中导航到 http://localhost:8080/h2-console 来访问控制台。有关可用选项的更多信息,请参阅 Spring Boot H2 控制台文档

默认情况下,H2 控制台处于禁用状态(除非您使用的是 Spring Boot 的开发工具),并且必须通过将 spring.h2.console.enabled 属性配置为 true 来启用。
H2 控制台仅供在开发过程中使用,因此应注意确保在生产环境中未将 spring.h2.console.enabled 设置为 true

4.4.5 多数据源

默认情况下,所有域类共享一个 DataSource 和一个数据库,但您可以选择将域类划分到两个或多个数据源中。

配置其他数据源

grails-app/conf/application.yml 中的默认 DataSource 配置如下所示

dataSource:
    pooled: true
    jmxExport: true
    driverClassName: org.h2.Driver
    username: sa
    password:

environments:
    development:
        dataSource:
            dbCreate: create-drop
            url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    test:
        dataSource:
            dbCreate: update
            url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    production:
        dataSource:
            dbCreate: update
            url: jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
            properties:
               jmxEnabled: true
               initialSize: 5

这将配置一个名为 dataSource 的 Spring bean 的单个 DataSource。要配置其他数据源,请添加一个 dataSources 块(在顶层、环境块中或两者中,就像标准 DataSource 定义一样),并使用自定义名称。例如,此配置添加了第二个 DataSource,在开发环境中使用 MySQL,在生产环境中使用 Oracle

dataSource:
    pooled: true
    jmxExport: true
    driverClassName: org.h2.Driver
    username: sa
    password:

dataSources:
    lookup:
        dialect: org.hibernate.dialect.MySQLInnoDBDialect
        driverClassName: com.mysql.jdbc.Driver
        username: lookup
        password: secret
        url: jdbc:mysql://localhost/lookup
        dbCreate: update

environments:
    development:
        dataSource:
            dbCreate: create-drop
            url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    test:
        dataSource:
            dbCreate: update
            url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    production:
        dataSource:
            dbCreate: update
            url: jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
            properties:
                jmxEnabled: true
                initialSize: 5
                ...
        dataSources:
            lookup:
                dialect: org.hibernate.dialect.Oracle10gDialect
                driverClassName: oracle.jdbc.driver.OracleDriver
                username: lookup
                password: secret
                url: jdbc:oracle:thin:@localhost:1521:lookup
                dbCreate: update

您可以使用相同或不同的数据库,只要它们受 Hibernate 支持即可。

如果需要在 Grails 工件中注入 lookup 数据源,可以这样做

DataSource dataSource_lookup
在定义多个数据源时,其中一个必须命名为“dataSource”。这是必需的,因为 Grails 通过确定哪个数据源名为“dataSource”来确定哪个数据源是默认数据源。

配置域类

如果域类没有 DataSource 配置,则默认为标准 'dataSource'。在 mapping 块中设置 datasource 属性以配置非默认 DataSource。例如,如果您希望使用 ZipCode 域来使用 'lookup' DataSource,请将其配置如下

class ZipCode {

   String code

   static mapping = {
      datasource 'lookup'
   }
}

一个域类也可以使用两个或多个数据源。使用带有名称列表的 datasources 属性配置多个数据源,例如

class ZipCode {

   String code

   static mapping = {
      datasources(['lookup', 'auditing'])
   }
}

如果域类使用默认 DataSource 和一个或多个其他数据源,请使用特殊名称 'DEFAULT' 指示默认 DataSource

class ZipCode {

   String code

   static mapping = {
      datasources(['lookup', 'DEFAULT'])
   }
}

如果域类使用所有配置的数据源,请使用特殊值 'ALL'

class ZipCode {

   String code

   static mapping = {
      datasource 'ALL'
   }
}

命名空间和 GORM 方法

如果域类使用多个 DataSource,则可以使用每个 DataSource 名称隐含的命名空间为特定 DataSource 进行 GORM 调用。例如,考虑以下使用两个数据源的类

class ZipCode {

   String code

   static mapping = {
      datasources(['lookup', 'auditing'])
   }
}

未显式使用命名空间时,指定的第一个 DataSource 是默认值,因此在本例中,我们默认为 'lookup'。但是,您可以使用 DataSource 名称在 'auditing' DataSource 上调用 GORM 方法,例如

def zipCode = ZipCode.auditing.get(42)
...
zipCode.auditing.save()

如您所见,您在静态情况下和实例情况下都将 DataSource 添加到方法调用中。

Hibernate 映射的域类

您还可以将带注释的 Java 类划分到不同的数据源中。使用默认数据源的类在 grails-app/conf/hibernate.cfg.xml 中注册。要指定带注释的类使用非默认数据源,请为该数据源创建一个 hibernate.cfg.xml 文件,该文件的文件名以数据源名称作为前缀。

例如,如果 Book 类在默认数据源中,则应在 grails-app/conf/hibernate.cfg.xml 中注册

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          '-//Hibernate/Hibernate Configuration DTD 3.0//EN'
          'http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd'>
<hibernate-configuration>
   <session-factory>
      <mapping class='org.example.Book'/>
   </session-factory>
</hibernate-configuration>

如果 Library 类在“ds2”数据源中,则应在 grails-app/conf/ds2_hibernate.cfg.xml 中注册

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          '-//Hibernate/Hibernate Configuration DTD 3.0//EN'
          'http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd'>
<hibernate-configuration>
   <session-factory>
      <mapping class='org.example.Library'/>
   </session-factory>
</hibernate-configuration>

使用 hbm.xml 文件映射的类的过程相同 - 只需在相应的 hibernate.cfg.xml 文件中列出它们即可。

服务

与域类一样,默认情况下,服务使用默认的 DataSourcePlatformTransactionManager。要将服务配置为使用不同的 DataSource,请使用静态 datasource 属性,例如

class DataService {

   static datasource = 'lookup'

   void someMethod(...) {
      ...
   }
}

事务服务只能使用单个 DataSource,因此请确保仅对 DataSource 与服务相同的域类进行更改。

请注意,服务中指定的 datasource 与域类使用的 datasource 无关;这取决于它们在域类自身中声明的 datasource。它用于声明要使用哪个事务管理器。

如果您在 dataSource1 中有一个 Foo 域类,在 dataSource2 中有一个 Bar 域类,如果 WahooService 使用 dataSource1,则保存新 Foo 和新 Bar 的服务方法将仅对 Foo 是事务性的,因为它们共享同一个 datasource。事务不会影响 Bar 实例。如果您希望两者都是事务性的,则需要使用两个服务和 XA 数据源来进行两阶段提交,例如使用 Atomikos 插件。

跨多个数据源的事务

默认情况下,Grails 不会尝试处理跨越多个数据源的事务。

您可以启用 Grails 使用“尽力而为 1PC”模式来处理跨多个数据源的事务。为此,您必须在 application.yml 中将 grails.transaction.chainedTransactionManagerPostProcessor.enabled 设置为 true

grails:
  transaction:
    chainedTransactionManagerPostProcessor:
      enabled: true

“尽力而为 1PC 模式” 相当通用,但在某些情况下可能会失败,开发人员必须意识到这一点。

这是一种非 XA 模式,涉及对多个资源进行同步的单阶段提交。由于未使用 2PC,因此它永远不会像 XA 事务那样安全,但如果参与者意识到这些折衷方案,通常已经足够好了。

基本思想是在事务中尽可能晚地延迟提交所有资源,以便唯一可能出错的是基础设施故障(而不是业务处理错误)。依赖“尽力而为 1PC”的系统认为,基础设施故障很少见,他们可以承担风险以换取更高的吞吐量。如果业务处理服务也被设计为幂等的,那么在实践中几乎不会出错。

BE1PC 实现已添加到 Grails 2.3.6 中。. 在此更改之前,其他数据源不参与 Grails 中启动的事务。其他数据源中的事务基本上处于自动提交模式。在某些情况下,这可能是想要的行为。一个原因可能是性能:在每个新事务开始时,BE1PC 事务管理器都会为每个数据源创建一个新事务。可以通过在其他数据源的相应配置块中设置 transactional = false,将其他数据源排除在 BE1PC 事务管理器之外。具有 readOnly = true 的数据源也将从链式事务管理器中排除(自 2.3.7 起)。

默认情况下,BE1PC 实现会将所有实现 Spring PlatformTransactionManager 接口的 bean 添加到链式 BE1PC 事务管理器。例如,Grails 应用程序上下文中的一个可能的 JMSTransactionManager bean 将被添加到 Grails BE1PC 事务管理器的链式事务管理器中。

您可以使用以下配置选项从 BE1PC 实现中排除事务管理器 bean

grails:
  transaction:
    chainedTransactionManagerPostProcessor:
      enabled: true
      blacklistPattern: '.*'

排除匹配是在事务管理器 bean 的名称上完成的。transactional = falsereadOnly = true 的数据源的事务管理器将被跳过,在这种情况下不需要使用此配置选项。

XA 和两阶段提交

当“尽力而为 1PC”模式不适合处理跨多个事务性资源(不仅是数据源)的事务时,有几种选项可用于向 Grails 应用程序添加 XA/2PC 支持。

Spring 事务文档 包含有关集成不同应用服务器的 JTA/XA 事务管理器的信息。在这种情况下,您可以在 resources.groovyresources.xml 文件中手动配置一个名为 transactionManager 的 bean。

4.5 版本控制

在运行时检测版本

您可以使用 Grails 对应用程序元数据的支持来检测应用程序版本,方法是使用 GrailsApplication 类。例如,在 控制器 中,有一个隐式的 grailsApplication 变量可以使用

def version = grailsApplication.metadata.getApplicationVersion()

您可以使用以下方法检索正在运行的 Grails 版本

def grailsVersion = grailsApplication.metadata.getGrailsVersion()

GrailsUtil

import grails.util.GrailsUtil
...
def grailsVersion = GrailsUtil.grailsVersion

4.6 依赖项解析

依赖项解析由 Gradle 构建工具 处理,所有依赖项都在 build.gradle 文件中定义。有关更多信息,请参阅 Gradle 用户指南