Play 2.1 - Evolution插件使用指南

    首先你須要明白Evolution的做用是什麼?它可讓你經過幾個腳本文件,輕鬆完成數據庫的管理工做。你只負責編寫腳本,腳本和數據庫之間的同步工做,Evolution幫你搞定。 java

1、如何開啓Evolution插件? sql

    play默認是啓用Evolution插件的,若是想禁用Evolution插件,在conf/application.conf中添加配置項evolutionplugin=disabled,或者設置經過設置系統屬性的方式-Devolutionplugin=disabled禁用Evolution插件至關於切斷了play與數據庫間的同步手段,實體類的任意變更都不會影響到數據庫的表結構,這在項目發佈時很是有用。 數據庫

2、Evolution腳本存放位置 瀏覽器

    Evolution腳本在項目中的路徑爲conf/evolutions/{database name},例如對於默認的default數據庫,路徑爲conf/evolutions/default。Evolution腳本能夠有不少個,腳本名爲連續的數字,從1開始。例如,第1個腳本1.sql,第2個腳本2.sql,如此類推...。 服務器

3、Evolution腳本格式 app

    Evolution腳本包含三個部分:註釋,up腳本和down腳本。 測試

1. 註釋 ui

在標記# --- !Ups以上的都是註釋部分,標記中的---不是必須的,只要包含#!Ups就能夠了,即標記模式要知足^#.*!Ups.*$,一樣地,標記# --- !Downs也是相似的。註釋部分沒有格式限制,能夠隨意書寫。 編碼

2. up腳本 spa

在標記# --- !Ups# --- !Downs之間的部分是up腳本,up腳本是一段用來初始化或更新數據庫的sql腳本,每一條sql語句必須以分號;結尾,若是sql語句中含有分號,須要使用;;進行轉義。註釋方法遵循標準sql,單行註釋使用--,多行註釋使用/* ... */。

3. down腳本

標記# --- !Downs以後的部分是down腳本,down腳本是一段撤銷腳本,相似於數據庫中的事務回滾,將數據庫恢復到up腳本執行以前的狀態。書寫規則同up腳本。

4、Evolution配置表PLAY_EVOLUTIONS

    Evolution插件使用PLAY_EVOLUTIONS管理同步腳本。在項目第一次啓動時,Evolution插件會在數據庫中建立PLAY_EVOLUTIONS表,比較惋惜的是,Evolution插件並無根據不一樣的數據庫類型生成不一樣的建表語句,而是硬編碼了下面的建表語句:

create table play_evolutions (
    id int not null primary key, 
    hash varchar(255) not null, 
    applied_at timestamp not null, 
    apply_script text, 
    revert_script text, 
    state varchar(255), 
    last_problem text
)


    若是鏈接的是Oracle數據庫,這條語句執行會報錯,由於Oracle不認識text類型。下文會講到如何針對Oracle手工修改建表語句。


PLAY_EVOLUTIONS表包含7個字段,解釋以下:

    -    id: 惟一對應一個腳本文件名,也成爲revision,值從1開始

    -    hash:apply_scriptrevert_script兩個字段內容拼接後的sha1哈希值,用來檢測腳本內容是否發生變化

    -    applied_at:記錄up或down腳本執行時間

    -    apply_script:存放腳本文件中的up腳本

    -    revert_script:存放腳本文件中的up腳本

    -    state:保存當前的執行狀態,值能夠爲:applied/applying_up/applying_down

    -    last_problem: 存放腳本執行時錯誤信息

每一個數據庫的Evolution腳本文件數和相應PLAY_EVOLUTIONS表中記錄條數相同,而且是一一對應關係,對應關係爲文件名和id相同。

5、Evolution插件執行過程分析

針對conf/application.conf配置的每一個數據源依次執行:

1. 在conf/evolutions/{database name}目錄下,依次尋找1.sql,2.sql,...,只至發現某個文件不存在爲止,例如目錄下有:0.sql,1.sql,2.sql,4.sql,則最終只會找到1.sql, 2.sql兩個文件,最後按文件名降序排列獲得一個列表;

2. 查詢PLAY_EVOLUTIONS中全部記錄,按id降序排列獲得一個列表;

