Managing database evolutions

When you use a relational database, you need a way to track and organize your database schema evolutions. Typically there are several situation where you need a more sophisticated way to track your database schema changes:

  • When you work within a team of developers, each person needs to know about any schema change.
  • When you deploy on a production server, you need to have a robust way to upgrade your database schema.
  • If you work on several machines, you need to keep all database schemas synchronized.

If you work with JPA, Hibernate can handle database evolutions for you automatically. Evolutions are useful if you don’t use JPA or if you prefer to manually take care of your database schema for finer tuning.

Evolutions scripts

Play tracks your database evolutions using several evolutions script. These scripts are written in plain old SQL and should be located in the db/evolutions directory of your application.

The first script is named 1.sql, the second script 2.sql, and so on…

Each script contains two parts:

  • The Ups part the describe the required transformations.
  • The Downs part that describe how to revert them.

For example, take a look at this first evolution script that bootstrap a basic application:

# Users schema

# --- !Ups

CREATE TABLE User (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    email varchar(255) NOT NULL,
    password varchar(255) NOT NULL,
    fullname varchar(255) NOT NULL,
    isAdmin boolean NOT NULL,
    PRIMARY KEY (id)
);

# --- !Downs

DROP TABLE User;

As you see you have to delimitate the both Ups and Downs section by using comments in your SQL script.

When evolutions are activated, Play will check your database schema state before each request in DEV mode, or before starting the application in PROD mode. In DEV mode, if your database schema is not up to date, an error page will suggest that you to synchronize your database schema by running the appropriate SQL script.

If you agree with the SQL script, you can apply it directly by clicking on the ‘Apply evolutions’ button.

If you use an in-memory database (db=mem), Play will automatically run all evolutions scripts if your database is empty.

Synchronizing concurrent changes

Now let’s imagine that we have two developers working on this project. Developer A will work on a feature that require a new database table. So it will create the following 2.sql evolution script:

# Add Post

# --- !Ups
CREATE TABLE Post (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    title varchar(255) NOT NULL,
    content text NOT NULL,
    postedAt date NOT NULL,
    author_id bigint(20) NOT NULL,
    FOREIGN KEY (author_id) REFERENCES User(id),
    PRIMARY KEY (id)
);

# --- !Downs
DROP TABLE Post;

Play will apply this evolution script to Developer A’s database.

On the other hand, developer B will work on a feature that require to alter the User table. So it will also create the following 2.sql evolution script:

# Update User

# --- !Ups
ALTER TABLE User ADD age INT;

# --- !Downs
ALTER TABLE User DROP age;

Developer B finishes his feature and commit (let’s say they are using Git). Now developer A has to merge the work of his colleague before continuing, so it run git pull, and the merge has a conflict, like:

Auto-merging db/evolutions/2.sql
CONFLICT (add/add): Merge conflict in db/evolutions/2.sql
Automatic merge failed; fix conflicts and then commit the result.

Each developer has created a 2.sql evolution script. So developer A needs to merge the content of this file:

<<<<<<< HEAD
# Add Post

# --- !Ups
CREATE TABLE Post (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    title varchar(255) NOT NULL,
    content text NOT NULL,
    postedAt date NOT NULL,
    author_id bigint(20) NOT NULL,
    FOREIGN KEY (author_id) REFERENCES User(id),
    PRIMARY KEY (id)
);

# --- !Downs
DROP TABLE Post;
=======
# Update User

# --- !Ups
ALTER TABLE User ADD age INT;

# --- !Downs
ALTER TABLE User DROP age;
>>>>>>> devB

The merge is really easy to do:

# Add Post and update User

# --- !Ups
ALTER TABLE User ADD age INT;

CREATE TABLE Post (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    title varchar(255) NOT NULL,
    content text NOT NULL,
    postedAt date NOT NULL,
    author_id bigint(20) NOT NULL,
    FOREIGN KEY (author_id) REFERENCES User(id),
    PRIMARY KEY (id)
);

# --- !Downs
ALTER TABLE User DROP age;

DROP TABLE Post;

This evolution script represents the new revision 2 of the database, that is different of the previous revision 2 that developer A has already applied.

So Play will detect it and ask developer A to synchronize his database by first reverting the old revision 2 already applied, and by applying the new revision 2 script:

