yahoo / squidb

SquiDB is a SQLite database library for Android and iOS
https://github.com/yahoo/squidb/wiki
Apache License 2.0
1.31k stars 132 forks source link

Making setId() work with default ID field #186

Closed iambibhas closed 8 years ago

iambibhas commented 8 years ago

I have a simple model spec that looks like this -

@TableModelSpec(className = "Survey", tableName = "survey")
public class SurveySpec {
    @PrimaryKey
    @ColumnSpec(name="_id")
    long Id;

    public String partner;
    public String name;
}

Now I need to insert some surveys with pregenerated IDs. But I can't seem to get that done. For example -

Survey s = new Survey().setId(1234).setName("Test with ID").setPartner("Test Partner");
Survey s2 = new Survey().setName("Test without ID").setPartner("Test Partner");
db.persist(s);
db.persist(s2);

The first insert doesn't work. The second one works. How to get around this?

Edit: I've found this -

Note that persist() will only insert a new row if the model you are persisting doesn't have its id property set. If it does, SquidDatabase will treat it as an update instead (see the Update section below). If you want to create a new row from the model object regardless of whether or not an id is set, you can call createNew(model), which will clear the id property and insert a new row automatically. However, we recommend you use persist() or persistWithOnConflict() in most cases.

But even when I use db.createNew(s), the record is created with an autoincremented id instead of the one I provided.

Edit: looks like createNew() is not what I'm looking for -

public boolean createNew(TableModel item) {
        item.setId(TableModel.NO_ID);
        return insertRow(item, null);
    }
sbosley commented 8 years ago

You're on the right track. As you've found in the docs, squidb assumes that the lack of primary key value means a new row should be inserted, and the presence of one means that row should be updated. As you can also see, createNew() clears the id field and then delegates to the protected insertRow() method. insertRow() is the method you want; it will attempt to perform an insert with exactly the values that are set in the model, including ID if present. insertRow() is a protected method, meant to be called by higher-level methods defined in SquidDatabase subclasses. The logic of how you want to handle things when an id is or isn't present can vary a lot by use case, and you should consider exactly what you want it to do, but here is one reasonable example:

// In your SquidDatabase subclass...
public boolean insertWithId(TableModel item) {
    return insertRow(item, ConflictAlgorithm.REPLACE);
}

This method, for example, will delete any pre-existing rows with that item's id value (that's what the REPLACE conflict algorithm) does, and then insert the item using its values, including any id that has been set. Again though, you should consider carefully exactly what you want the behavior to be in your various use cases, and implement some method for that behavior.

iambibhas commented 8 years ago

@sbosley this sounds good. But I wish insertNew() had a flag to tell it not to clear the ID field. You could turn it off by default, not breaking current implementations. Was there a reason to make it so hard for the user to define an ID?

sbosley commented 8 years ago

createNew() might be better thought of as something like insertOrCopy() -- if the row does not exist, create it; if it does exist, make a new row with the same values.

We have found that it's very rare that we care about the value of the rowid SQLite uses, so squidb regards it as more of an internal bookkeeping value that indicates whether or not a model object is tied to an existing row. More often, we find that if there is some other "key" value besides the rowid, that it is something like a server-provided String guid. In such cases, thinking of the rowid as internal bookkeeping, we use persistWithOnConflict() if a conflict algorithm is appropriate for the use case, or we define business logic to insertRow() or updateRow() that is custom tailored to the specific use case if a conflict algorithm is not what we want.

Hope that helps!