3. 比較前兩步獲得的兩個列表:

    1)若是有腳本文件在數據庫中不存在,則向PLAY_EVOLUTIONS插入一條記錄,並執行該腳本文件的up腳本;

    2)若是PLAY_EVOLUTIONS表中有記錄,可是該腳本文件卻不存在,則執行該條記錄的down腳本,而且刪除該條記錄

    3)若是腳本文件存在,而且PLAY_EVOLUTIONS表中也有相應記錄,則比較腳本文件的sha1(up腳本+down腳本)與表中記錄的hash值是否相等,若是相等,則不作任何處理;若是不等,則先執行表中記錄的down腳本,刪除該條記錄,從新插入一條與腳本文件對應的新記錄,執行up腳本。考慮到一個應用可能在多臺服務器上同時部署,在執行up/down腳本時,會先將表中相應記錄的state改成applying_up/applying_down狀態,若是執行出錯,則更新last_problem字段,存入錯誤描述,狀態保持不變,若是執行成功,則將狀態更新成applied。

6、常見問題解決方法    

1. 瀏覽器總是提示"Database xxx needs evolution!", 則在conf/application.conf中添加配置applyEvolutions.{database name}=true,便可解決。

2. up/down腳本執行出錯後,啓動項目瀏覽器老是提示"Database xxx is in inconsistent state!", 若是有腳本執行失敗,則Evolution插件不會再嘗試執行出錯的腳本,而是直接在瀏覽器中報錯,此時的解決辦法是手工在數據庫中執行出錯腳本,而後再單擊頁面上的"Mark it resolved"按鈕。

3. Ebean每次都會從新生成1.sql文件,如何手工修改1.sql,而不是用Ebean的自動生成腳本?

    刪除1.sql文件的頭兩行註釋:

   

7、不一樣運行模式下的差別

1. 測試模式下(Mode.Test),無視配置參數,任意的Evolution操做都會被直接執行。

2. 開發模式下(Mode.Dev),若是配置了applyEvolutions.{database name}=true,則自動執行本次Evolution操做,不然會在瀏覽器中提示"Database xxx needs evolution!"

3. 產品模式下(Mode.Prod)狀況比較複雜,根據配置參數分三種狀況:

    1)若是本次Evolution操做不涉及down腳本,而且配置了applyEvolutions.{database name}=true,則自動執行本次Evolution操做;

    2)若是本次Evolution操做涉及down腳本,而且配置了applyEvolutions.{database name}=trueapplyDownEvolutions.{database name}=true自動執行本次Evolution操做;

    3)若是本次Evolution操做涉及down腳本,而且沒有同時配置applyEvolutions.{database name}=trueapplyDownEvolutions.{database name}=true兩個參數,則直接拋出InvalidDatabaseRevision異常。

8、Evolution with Oracle

    在play第一次鏈接數據庫時,Evolution插件會嘗試建立PLAY_EVOLUTIONS表,上文曾提到過,Evolution插件以硬編碼形式提供的建表語句沒法在Oracle中執行,緣由是Oracle中沒有text類型,因此在將play的數據源切換至Oracle時,咱們須要手工在Oracle上建立PLAY_EVOLUTIONS表,建表語句以下:

create table play_evolutions (
    id number not null primary key, 
    hash varchar2(255) not null,
    applied_at timestamp not null,
    apply_script clob,
    revert_script clob,
    state varchar2(255),
    last_problem clob
)

    這裏會有個問題,apply_script和revert_script存放的是up和down腳本,有時腳本會很大,而不少數據庫都會限制text類型必須小於64kb,就算選擇Oracle的clob類型也必須小於4000kb,較通用的解決辦法是將大的腳本文件分紅幾個較小的腳本文件。

    另外須要注意的是,Oracle中字段名不能超過30個字符,不要使用實體映射的默認表名,例如User/Role,最好使用@Table註解生成另一個名稱:

@Entity
@Table(name="r_user")
public class User extends Model {
    @Id
    public Long id;
    
    @Constraints.Required
    public String name;
    public static Finder<Long,User> find = new Finder<Long,User>(Long.class, User.class); 
}


9、小結

    Evolution插件整體仍是使人比較滿意的,遺憾的是在鏈接Oracle數據源時須要手工干預。但願在之後版本中,Evolution插件可以自動判斷數據庫類型,儘可能減小人爲的手工干預。


10、參考

    - http://www.playframework.com/documentation/2.1.1/Evolutions

相關文章
相關標籤/搜索