您的位置:首页 > 其它

Evolution的作用是什么?

2015-10-22 21:00 267 查看
首先你需要明白Evolution的作用是什么?它可以让你通过几个脚本文件,轻松完成数据库的管理工作。你只负责编写脚本,脚本和数据库之间的同步工作,Evolution帮你搞定。





一、如何开启Evolution插件?

play默认是启用Evolution插件的,如果想禁用Evolution插件,在conf/application.conf中添加配置项evolutionplugin=disabled,或者设置通过设置系统属性的方式-Devolutionplugin=disabled。禁用Evolution插件相当于切断了play与数据库间的同步手段,实体类的任意变动都不会影响到数据库的表结构,这在项目发布时非常有用。

二、Evolution脚本存放位置

Evolution脚本在项目中的路径为conf/evolutions/{database name},例如对于默认的default数据库,路径为conf/evolutions/default。Evolution脚本可以有很多个,脚本名为连续的数字,从1开始。例如,第1个脚本1.sql,第2个脚本2.sql,如此类推...。

三、Evolution脚本格式

Evolution脚本包含三个部分:注释,up脚本和down脚本。





1. 注释

在标记# --- !Ups以上的都是注释部分,标记中的---不是必须的,只要包含#!Ups就可以了,即标记模式要满足^#.*!Ups.*$,同样地,标记#
--- !Downs也是类似的。注释部分没有格式限制,可以随意书写。

2. up脚本

在标记# --- !Ups和# --- !Downs之间的部分是up脚本,up脚本是一段用来初始化或更新数据库的sql脚本,每一条sql语句必须以分号;结尾,如果sql语句中含有分号,需要使用;;进行转义。注释方法遵循标准sql,单行注释使用--,多行注释使用/*
... */。

3. down脚本

标记# --- !Downs之后的部分是down脚本,down脚本是一段撤销脚本,类似于数据库中的事务回滚,将数据库恢复到up脚本执行之前的状态。书写规则同up脚本。

四、Evolution配置表PLAY_EVOLUTIONS

Evolution插件使用表PLAY_EVOLUTIONS管理同步脚本。在项目第一次启动时,Evolution插件会在数据库中创建PLAY_EVOLUTIONS表,比较可惜的是,Evolution插件并没有根据不同的数据库类型生成不同的建表语句,而是硬编码了下面的建表语句:

?
如果连接的是Oracle数据库,这条语句执行会报错,因为Oracle不认识text类型。下文会讲到如何针对Oracle手工修改建表语句。

PLAY_EVOLUTIONS表包含7个字段,解释如下:

- id: 唯一对应一个脚本文件名,也成为revision,值从1开始

- hash:apply_script和revert_script两个字段内容拼接后的sha1哈希值,用来检测脚本内容是否发生变化

- applied_at:记录up或down脚本执行时间

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

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

- state:保存当前的执行状态,值可以为:applied/applying_up/applying_down

- last_problem: 存放脚本执行时错误信息

每个数据库的Evolution脚本文件数和相应PLAY_EVOLUTIONS表中记录条数相同,并且是一一对应关系,对应关系为文件名和id相同。

五、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。

六、常见问题解决方法

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文件的头两行注释:





七、不同运行模式下的差异

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}=true和applyDownEvolutions.{database
name}=true,则自动执行本次Evolution操作;

3)如果本次Evolution操作涉及down脚本,并且没有同时配置applyEvolutions.{database
name}=true和applyDownEvolutions.{database
name}=true两个参数,则直接抛出InvalidDatabaseRevision异常。

八、Evolution with Oracle

在play第一次连接数据库时,Evolution插件会尝试创建PLAY_EVOLUTIONS表,上文曾提到过,Evolution插件以硬编码形式提供的建表语句无法在Oracle中执行,原因是Oracle中没有text类型,所以在将play的数据源切换至Oracle时,我们需要手工在Oracle上创建PLAY_EVOLUTIONS表,建表语句如下:

?
这里会有个问题,apply_script和revert_script存放的是up和down脚本,有时脚本会很大,而很多数据库都会限制text类型必须小于64kb,就算选择Oracle的clob类型也必须小于4000kb,较通用的解决办法是将大的脚本文件分成几个较小的脚本文件。

另外需要注意的是,Oracle中字段名不能超过30个字符,不要使用实体映射的默认表名,例如User/Role,最好使用@Table注解生成另外一个名称:

?
九、小结

Evolution插件总体还是令人比较满意的,遗憾的是在连接Oracle数据源时需要手工干预。希望在以后版本中,Evolution插件能够自动判断数据库类型,尽量减少人为的手工干预。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: