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 中的組態通常分為 2 個領域:建置組態和執行時期組態。
建置組態通常透過 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
這個區分是有必要的,因為在 |
對於 Groovy 組態,下列變數可供組態指令碼使用
變數 | 說明 |
---|---|
userHome |
正在執行 Grails 應用程式的帳戶的家目錄位置。 |
grailsHome |
你安裝 Grails 的目錄位置。如果設定了 |
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
方法(如上所示)對於有效率地擷取組態屬性很有用,同時指定屬性類型(預設類型為字串)和/或提供預設的備用值。
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` config 物件相同的屬性和用法。以下是前一個範例中的服務類別,它使用 `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 值註解
你可以使用 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 格式 Config 的選項
`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 是 SpringBoot,因此組態選項也可用,有關文件,請參閱:https://spring-docs.dev.org.tw/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 簡化了記錄器名稱。以下範例說明了變更
BookController.groovy
位於 grails-app/controllers/com/company
,未註解 @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
...
可以在 application.groovy
中使用 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 檔案形式提供。如果 Maven 儲存庫中有 JAR 檔案,最好使用相依性解析來解析 JAR 檔案,例如您可以像這樣新增 MySQL 驅動程式的相依性
dependencies {
runtimeOnly 'mysql:mysql-connector-java:5.1.29'
}
解析 JAR 檔案後,您需要熟悉 Grails 管理其資料庫組態的方式。組態可以保留在 grails-app/conf/application.groovy
或 grails-app/conf/application.yml
中。這些檔案包含資料來源定義,其中包括下列設定
-
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
,會將 DataSource 設為唯讀,這會導致連線池在每個Connection
上呼叫setReadOnly(true)
-
transactional
- 如果為false
,會將 DataSource 的 transactionManager bean 留在鏈結的 BE1PC transaction manager 實作之外。這只適用於其他資料來源。 -
persistenceInterceptor
- 預設資料來源會自動連接到持續性攔截器,其他資料來源不會自動連接,除非將此設定為true
-
properties
- 要設定在 DataSource bean 上的額外屬性。請參閱 Tomcat Pool 文件。還有一個 Javadoc 格式的 屬性文件。 -
jmxExport
- 如果為false
,會停用所有 DataSources 的 JMX MBeans 註冊。預設會為屬性中jmxEnabled = true
的 DataSources 加入 JMX MBeans。 -
type
- 如果您希望在有多個可用時強制 Grails 使用它,則為連線池類別。
application.groovy
中 MySQL 的典型組態可能是像這樣
dataSource {
pooled = true
dbCreate = "update"
url = "jdbc:mysql://127.0.0.1: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
}
}
在組態 DataSource 時,請勿在任何組態設定之前包含 type 或 def 關鍵字,因為 Groovy 會將這些視為局部變數定義,而且不會處理它們。例如,下列無效 |
dataSource {
boolean pooled = true // type declaration results in ignored local variable
...
}
使用額外屬性進行進階組態的範例
dataSource {
pooled = true
dbCreate = "update"
url = "jdbc:mysql://127.0.0.1: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.dev.org.tw/tomcat-7.0-doc/jdbc-pool.html#Common_Attributes
// https://tomcat.dev.org.tw/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.dev.org.tw/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://mysqldev.dev.org.tw/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 相同,但在應用程式正常關閉時也會捨棄表格。
-
更新 - 建立遺失的表格和索引,並更新目前的架構,而不會刪除任何表格或資料。請注意,這無法適當地處理許多架構變更,例如欄位重新命名(您會保留包含現有資料的舊欄位)。
-
驗證 - 不會對您的資料庫進行任何變更。將設定檔與現有的資料庫架構進行比較,並報告警告。
-
任何其他值 - 不執行任何動作
4.4.1 資料來源和環境
前一個範例設定假設您想要對所有環境使用相同的設定:生產、測試、開發等。
不過,Grails 的 DataSource 定義是「環境感知」的,因此您可以執行
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 類別自動產生資料庫表格。選項說明在 DataSource 區段中
-
建立
-
建立並刪除
-
更新
-
驗證
-
無值
在 開發 模式中,dbCreate
預設設為「建立並刪除」,但在開發的某個時間點(絕對是在您進入生產環境時),您需要停止每次啟動伺服器時刪除並重新建立資料庫。
您可能會想切換到 更新
,以便保留現有資料,並只在您的程式碼變更時更新架構,但 Hibernate 的更新支援非常保守。它不會進行任何可能導致資料遺失的變更,也不會偵測重新命名的欄位或表格,因此您會保留舊的欄位或表格,同時也會擁有新的欄位或表格。
Grails 透過外掛程式支援使用 Liquibase 或 Flyway 進行遷移。
4.4.3 具有交易感知功能的 DataSource 代理程式
實際的 dataSource
bean 會封裝在具有交易感知功能的代理程式中,因此您將獲得目前交易或 Hibernate Session
使用的連線(如果有一個交易或 Hibernate Session
處於活動狀態)。
如果不是這樣,從 dataSource
擷取連線將會是一個新的連線,而且您將無法看到尚未提交的變更(假設您有明智的交易隔離設定,例如 READ_COMMITTED
或更佳)。
4.4.4 資料庫主控台
H2 資料庫主控台是 H2 的一項便利功能,它提供了一個基於 Web 的介面,供您使用任何有 JDBC 驅動程式的資料庫,而且對於檢視您正在開發的資料庫非常有用。當針對記憶體中資料庫執行時,它特別有用。
您可以透過在瀏覽器中導覽至 https://127.0.0.1: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://127.0.0.1/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
和一個或多個其他 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
與服務相同的網域類別進行變更。
請注意,服務中指定的資料來源與網域類別使用的資料來源無關;這是由網域類別本身中宣告的資料來源決定的。它用於宣告要使用哪個交易管理員。
如果您在 dataSource1
中有一個 Foo
領域類別,在 dataSource2
中有一個 Bar
領域類別,如果 WahooService
使用 dataSource1
,一個用於儲存新的 Foo
和新的 Bar
的服務方法只會對 Foo
進行交易,因為它們共用相同的資料來源。交易不會影響 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 交易管理員的交易管理員鏈。
您可以使用此組態選項將交易管理員 Bean 排除在 BE1PC 實作之外
grails:
transaction:
chainedTransactionManagerPostProcessor:
enabled: true
blacklistPattern: '.*'
排除比對是針對交易管理員 bean 的名稱進行。具有 transactional = false
或 readOnly = true
的資料庫交易管理員將會略過,而且在這種情況下不需要使用此組態選項。
XA 和兩階段提交
如果最佳嘗試 1PC 模式不適合用於處理跨多個交易資源(不只資料庫)的交易,則有幾個選項可供使用,以將 XA/2PC 支援新增到 Grails 應用程式。
Spring 交易文件 包含有關整合不同應用程式伺服器的 JTA/XA 交易管理員的資訊。在這種情況下,您可以在 resources.groovy
或 resources.xml
檔案中手動設定名稱為 transactionManager
的 bean。
4.5 版本控制
在執行階段偵測版本
您可以使用 GrailsApplication 類別,透過 Grails 對應用程式元資料的支援來偵測應用程式版本。例如,在 控制器 中有一個隱含的 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 使用者指南 以取得更多資訊。