这个问题难倒了我。
由于某种原因,indexedDB 中的自动增量键生成器在使用 put-transaction 对现有对象执行和更新后会重置,从而导致数据库中的数据被覆盖。
对于我的应用程序,我使用的是 AngularJS 自行编写的 IndexedDB 服务,并实现了所有基本的 CRUD 功能。 我还可以补充一点,我正在使用 Ionic Framework 进行开发,尽管我怀疑这是罪魁祸首。
考虑到该服务是一项正在进行的工作,我已将对象存储的关键路径默认为“id”,并采用自动增量策略。 然而,给定存储的索引由用户在特定对象中决定。 举个例子:
dbHelper.objectStores = [{'employees',
indices: [{indexName: 'name', isUnique: false},
{indexName: 'phone', isUnique: true}]}];
除非已经在数据库中创建,否则这将创建具有索引“name”和“phone”的对象存储“employees”,其中“phone”必须是唯一值,而“name”则不是。
这里是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;
};
最后,来自调用事务的 Controller 的代码。 这里的策略是,在第一次持久化项目时使用 addItemToStore 函数将项目添加到数据库中,然后使用 updateItem 函数。 第一次添加后,会立即获取对象,以便继续使用数据库中指定的 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);
});
}
到了这里,一切都会陷入 hell 。 我添加一个对象,返回到另一个 View 并在 id=1 的列表中找到它。 我添加另一个对象,返回到 ListView ,它的 id=2 就在那里。 等等等等..
然后,在使用 $scope.updateTemplate 函数更新任一对象后(该函数也像魅力一样工作),事情变得有趣:
添加的下一个对象的 id=1 并完全删除之前的旧数字。
接下来的对象也会获得 id,从而导致它们替换已经存在的对象。
什么可能导致这种情况?
为了进行测试,我在 OS 10.10 中使用 Safari 8,并部署到带有 KitKat 4.4.2 的 LGG2。
最佳答案
说实话,我浏览了一下,但我看到了这个,“Safari 8” - 最新的 iOS 和 Safari 对 IndexedDB 有严重的错误:http://www.raymondcamden.com/2014/9/25/IndexedDB-on-iOS-8--Broken-Bad
关于angularjs - IndexedDB key 生成器在 put-transaction 后重置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26708479/