List<String> allSql = new ArrayList<String>();
switch(oldVersion)
{
case 4:
allSql.add("alter table mytable add column cheese(text)");
allSql.add("update mytable set cheese = 'Swiss'");
case 5:
allSql.add("alter table mytable add column wine(text)");
}
//Loop through all and execute...
Fully creating tables from the objects is really useful in
development, and when you have completely new tables, but doing that
as an upgrade strategy is a bad idea: phone, desktop or server app.
Sql updates are generally much easier than writing custom code to copy
the data out, then copy it back in. Keep in mind, you might have apps
that are more than one version behind, so you'll need to make sure all
updates are applied incrementally (hence the switch statement. NOTICE
NO BREAK STATEMENTS!!!! The code will start at the old version
number, and run through all of the rest of the statements).
ormlite is not meant to really "replace" everything. Just sort of
help out when boilerplate code would otherwise be written. Updates
are generally a manual process any way you shake it.
I am toying with something a little more removed. Like a liquibase
lite, where we track what updates already exist in your db and only
apply new ones, but in general, the phone db's are all that
complicated.
-Kevin
OpenJPA, or any other ORM, will probably delete that int column and
add the boolean column, but what it SHOULD do, is add the boolean,
fill it with 'hasCats = catCount > 0' and THEN delete catCount.
One of our "competitors", https://www.activeandroid.com/, is going to
have that problem. One of the reasons I didn't use that, and got
involved with ormlite, is because they were going down the road of
having a "migration engine", which will suffer from the same problem
described above. (See my forum posts towards the bottom here...
http://tracker.activeandroid.com/boards/1/topics/6
No ORM tool that auto-updates can avoid it. Its inherent in the domain.
Now, you might say "OK, but I don't have any updates like that".
Maybe true. Not yet. But when you do, you're kind of screwed. Any
"migration engine" should allow for some form of manual update as I've
described, because although it might be rare in your code, its sort of
like a gun to a cop. Usually you don't need it, but if you do and
don't have it, oh well.
So, we probably could provide for some sort of object diff to update,
but the effort involved would probably dwarf any urge to have it. For
me, anyway. Its kind of a poison feature. Say you have versions 1-4,
on object "Person".
Version 1:
Person object has 'catCount'
Version 2:
Person object loses 'catCount' and adds 'hasCats'. We use
auto-update, which means we've lost all the cat related info, but we
don't care. We also add a 'dogCount' field, because dogs are cooler,
and we actually care how many you have.
Version 3:
We decide that, again, the number of dogs you haven't isn't important,
but we want to retain your dog ownership info, again because dogs are
cooler, so we drop down to manual updates for this version. We run
'alter table add column hasDogs(short)'. Then 'update Person set
hasDogs = dogCount > 0'.
Version 4:
We want a field for how many categories you have, called 'catCount'.
(ha ha). We realize doing this that sqlite doesn't actually delete
columns, so the old catCount filed is still there. Set it to 0, then
repurpose it.
So, somebody downloads your app at version 1, and updates your app
through all versions. it works.
Somebody else downloads version 1, then updates on version 3. Great,
right? Fail. The update tries to access 'dogCount', which never
existed on the entity object, since the user never saw V2, and never
updated to V2.
You can imaging several other version conflicts in that series
(install V3, update to V4, not 'catCount'. Some others probably in
there).
Moral of story. You can auto-create some tables, but updates in
pretty much every context, except temporary cache, are best handled
with sql modifications (and temp caches might as well be dropped and
recreated, right?)
-Kevin
> I've been through a few schema changes over the past few days and it
> went smoothly using the strategy you described previously. The biggest
> bump was discovering that SQLite does not support dropping columns
> from a table - took me a while to figure out that my SQL was fine...
Huh, didn't know that. Here's the recipe to drop columns by copying to a temp table:
http://www.sqlite.org/faq.html#q11
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;
Interesting that the don't rename t1_backup to t1 there.
gray