这个问题令我难过。
出于某种原因,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。
答案 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);
}