Error occurred running Grails CLI: startup failed:script14738267015581837265078.groovy: 13: unable to resolve class com.foo.Bar
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`
-
使用 `application.groovy` 进行不依赖于应用程序类的配置
-
使用 `runtime.groovy` 进行依赖于应用程序类的配置
这种分离是必要的,因为 `application.groovy` 中定义的配置值对 Grails CLI 可用,而 Grails CLI 需要能够在编译应用程序之前加载 `application.groovy`。当 CLI 执行这些命令时,`application.groovy` 中对应用程序类的引用将导致异常。 |
对于 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 是工件的类型,例如 controllers
或 services
,而 className
是工件的完全限定名称。
Grails 3.3.x 简化了记录器名称。以下示例说明了这些更改
位于 grails-app/controllers/com/company
的 BookController.groovy
未使用 @Slf4j
进行注释
记录器名称(Grails 3.3.x 或更高版本) |
记录器名称(Grails 3.2.x 或更低版本) |
|
|
位于 grails-app/controllers/com/company
的 BookController.groovy
使用 @Slf4j
进行注释
记录器名称(Grails 3.3.x 或更高版本) |
记录器名称(Grails 3.2.x 或更低版本) |
|
|
位于 grails-app/services/com/company
的 BookService.groovy
未使用 @Slf4j
进行注释
记录器名称(Grails 3.3.x 或更高版本) |
记录器名称(Grails 3.2.x 或更低版本) |
|
|
位于 grails-app/services/com/company
的 BookService.groovy
使用 @Slf4j
进行注释
记录器名称(Grails 3.3.x 或更高版本) |
记录器名称(Grails 3.2.x 或更低版本) |
|
|
位于 src/main/groovy/com/company
的 BookDetail.groovy
使用 @Slf4j
进行注释
记录器名称(Grails 3.3.x 或更高版本) |
记录器名称(Grails 3.2.x 或更低版本) |
|
|
4.1.3.2 从堆栈跟踪日志中屏蔽请求参数
当 Grails 记录堆栈跟踪时,日志消息可能包含当前请求的所有请求参数的名称和值。要屏蔽安全请求参数的值,请在 grails.exceptionresolver.params.exclude
配置属性中指定参数名称
grails:
exceptionresolver:
params:
exclude:
- password
- creditCard
可以通过将 grails.exceptionresolver.logRequestParameters
配置属性设置为 false 来完全关闭请求参数日志记录。当应用程序在 DEVELOPMENT 模式下运行时,默认值为 true,对于所有其他环境,默认值为 false。
grails:
exceptionresolver:
logRequestParameters: false
4.1.3.3 外部配置文件
如果设置了配置属性 logging.config
,则可以指示 Logback
使用外部配置文件。
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 配置选项
例如,要为所有域类启用 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.proxyPort 和 https.proxyUser ,但是用户名和密码使用 http.proxyUser 和 http.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
类可以用来执行与插件相同的功能。您可以通过覆盖适当的方法来覆盖 常规插件钩子,例如 doWithSpring
、doWithApplicationContext
等
class Application extends GrailsAutoConfiguration {
@Override
Closure doWithSpring() {
{->
mySpringBean(MyType)
}
}
...
}
4.3 环境
每个环境的配置
Grails 支持每个环境配置的概念。grails-app/conf
目录中的 application.yml
和 application.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
块指定 DataSource
的 dbCreate
和 url
属性的每个环境设置。
针对不同环境进行打包和运行
Grails 的 命令行 具有在特定环境的上下文中执行任何命令的内置功能。格式为
grails <<environment>> <<command name>>
此外,Grails 还有 3 个预设环境:dev
、prod
和 test
,分别用于 开发
、生产
和 测试
。例如,要为 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.groovy
或 grails-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 - 不对数据库进行任何更改。将配置与现有数据库架构进行比较并报告警告。
-
任何其他值 - 不执行任何操作
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 自动数据库迁移
-
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 文件中列出它们即可。
服务
与域类一样,默认情况下,服务使用默认的 DataSource
和 PlatformTransactionManager
。要将服务配置为使用不同的 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 模式” 相当通用,但在某些情况下可能会失败,开发人员必须意识到这一点。
基本思想是在事务中尽可能晚地延迟提交所有资源,以便唯一可能出错的是基础设施故障(而不是业务处理错误)。依赖“尽力而为 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 = false
或 readOnly = true
的数据源的事务管理器将被跳过,在这种情况下不需要使用此配置选项。
XA 和两阶段提交
当“尽力而为 1PC”模式不适合处理跨多个事务性资源(不仅是数据源)的事务时,有几种选项可用于向 Grails 应用程序添加 XA/2PC 支持。
Spring 事务文档 包含有关集成不同应用服务器的 JTA/XA 事务管理器的信息。在这种情况下,您可以在 resources.groovy
或 resources.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 用户指南。