Inconsistent states

Sometimes you will make a mistake in your evolution scripts, and they will fail. In this case, Play will mark your database schema as being in an inconsistent state and will ask you to manually resolve the problem before continuing.

For example, the Ups script of this evolution has an error:

# Add another column to User

# --- !Ups
ALTER TABLE Userxxx ADD company varchar(255);

# --- !Downs
ALTER TABLE User DROP company;

So trying to apply this evolution will fail, and Play will mark your database schema as inconsistent:

Now before continuing you have to fix this inconsistency. So you run the fixed SQL command:

ALTER TABLE User ADD company varchar(255);

… and then mark this problem as manually resolved by clicking on the button.

But because you evolution script has error, you probably want to fix it. So you modify the 3.sql script:

# Add another column to User

# --- !Ups
ALTER TABLE User ADD company varchar(255);

# --- !Downs
ALTER TABLE User DROP company;

Play detects this new evolution that replaces the previous 3 one, and will run the following script:

Now everything is fixed, and you can continue to work.

In developement mode however it is often simpler to simply trash your developement database and reapply all evolutions from the beginning.

Evolutions commands

The evolutions run interactively in DEV mode. However in PROD mode you will have to use theevolutions command to fix your database schema before running your application.

If you try to run a application in production mode on a database that is not up to date, the application will not start.

~        _            _
~  _ __ | | __ _ _  _| |
~ | ‘_ \| |/ _‘ | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! master-localbuild, http://www.playframework.org
~ framework ID is prod
~
~ Ctrl+C to stop
~
13:33:22 INFO  ~ Starting ~/test
13:33:22 INFO  ~ Precompiling ...
13:33:24 INFO  ~ Connected to jdbc:mysql://localhost
13:33:24 WARN  ~
13:33:24 WARN  ~ Your database is not up to date.
13:33:24 WARN  ~ Use `play evolutions` command to manage database evolutions.
13:33:24 ERROR ~ 

@662c6n234
Can‘t start in PROD mode with errors

Your database needs evolution!
An SQL script will be run on your database.

play.db.Evolutions$InvalidDatabaseRevision
	at play.db.Evolutions.checkEvolutionsState(Evolutions.java:323)
	at play.db.Evolutions.onApplicationStart(Evolutions.java:197)
	at play.Play.start(Play.java:452)
	at play.Play.init(Play.java:298)
	at play.server.Server.main(Server.java:141)
Exception in thread "main" play.db.Evolutions$InvalidDatabaseRevision
	at play.db.Evolutions.checkEvolutionsState(Evolutions.java:323)
	at play.db.Evolutions.onApplicationStart(Evolutions.java:197)
	at play.Play.start(Play.java:452)
	at play.Play.init(Play.java:298)
	at play.server.Server.main(Server.java:141)

The error message ask you to run the play evolutions command:

$ play evolutions
~        _            _
~  _ __ | | __ _ _  _| |
~ | ‘_ \| |/ _‘ | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! master-localbuild, http://www.playframework.org
~ framework ID is gbo
~
~ Connected to jdbc:mysql://localhost
~ Application revision is 3 [15ed3f5] and Database revision is 0 [da39a3e]
~
~ Your database needs evolutions!

# ----------------------------------------------------------------------------

# --- Rev:1,Ups - 6b21167

CREATE TABLE User (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    email varchar(255) NOT NULL,
    password varchar(255) NOT NULL,
    fullname varchar(255) NOT NULL,
    isAdmin boolean NOT NULL,
    PRIMARY KEY (id)
);

# --- Rev:2,Ups - 9cf7e12

ALTER TABLE User ADD age INT;
CREATE TABLE Post (
    id bigint(20) NOT NULL AUTO_INCREMENT,
    title varchar(255) NOT NULL,
    content text NOT NULL,
    postedAt date NOT NULL,
    author_id bigint(20) NOT NULL,
    FOREIGN KEY (author_id) REFERENCES User(id),
    PRIMARY KEY (id)
);

# --- Rev:3,Ups - 15ed3f5

ALTER TABLE User ADD company varchar(255);

# ----------------------------------------------------------------------------

~ Run `play evolutions:apply` to automatically apply this script to the db
~ or apply it yourself and mark it done using `play evolutions:markApplied`
~

If you want Play to automatically run this evolution for you, then run:

