IndexedDB密钥生成器在put-transaction之后重置

时间:2014-11-03 05:45:41

标签: angularjs ionic-framework indexeddb angularjs-service

这个问题令我难过。

出于某种原因,indexedDB中的自动增量密钥生成器在使用put-transaction对现有对象执行和更新后重置,从而导致数据库中的数据被覆盖。

对于我的应用程序,我使用自编写的用于angularJS的IndexedDB服务,并实现了所有基本的CRUD功能。 我还可以补充说,我正在开发Ionic Framework,尽管我怀疑这是否应该受到指责。

考虑到服务是一项正在进行的工作,我已经让对象存储的关键路径默认为" id"采用自动增量策略。 然而,给定商店的索引由用户决定在特定对象中。 举个例子:

dbHelper.objectStores = [{'employees', 
    indices: [{indexName: 'name', isUnique: false}, 
              {indexName: 'phone', isUnique: true}]}];

除非已在数据库中创建,否则将创建对象存储'员工'有索引'名称'和'电话',其中'电话'必须是一个独特的价值,而名称'不会。

这是openDB函数的实现。 请注意,dbHelper.objectStores应该是空的,因为它在用户打开数据库之前分配这些属性(否则它是默认的)。

angular.module('dbProvider', [])
.factory('$db', ['$window', function($window) {

// DB Object

var dbHelper = {};

// Properties - Are given defaults unless assigned manually by user before openDB is invoked.

dbHelper.dbName = 'defaultDB';
dbHelper.dbVersion = 1;
dbHelper.objectStores = [];

dbHelper.openDB = function(onCompleteCallback, onErrorCallback) {

        console.log('Atempting to open db with name ' + dbHelper.dbName + '.');

        var request = $window.indexedDB.open(dbHelper.dbName, dbHelper.dbVersion);

      // Invoked by indexedDB if version changes

      request.onupgradeneeded = function(e) {

        console.log('Version change. Current version: ' + dbHelper.dbVersion);

        var db = e.target.result;

        e.target.transaction.onerror = onErrorCallback;

        if(dbHelper.objectStores.length === 0) {
            dbHelper.objectStores.push({name:'defaultStore', indices: []});
        }

        for(var store in dbHelper.objectStores) {

            if(db.objectStoreNames.contains(dbHelper.objectStores[store].name)) {

                console.log(dbHelper.objectStores[store].name + ' deleted.');
                db.deleteObjectStore(dbHelper.objectStores[store].name);

            }

            var newStore = db.createObjectStore(dbHelper.objectStores[store].name, {keyPath: "id", autoIncrement: true});

            for(var index in dbHelper.objectStores[store].indices) {

                newStore.createIndex(dbHelper.objectStores[store].indices[index].indexName,
              dbHelper.objectStores[store].indices[index].indexName,  
              {unique : dbHelper.objectStores[store].indices[index].isUnique});
            }

            console.log(dbHelper.objectStores[store].name + ' created.');
        }

    };

    request.onsuccess = function(e) {
        console.log('DB ' + dbHelper.dbName + ' open.');
        dbHelper.indexedDB.db =  e.target.result;

        onCompleteCallback();
    };

    request.onerror = onErrorCallback;

};

以下是一些CRUD函数(有问题的函数):

dbHelper.findItemWithIndex = function(keyValue, storename,
    onCompleteCallback,onErrorCallback) {

    var db = dbHelper.indexedDB.db;
    var trans = db.transaction([storename], "readwrite");
    var store = trans.objectStore(storename);

    var index = store.index(keyValue.key);
    index.get(keyValue.value).onsuccess = function(event) {

    onCompleteCallback(event.target.result);

    };

};

dbHelper.addItemToStore = function(item, storename, 
    onCompleteCallback, onErrorCallback) {

    var db = dbHelper.indexedDB.db;
    var trans = db.transaction([storename], "readwrite");
    var store = trans.objectStore(storename);

    var request = store.add(item);

    trans.oncomplete = onCompleteCallback;

    request.onerror = onErrorCallback;
};

dbHelper.deleteItemFromStore = function(itemId, storename, 
    onCompleteCallback, onErrorCallback) {

    var db = dbHelper.indexedDB.db;
    var trans = db.transaction([storename], "readwrite");
    var store = trans.objectStore(storename);

    var request = store.delete(itemId);

    trans.oncomplete = onCompleteCallback;

    request.onerror = onErrorCallback;

};

dbHelper.updateItem = function(item, storename, onCompleteCallback, onErrorCallback) {

    var db = dbHelper.indexedDB.db;
    var trans = db.transaction([storename], "readwrite");
    var store = trans.objectStore(storename);

    var request = store.put(item);

    trans.oncomplete = onCompleteCallback;

    request.onerror = onErrorCallback;


};

最后,调用事务的控制器中的代码。 这里的策略是,项目在第一次持久化时使用addItemToStore函数添加到数据库,然后是updateItem函数。 第一次添加后,会立即获取该对象,以便继续使用db中指定的id进行处理。

$scope.updateTemplate = function() {

    console.log('Saving..');

    var onCompleteCallback = {};

    if(!$scope.formTemplate.firstSave) {
        onCompleteCallback = $scope.updateModel;
    } else {
        $scope.formTemplate.firstSave = false;
            onCompleteCallback = $scope.setId;
    }

    $db.updateItem($scope.formTemplate, $scope.objectStore.name, 
        onCompleteCallback, $scope.dbError);

};

$scope.newItem = function() {

    $db.addItemToStore($scope.formTemplate, $scope.objectStore.name, 
        $scope.setId, $scope.dbError);

};

$scope.setId = function() {

    $db.findItemWithIndex(
        {key: 'title', 
        value: $scope.formTemplate.title},
        $scope.objectStore.name,
        function(result) {
            console.log(JSON.stringify(result));
            $scope.formTemplate = result;
        },
        function(error) {
            $scope.dbError(error);
        });

}

在这里,一切都变得地狱。 我添加一个对象,返回到另一个视图,并在id = 1的列表中找到它。 我添加另一个对象,返回列表视图,其中id = 2。 依此类推......

然后,在使用$ scope.updateTemplate函数更新任一对象之后,这个函数也像魅力一样,事情变得有趣:

添加的下一个对象获取id = 1并完全删除之前的旧的数字uno。

下一个对象也会获得id,导致他们替换已经存在的对象。

是什么导致这个?

为了测试我在OS 10.10中使用Safari 8,并且我使用KitKat 4.4.2部署到LGG2。

2 个答案:

答案 0 :(得分:1)

说实话,我撇去了,但是我看到了这一点," Safari 8" - 最新的iOS和Safari在IndexedDB上存在严重错误:http://www.raymondcamden.com/2014/9/25/IndexedDB-on-iOS-8--Broken-Bad

答案 1 :(得分:1)

在iOS9中,许多IndexedDb错误都是固定的,但不是全部。我们目前正在iOS9 Beta 2上进行测试,您发现的这个特定错误并未修复。

我们能够通过不在对象存储上使用自动增量来解决此问题。我们只需手动找到最大键值并增加该值。

插入对象如下所示:

var store = db.transaction([entity], "readwrite").objectStore(entity);
store.openCursor(null, "prev").onsuccess = function (event) {
  var maxKey = event.target.result.key || 0;
  object.id = maxKey + 1;
  store.add(object);
}