PersistenceJS:使用migrate()更新现有数据库

时间:2014-06-13 11:45:56

标签: javascript html5 persistence

我目前正在尝试使用PersistenceJS的迁移插件对现有数据库进行更改。我可以在DB中添加/编辑/删除项目,但是......

  • 如何将列添加到现有(!)表中?
  • 如何更改现有(!)列的类型,例如来自' text'到'整数'?

  • These changes should retain currently existing data.

可悲的是,文档有点稀缺,也许你可以帮忙吗?

以下是正常工作设置:

persistence.store.websql.config(persistence, 'tododatabase', 'todos are fun', 5*1024*1024);

var Todo = persistence.define('Todo', {
    task: 'TEXT',
    priority: 'INT',
    done: 'BOOL'
});

persistence.schemaSync();

function addTodo( item ){
    var todo = new Todo();
    todo.task = item.task;
    todo.priority = item.priority;
    todo.done = item.done;

    persistence.add(todo);
    persistence.flush();
};

function deleteTodo( item, callback ){
    // item.id was created automatically by calling "new Todo()"
    Todo.all().filter('id','=', item.id ).destroyAll( function(){
        persistence.flush( callback );
    });
};

有点的迁移代码:

persistence.defineMigration(1, {
    up: function() {
        this.createTable('Todo', function(t){
            t.text('task');
            t.integer('priority');
            t.boolean('done');
        });
    },
    down: function() {
        this.dropTable('Todo');
    }
});

persistence.defineMigration(2, {
    up: function() {
        this.addColumn('Todo', 'due', 'DATE');
    },
    down: function() {
        this.removeColumn('Todo', 'due');
    }
});


function migrate( callback ){
    console.log('migrating...');
    persistence.migrations.init( function(){
        console.log('migration init');
        // this should migrate up to the latest version, in our case: 2
        persistence.migrate( function(){
            console.log('migration complete!');
        } );
    });
}

结果...

  1. 调用migrate()只会注册到" migration init",永远不会调用完整的处理程序," due"未创建列
  2. 在调用migrate()之前没有调用schemaSync(),因为Zef Hemel自己在this post中提出的结果与1相同。
  3. 将第一行更改为persistence.store.websql.config(persistence, 'newdatabase', 'testing migration', 5*1024*1024);,不调用schemaSync(),只调用migrate()将成功记录"迁移完成!" - 但它是在一个新的,完全空的数据库" newdatabase"中实现的,当然不会保留任何现有数据。
  4. 摘要

    使用persistence.store.websql.config(...)persistence.define('Todo',...)persistence.schemaSync()创建了一个数据库。

    我现在想要保留该数据库中已存在的所有数据,但希望

    • 从'整数'更改列priority的类型到'文字'
    • 添加类型为' date'的列due所有现有的Todos

    如果你能把我推向正确的方向,我会非常感激!

    谢谢!

2 个答案:

答案 0 :(得分:5)

我终于开始工作了。我的初始要求存在许多问题,我想指出以供将来参考。看一下第一个迁移定义:

