Primary Key issue on iOS8 implementation of Indexe

2019-01-16 14:38发布

问题:

The issue is when you have two different object stores in the same indexeddb, primary key values appear to be "shared" across all stores.

<body>
    <script type="text/javascript">
        //prefixes of implementation that we want to test
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;

//prefixes of window.IDB objects
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange

if (!window.indexedDB) {
    window.alert("Your browser doesn't support a stable version of IndexedDB.")
}


var db;
var request = window.indexedDB.open("newDatabase", 4);

request.onerror = function(event) {
  console.log("error: ");
};

request.onsuccess = function(event) {
  db = request.result;
  console.log("success: "+ db);
};

request.onupgradeneeded = function(event) {
        var db = event.target.result;
        var objectStore = db.createObjectStore("customers", {keyPath: "arseid"});
    var objectStore = db.createObjectStore("test", {keyPath: "id"});
}



function add1() {
        var x = new Date();
    var h1 = x.getHours();
    var m1 = x.getMinutes();
    var s1 = x.getSeconds();
    console.log('starting insert on ' +  h1 + ':' + m1 + ':' + s1);

    var tx = db.transaction(["customers"], "readwrite");
    for (var i = 0; i < 1000; i++) {
        var request = tx.objectStore("customers")
                .put({ arseid: i, name: "Jonathan Smith", email: "jonathan.smith@anemailaddress.com", favourite: "chocolate cake", pet: "rudolph the red nose reindeer", address: "999 letsbe avenue, townton, countyshire" });
    }


    tx.oncomplete = function (e) {
            // Re-render all the todo's
            var x2 = new Date(); 
            var h2 = x2.getHours(); 
            var m2 = x2.getMinutes(); 
            var s2 = x2.getSeconds(); 
               console.log('transaction complete ' + h2 + ':' + m2 + ':' + s2);
        }
}


function add2() {
    //tx 2
    var tx2 = db.transaction(["test"], "readwrite");
    for (var i = 0; i < 1000; i++) {
        var request2 = tx2.objectStore("test")
                .put({ id: i, name: "Robwin Mwengway", email: "jonathan.smith@anemailaddress.com", favourite: "chocolate cake", pet: "rudolph the red nose reindeer", address: "999 letsbe avenue, townton, countyshire" });
    }

    tx2.oncomplete = function (e) {
            var x3 = new Date(); 
            var h3 = x3.getHours(); 
            var m3 = x3.getMinutes(); 
            var s3 = x3.getSeconds(); 
               console.log('transaction complete ' + h3 + ':' + m3 + ':' + s3);
        }
}


    </script>
<button onclick="add1()">Add1 data to indexedDb</button>
<button onclick="add2()">Add2 data to indexedDb</button>
</body>

(Fiddle: http://jsfiddle.net/jonnyknowsbest/4pdp8vxe/)

In iOS8, if you run up the fiddle and click "Add1 data to IndexedDb", then 1000 entries get added to the "customers" table. If you then click "Add2 data to IndexedDb", then 1000 entries get added to the "suppliers" table, but the 1000 from the "customers" is removed.

Has anyone else come across this? Is this part of the IndexedDb specification? Chrome does not seem to have this problem.

EDIT: Found this W3 Org IndexedDB Recommendation: "There can never be multiple records in a given object store with the same key." Apple seem to have applied this at the database level.

回答1:

I can confirm that iOS8 is definitely buggy here. I tried a few workarounds, but the best I can suggest is a primary key that combines some unique string, like the name of the objectStore, with a number. So for example, given two objectStores called people and notes, I'd store data with keys like so:

people/X notes/X

You can set X manually, or, use the .count() method on the objectStore to find the count and add one. Here is an example:

//Define a person
var person = {
    name:"Ray",
    created:new Date().toString(),
}

//Perform the add
db.transaction(["people"],"readwrite").objectStore("people").count().onsuccess = function(event) {
    var total = event.target.result;
    console.log(total);
    person.id = "person/" + (total+1);

    var request = db.transaction(["people"],"readwrite").objectStore("people").add(person);

    request.onerror = function(e) {
        console.log("Error",e.target.error.name);
        //some type of error handler
    }

    request.onsuccess = function(e) {
        console.log("Woot! Did it");
    }

}

Note that I specified keyPath of "id" for this OS.



回答2:

I had a similar problem however my first insert to the objectstore was a small array with just username and email and the second objectstore was very large with several nested arrays of data.

My insert method below would call back success on the all items however only the second object store would be written to the db correctly.

When I tried reversing the order I was writing to the database (writing the large objectstore items first and the username/email second) both objectstores were written correctly however the the primary keys were shared between the two objects stores. Pilots Primary Key: 1,2,3,4,5 AC Primary Key: 6,7,8...

function insert_GroupRecord(Record, Store){
    //update individual sync record
    var trans = dbGroup.transaction([Store],"readwrite");
    var store = trans.objectStore(Store);
    var request = store.put(Record);
    request.onsuccess = function(e){
        IOS_postMessage({Message:"SyncStatus", status:"Group_Insert "+Store});
    };
    request.onerror = function(e){
        GroupSyncERROR = true;
        //IOS_postMessage({Message:"SyncStatus", status:"GroupSyncFail "+Store});
    };

    request.onupgradeneeded = function(evt){
        var objectStore = evt.currentTarget.result.createObjectStore("AC",{ keyPath: "id", autoIncrement: true });
        objectStore.createIndex("ident", "ident", { unique: true });
        var objectStore2 = evt.currentTarget.result.createObjectStore("Pilots",{ keyPath: "id", autoIncrement: true });
        objectStore2.createIndex("chatname", "chatname", { unique: true });
        console.log("KFM_Group Upgrade Completed");

    };
}