Search code examples
javascriptsqlitejaydata

JayData: Trying to insert with a 1 to Many, getting DEFAULT ERROR CALLBACK


I have stripped down my application and database context to the simplest bits. I have two tables, Maps and Markers. Many Markers exist for each map. With an existing Map, I can't seem to insert new Markers; I am getting the default error callback.

Tracing through the jaydata.js, I am getting an error in the SqLiteProvider.js file, in the exec function. It is an SQL failure, and is attempting to insert the Map (with the primary key set) into the table, which fails. What am I doing wrong?

My Context:

$data.Entity.extend("$org.types.Marker", {
    Id: { type: "int", key: true, computed: true },
    Left: { type: "int", required: true },
    Top: { type: "int", required: true },
    Color: { type: "string", required: true },
    Name: { type: "string", required: true },
    Map: { type: "$org.types.Map", inverseProperty: "Markers" }
});


$data.Entity.extend("$org.types.Map", {
    Id: { type: "int", key: true, computed: true },
    Title: { type: "string", required: true },
    Src: { type: "string", required: true },
    Height: { type: "int", required: true },
    Width: { type: "int", required: true },
    Markers: { type: "Array", elementType: "$org.types.Marker", inverseProperty: "Map" }
});

$data.EntityContext.extend("$org.types.OrgContext", {
    Maps: { type: $data.EntitySet, elementType: $org.types.Map },
    Markers: { type: $data.EntitySet, elementType: $org.types.Marker },
 });

var DB = new $org.types.OrgContext({ name: "webSql", databaseName: "Testing"});

My test code:

DB.Maps.single( function (map) { return map.Id == 1; }, {}, function(map) {
        var marker = new $org.types.Marker({
            Left: 250,
            Top: 350,
            Color: 'green',
            Name: 'Example',
            Map: map});

        DB.Markers.add(marker);
        DB.saveChanges(function() {
            alert(marker.Id);
        });

    });

Solution

  • The example doesn't insert the map object. This is not a problem if you already have it.

    There is a general problem, which is forgotten many times: the context.onReady() should be used before accessing data with the provider.

    This won't solve the problem per se, there is one line should be added before saving the ne marker object:

    DB.Maps.attach(map);
    

    This is necessary to move the object to the scope of the context and set the entityState property of the map to $data.EntityState.Unchanged. If you read an entity from the DB, the entityState will be unknown, and the provider tries to save it as a new record. Trying to saving the map entity as a new record caused an SQL error, because there was an existing record with ID=1.

    The updated code looks like bellow:

    var DB = new $org.types.OrgContext({ name: "webSql", databaseName: "Testing" });
    
            DB.onReady(function () {
                DB.Maps.single(function (map) { return map.Id == 1; }, {}, function (map) {
                    DB.Maps.attach(map); //this sets map.entityState = $data.EntityState.Unchanged;
                    var marker = new $org.types.Marker({
                        Left: 250,
                        Top: 350,
                        Color: 'green',
                        Name: 'Example',
                        Map: map
                    });
                    DB.Markers.add(marker);
                    DB.saveChanges(function () {
                        alert(marker.Id)
                    });
                });
            });
    

    UPDATE: if you save a new Maker with a new Map entity, there is no need to attach, and your code needs only the DB.onReady()