前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Play 2.1 - Evolution插件使用指南

Play 2.1 - Evolution插件使用指南

作者头像
joymufeng
发布2018-05-17 15:16:02
1.2K0
发布2018-05-17 15:16:02
举报

    首先你需要明白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插件并没有根据不同的数据库类型生成不同的建表语句,而是硬编码了下面的建表语句:

代码语言:javascript
复制
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_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表,建表语句如下:

代码语言:javascript
复制
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注解生成另外一个名称:

代码语言:javascript
复制
@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); 
}

九、小结

    Evolution插件总体还是令人比较满意的,遗憾的是在连接Oracle数据源时需要手工干预。希望在以后版本中,Evolution插件能够自动判断数据库类型,尽量减少人为的手工干预。

十、参考

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档