Android Room version 1.1.0 now provides the method clearAllTables().
Altough this method is very convenient, it does not clear the auto-increment value generated by the by autoGenerate(), as stated in the official documentation.
I would also like to clear the primary keys of all the tables in my database, without having to call individual queries for each table.
Based on this answer, I would assume that this SQL statement would work:
DELETE FROM sqlite_sequence WHERE name='table1' OR name='table2';
...chaining on as many OR clauses as needed.
However, sqlite_sequence is not a Room-managed table, so you may need to execute this SQL using a SupportSQLiteDatabase. You get one of those by calling getOpenHelper().getWritableDatabase() on your RoomDatabase.
However, I would not bother with this. IMHO, you should not be relying on any particular behavior of AUTOINCREMENT (e.g., always starting from some specific value). In that case, it's unclear what value there is in hacking SQLite to reset these values.
Related
I currently use a sqlite database for an application in both iOS and Android. I don't wish to move onto CoreData or JSON at present for this application as it works perfectly well with the sqlite database.
However I would like to update one of the tables in the database on my next update.
I presumed this would be something that many people would do but interestingly I've tried searching for answers but getting no results.
So my question is two-fold:
1) Is it possible to replace a table within a sqlite database?
2) If so, how does one go about doing this?
I have no code to share as I can't determine how to even start.
Thanks in advance
1) Is it possible to replace a table within a sqlite database?
Yes.
2) If so, how does one go about doing this?
You utilise a means of detecting that a change (an upgrade) is required.
On Android if using a subclass of SQLiteOpenHelper then you increase the version number (4th parameter of the super call), in which case the onUpgrade method runs, so you override the onUpgrade method to handle the update to the table(s).
This utilises the user_version field that is stored in the database's header. In short the SQliteOpenHelper when opening the databases compares the value in the header with the value passed as the 4th parameter. If the passed value is greater than the header value onUpgrade is called. If the values are the same then no such call is made. If the passed value is lower than the value in the header then onDownGrade is called which unless coded results in an exception. (typically onDowngrade is not utilised)
On IOS you could replicate this methodology of comparing a value stored in the user_version extracted via PRAGMA user_version and set via the PRAGMA user_version = ? against a value that indicates a newer version when applicable (i.e. that is not stored in the database). This isn't the only solution
The link above has a brief description which includes a link in relation to SQLite's ALTER TABLE, that itself explains the limitations of the ALTER TABLE command and has a section that covers alternative solutions. This would apply to both IOS and Android.
Well, the title says it all - does pragma foreign_keys = true to an existing database makes my database "less fast"?
Whenever data is added, removed, or changed, the database needs to check that the constraints still hold. The documentation lists some examples of how these checks look like.
In practice, it is very likely that you will need to access all the referenced tables anyway, so everything will be in the cache.
Furthermore, the biggest slowdown if a database write is the transaction overhead. So it is likely that the additional checking does not lead to a noticeable delay.
In any case, SELECT queries are not affected by foreign key constraints.
Although the accepted answer is sufficient, I want to put here one specific use case.
For anyone who has to imply foreign keys constraint to an existing database and probably needs to do some refactoring of the database, call setForeignKeyConstraintsEnabled in the onOpen method of the SQLiteOpenHelper.
From the book Android Enterprise:
The onOpen method is called only after onCreate, onUpgrade, and onDowngrade, leaving
those methods free to play fast and loose while they rebuild the schema. The occasion in
which an application must rename or recreate a table or two during an upgrade might be
one of the few times that it is truly a relief that foreign key constraints are not enforced.
Enabling foreign key constraints in the onOpen method causes them to be enforced only
after the database has been initialized.
I'm trying to implement databases linked with foreign keys and I've been told it's better (more secure) to stick to queries instead of rawQueries in general, but I'm unsure how far. What's the best approach to CRUD operations; to use multiple queries (.query(.)) or use more sophisticated rawQueries?
For example
If I want to delete a row from a table iff there is no other table linking to that particular row.
Or are there perhaps better ways when constructing the database that makes such operations smoother?
Also, when inserting into a table and there's already a row (and the column is unique or similar restraint), is there a quick way to find the rowId of the value being inserted regardless of whether or not it was inserted or already exists?
I tried `insertWithOnConflict(.)`
but it throws -1 instead of the rowId when already exists. Is there a better way than to execute another query afterwards upon failure?
I have an application that uses ORMLite. I need to create a function to reset the entire db (basically I need to delete all rows from every db, reset the autoincrement and reset indexes).
I probably can do this by launching a truncate on every table but does ORMLite has some specific method to do this?
ORMLite does not have a special method to reset the entire db. It does support the TableUtils.clearTable(connectionSource, dataClass)) method which removes all of the rows from the table, which would clear the index, but this will not reset the auto-increment. Also, I'm not sure what "reset indexes" implies more than clearing them. The process of resetting the auto-increment is going to be extremely database dependent so ORMLite will most likely never have native support that anyway.
I think your best bet is to drop the table using TableUtils.dropTable() and then re-create it with TableUtils.createTable() .
There is one way possible.. delete the database but this method is usually used when user log out and login with another user id etc, at login db is created and at logout db is deleted.
mContext.deleteDatabase("xxxxxx");
where "xxxxx" is the database name.
You can also use TableUtils.createTableIfNotExists(source,dataClass). Useful when testing.
When using a content provider for SQLite database access
Is it better practice to have a content provider for each table or to use one for all tables?
How to handle one-to-many relationships when creating new records?
A ContentProvider is not a database
A ContentProvider is a way to publicly (or semi-publicly) access data as content. This may be done in a number of ways, via file access, SQLite or even web access. A ContentProvider in and of itself is not a database, but you can program a database for it. You may also have multiple ContentProviders accessing the same database, but distributing different levels of access, or the same content in different ways according to the requestor.
What you are really asking is not a ContentProvider question, but a database question "How to handle relationships in an SQLite database" because the ContentProvider doesn't use any database code unless you tell it to via an SQLiteOpenHelper and other similar classes. So, you simply have to program your database access correctly and your SQLite database will work as desired.
A database is a database
In the old days, databases were simply flat files where each table was often its own entity to allow for growth. Now, with DBMS, there is very little reason to ever do that. SQLite is just like any other database platform in this regard and can house as many tables as you have space to hold them.
SQLite
There are certain features that SQLite handles well, some that it handles - but not well, and some that it does not handle at all. Relationships are one of those things that were left out of some versions of Android's SQLite, because it shipped without foreign key support. This was a highly requested feature and it was added in SQLite 3.6.22 which didn't ship until Android 2.2. There are still many reported bugs with it, however, in its earliest incarnations.
Android pre 2.2
Thankfully being SQL compliant and a simple DBMS (not RDBMS at this time), there are some easy ways to work around this, after all, a foreign key is just a field in another table.
You can enforce database INSERT and UPDATE statements by creating CONSTRAINTs when you use your CREATE TABLE statement.
You can query the other table for the appropriate _id to get your foreign key.
You can query your source table with any appropriate SELECT statement using an INNER JOIN, thus enforcing a pseudo-relationship.
Since Android's version of SQLite does not enforce relationships directly, if you wanted to CASCADE ON DELETE you would have to do it manually. But this can be done via another simple SQL statement. I have essentially written my own library to enforce these kinds of relationships, as it all must be done manually. I must say, however, the efficiency of SQLite and SQL as a whole makes this very quick and easy.
In essence, the process for any enforced relationship goes as follows:
In a query that requires a foreign key, use a JOIN.
In an INSERT use a CONSTRAINT on the foreign key field of NOT NULL
In an UPDATE on the primary key field that is a foreign key in another TABLE, run a second UPDATE on the related TABLE that has the foreign key. (CASCADE UPDATE)
For a DELETE with the same parameters, do another DELETE with the where being foreign_key = _id (make sure you get the _id before you DELETE the row, first).
Android 2.2+
Foreign keys is supported, but is off by default. First you have to turn them on:
db.execSQL("PRAGMA foreign_keys=ON;");
Next you have to create the relationship TRIGGER. This is done when you create the TABLE, rather than a separate TRIGGER statement. See below:
// Added at the end of CREATE TABLE statement in the MANY table
FOREIGN KEY(foreign_key_name) REFERENCES one_table_name(primary_key_name)
For further information on SQLite and its capabilities, check out SQLite official site. This is important as you don't have all of the JOINs that you do in other RDBMS. For specific information on the SQLite classes in Android, read the documentation.
As for first question: you don't need to create content provider for every table. You can use in with multiple tables, but the complexity of provider increased with each table.
A Content Provider is roughly equivalent to the concept of a database. You'd have multiple tables in a database, so having multiple tables in your content provider makes perfect sense.
One to many relationships can be handled just like in any other database. Use references and foreign keys like you would with any other database. You can use things like CASCADE ON DELETE to make sure records are deleted when the records they reference in other tables are also deleted.