$ play evolutions:apply

If you prefer running this script manually on your production database, you need to tell Play that you database is up-to-date by running:

$ play evolutions:markApplied

If there are any errors while automatically running the evolutions scripts, as in DEV mode, you need to manually resolve them, and mark your database schema a fixed by running:

$ play evolutions:resolve

Continuing the discussion

Learn how to configure Logging.

时间: 2024-11-19 23:58:21

Managing database evolutions的相关文章

7 Managing Database Workload Using Services

Note: A database service can only be offered on a single network. Caution: By default, any named user may create a server pool. To restrict the operating system users that have this privilege, Oracle strongly recommends that you add specific users to

Oracle 11gR2 RAC 新特性说明

最近接触了一下Oracle 11g R2 的RAC,发现变化很大. 所以在自己动手做实验之前还是先研究下它的新特性比较好. 一.    官网介绍 先看一下Oracle 的官网文档里对RAC 新特性的一点说明. Oracle Database 11g Release 2 (11.2.0.2) New Features in Oracle RAC http://download.oracle.com/docs/cd/E11882_01/rac.112/e16795/whatsnew.htm#CHDJ

11g ocm认证考试经历

从2012年北漂以来到现在有近5个年头了,dba这活儿正了八经的玩数据库其实是从这两三年,学校学的sql server当时理解的不深,只学会了一些简单的sql语句,2012-2014年因为在大学做运维工作相对轻松,所以决定考一下ocp,当时最初的相做数据库的原因很简单,上智联和前程上搜了一下dba的工作招聘需求量很大,而且在那会儿工资比我干的工作薪资高,很单纯的一个想法,北漂吗?出来飘的,毕竟钱这玩意儿在北京是活命的东西,多点还是有用的! 不扯别的了,萌生考ocm的念头在14年拿到ocp那会儿就

android-SQLite 和 Content

SQLite 游标(Cursor)相当于指向底层数据中结果集的指针,而不是提取和返回结果值的副本,是在结果集中对位置(行)进行控制的管理方式. moveToFirst:把游标移动到查询结果的第一行 moveToNext:把游标移动到下一行 moveToPrevious:把游标移动到前一行 getCount:返回结果集中的行数 getColumnIndexOrThrow:返回具有指定名称的列的索引 getColumnName:返回指定索引的列的名称 getColumnNames:返回当前游标中的所

TimesTen 数据库复制学习:10. 定义classic复制

设计高可用系统 复制的目标为: 1. 提供一个或多个复制数据库,保证数据可以为应用所用 2. 提供复制数据库用于恢复 3. 负载均衡 4. 无中断的软件升级和维护 classic replication scheme支持以下形式: Unidirectional - 这个和ASP有和区别,会切换角色吗 Bidirectional split workload - 双向,属于互备型,两个数据库负责不同的工作负载 Bidirectional distributed workload - 双向,属于双活

Oracle Schema Objects

One characteristic of an RDBMS is the independence of physical data storage from logical data structures. RDBMS的特点之一是物理数据与逻辑数据结构的独立性. Introduction to Schema Objects Schema Object Types Schema Object Storage Schema Object Dependencies SYS and SYSTEM S

Oracle_052_lesson_p7

Managing Database Storage Structures 管理DB存储架构 you should be able to:1.Describe the storage of table row data in blocks2.Create and manage tablespaces3.Obtain tablespace information desc dba_segments;desc dba_extents; 例 循环插入:declarebeginfor i in 1..10

Oracle-开篇-学习经验

工作五年,未有深入研究,仅以自身经验浅谈如何学习,不足之处还望见谅. Oracle数据库软件产品在实际工作中分为数据库开发和数据库运维,尽管大部分公司中没有对此区分,但两者有着本质区别. 数据库开发基本通过PL/SQL实现对业务的处理,属编程.数据库管理员则考验对数据库软件的理解以及使用是否熟练,DBA指的就是它,你的手机通常要24小时待命. 数据库开发人员的职责,Oracle官方说法: Oracle database developers can expect to be involved i

Managing SQLite Database

Approach #1: Use a Singleton to Instantiate the SQLiteOpenHelper Declare your database helper as a static instance variable and use the Singleton pattern to guarantee the singleton property. The sample code below should give you a good idea on how to