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

相关文章
相关标签/搜索