persistence.defineMigration(1, {
    up: function() {
        this.createTable('Todo', function(t){
        ...


毫不奇怪,createTable将完全执行以下操作:它将执行SQL语句'CREATE TABLE Todo ...',如果已存在名称为Todo的表,它将静默失败并停止迁移。这就是为什么它适用于新数据库,而不是现有数据库。请记住:我已经拥有一个带有表格的实时数据库" Todo"需要更新。如果你重新开始(即你没有使用schemaSync),createTable就可以了。由于迁移插件不提供createTableIfNotExists方法,因此我需要使用executeSql,如下所示:

persistence.defineMigration(1, {
    up: function() {
        this.executeSql('CREATE TABLE IF NOT EXISTS Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority INT, done BOOL)');
        ...


既然从模式版本0到1的迁移成功,那么迁移到版本2也是成功的。

迁移到版本3时,priority列的类型需要从int更改为text。这通常使用ALTER COLUMN SQL命令完成,Web SQL / SQLite不支持。请参阅 Omitted Features for SQLite

使用SQLite更改列需要一个4步骤的解决方法:

persistence.defineMigration(3, {
    up: function() {
        // rename current table
        this.executeSql('ALTER TABLE Todo RENAME TO OldTodo');
        // create new table with required columns and column types
        this.executeSql('CREATE TABLE Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority TEXT, done BOOL)');
        // copy contents from old table to new table
        this.executeSql('INSERT INTO Todo(id, task, priority, done) SELECT id, task, priority, done FROM OldTodo');
        // delete old table
        this.executeSql('DROP TABLE OldTodo');
    },
    ...


当然,在更改列类型后,' Todo'的实体定义也应该改变:

var Todo = persistence.define('Todo', {
    task: 'TEXT',
    priority: 'TEXT', // was 'INT'
    due: 'DATE',
    done: 'BOOL'
});


最后,完整的来源:

persistence.store.websql.config(persistence, 'tododatabase', 'todos are fun', 5*1024*1024);

// persistence.debug = true;

//v0 + v1
// var Todo = persistence.define('Todo', {
//  task: 'TEXT',
//  priority: 'INT',
//  done: 'BOOL'
// });

//v2
// var Todo = persistence.define('Todo', {
//  task: 'TEXT',
//  priority: 'INT',
//  due: 'DATE',
//  done: 'BOOL'
// });

//v3
var Todo = persistence.define('Todo', {
    task: 'TEXT',
    priority: 'TEXT',
    due: 'DATE',
    done: 'BOOL'
});


persistence.defineMigration(1, {
    up: function() {
        this.executeSql('CREATE TABLE IF NOT EXISTS Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority INT, done BOOL)');
    },
    down: function() {
        this.dropTable('Todo');
    }
});

persistence.defineMigration(2, {
    up: function() {
        this.addColumn('Todo', 'due', 'DATE');
    },
    down: function() {
        this.removeColumn('Todo', 'due');
    }
});

persistence.defineMigration(3, {
    up: function() {
        // rename current table
        this.executeSql('ALTER TABLE Todo RENAME TO OldTodo');
        // create new table with required columns
        this.executeSql('CREATE TABLE Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority TEXT, due DATE, done BOOL)');
        // copy contents from old table to new table
        this.executeSql('INSERT INTO Todo(id, task, priority, due, done) SELECT id, task, priority, due, done FROM OldTodo');
        // delete current table
        this.executeSql('DROP TABLE OldTodo');
    },
    down: function() {
        this.executeSql('ALTER TABLE Todo RENAME TO OldTodo');
        this.executeSql('CREATE TABLE Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority INT, due DATE, done BOOL)');
        this.executeSql('INSERT INTO Todo(id, task, priority, due, done) SELECT id, task, priority, due, done FROM OldTodo');
        this.executeSql('DROP TABLE OldTodo');
    }
});


function migrate( callback ){
    console.log('migrating...');
    persistence.migrations.init( function(){
        console.log('migration init');
        persistence.migrate( function(){
            console.debug('migration complete!');
            callback();
        } );
    });
};

migrate( onMigrationComplete );

function onMigrationComplete(){
    // database is ready. do amazing things...
};

答案 1 :(得分:2)

这是一个很好的解释,谢谢!但我想我知道一种更简单的方法来实现这一目标。

我遇到了和你一样的麻烦:我有一组用persistence.define描述并用persistence.schemaSync创建的模式。

所以这是我的特例:

    // This is my mixin for all schemas    
    var Versioned = persistence.defineMixin('Versioned', {
      serverId: "TEXT",
      intVersion: "INT",
      dtSynced: "DATE",
      dtCreatedAt: "DATE",
      dtUpdatedAt: "DATE",
      delete: "BOOL",
      update: "BOOL",
      add: "BOOL",
      isReadOnly: "BOOL"
    });

    // This is one of the schemas I need to update with a new field.        
    var Person = persistence.define('Person', {
      fullName: "TEXT",
      rate: "INT"
    });

    //... More schema definitions

    // Setup mixin        
    Person.is(Versioned);

    // Sync schemas
    persistence.schemaSync();

确定。没什么特别的。现在几个月后我的应用程序正在投入生产中,我想在isEmployed架构中添加一个新字段Person

根据文档,我应该将所有模式定义重写为迁移并停止使用persistence.schemaSync()。但我不想重写我的所有定义。而不是我在PersistenceJS init代码后面定义一个新的迁移:

    // Init ORM
    persistence.store.websql.config(
      persistence,
      'Sarafan',
      '0.0.2',                
      'Sarafan.app database', 
      100 * 1024 * 1024,      
      0                        
   );

    // Define Migrations
    persistence.defineMigration(1, {
      up: function () {
        this.addColumn('Person', 'isEmployed', 'BOOL');
      }
    });

    // ... describing isVersioned mixin


    // Updated schema definition with a new field 'isEmployed'
    var Person = persistence.define('Person', {
      fullName: "TEXT",
      rate: "INT",
      isEmployed: "BOOL"
    });

    //... More schema definitions

    // Setup mixin        
    Person.is(Versioned);

    // Apply the migration right away from the schemaSync call.
    persistence.schemaSync(function (tx) {
      persistence.migrations.init(function () {
      persistence.migrate(function(){
        // Optional callback to be executed after initialization
        });
      });
    });

就是这样!我测试了这种方法只是为了向模式添加新字段。

让我知道它是否对您有效。