I know if I don't use a field named _id as my primary key in Android, that certain things like the CursorAdapter won't work, but does the _id column need to be an autoincrement int?
Could I use a Guid as the key, as long as it's called _id, and have the CursorAdapter still work?
The yellow box in the storage guide says:
Android does not impose any
limitations beyond the standard SQLite
concepts. We do recommend including an
autoincrement value key field that can
be used as a unique ID to quickly find
a record. This is not required for
private data, but if you implement a
content provider, you must include a
unique ID using the BaseColumns._ID
constant.
Now when you click on the BaseColumns class you will see
public static final String _ID
The unique ID for a row.
Type: INTEGER (long)
Constant Value: "_id"
So I guess, a GUID will not work.
Related
My Room database has the following entity:
#Entity
public class SmsMessage {
#PrimaryKey
public long id;
public boolean incoming;
public String sender;
public String receiver;
public String body;
public long timestamp;
}
This fails with the following RuntimeException when trying to insert more than one item into the database:
SQLiteConstraintException: PRIMARY KEY must be unique (code 19)
The generated SQL CREATE TABLE statement looks like this:
CREATE TABLE `SmsMessage` (
`id` INTEGER NOT NULL,
`incoming` INTEGER NOT NULL,
`sender` TEXT,
`receiver` TEXT,
`body` TEXT,
`timestamp` INTEGER NOT NULL,
PRIMARY KEY(`id`)
);
This seems to be different from INTEGER NOT NULL PRIMARY KEY, even though I can't find any documentation for this behaviour in the SQLite documentation.
It seems I have to use #PrimaryKey (autogenerate=true) in order to make Room automatically generate primary key values. Looking at the generated database when using autogenerate=true, this generates the following SQL:
CREATE TABLE `SmsMessage` (
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`incoming` INTEGER NOT NULL,
`sender` TEXT,
`receiver` TEXT,
`body` TEXT,
`timestamp` INTEGER NOT NULL
);
It seems that autogenerate=true corresponds to SQLite AUTOINCREMENT. However, the SQLite documentation makes quite clear that AUTOINCREMENT isn't needed (and in most cases not recommended) in order to automatically generate unique primary keys. The purpose of AUTOINCREMENT is basically to prevent re-use of used but deleted primary keys.
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.
In SQLite, a column with type INTEGER PRIMARY KEY is an alias for the ROWID (except in WITHOUT ROWID tables) which is always a 64-bit signed integer.
On an INSERT, if the ROWID or INTEGER PRIMARY KEY column is not explicitly given a value, then it will be filled automatically with an unused integer, usually one more than the largest ROWID currently in use. This is true regardless of whether or not the AUTOINCREMENT keyword is used.
If the AUTOINCREMENT keyword appears after INTEGER PRIMARY KEY, that changes the automatic ROWID assignment algorithm to prevent the reuse of ROWIDs over the lifetime of the database. In other words, the purpose of AUTOINCREMENT is to prevent the reuse of ROWIDs from previously deleted rows.
So it seems that using #PrimaryKey(autogenerate=true) is usually not needed nor recommended. But only using #PrimaryKey alone will not automatically generate values at all.
How do I tell Room that what I want is 'id' INTEGER NOT NULL PRIMARY KEY?
That's not possible at the moment - the only option is AUTOINCREMENT. You can star the existing feature request for progress updates on support for this use case.
It is a bit confusing.... If I implement BaseColumns for each of my tables it automatically create an autoincremented primary key for me called _ID but then Do I need to create my own primary keys for each of my tables as well or is it redundant or even not necessary?
In case I need my own primary key, let's say, _MyID, I guess the primary key will be formed by _ID and my own (_MyID), right? so in this case, it would be possible to insert the more than one register with the same _MyID.... as _ID is autoincremented automatically, that is:
_ID _MyID Other Fields.....
1 1000 ....
2 1000 ....
3 1000 ....
... and so on
so in this case, how to control that only one register can have the value 1000 for _MyID?
Also, I guess I can use _ID column to act as a foreign key with other tables, right?
The main use for BaseColumns._ID is that Android's CursorAdapter will look for that column name in the cursor you give it. There may be other classes that do the same, but I can't think of any off the top of my head. If you aren't using CursorAdapter, then there's really nothing binding you to using _id as a column name in your table and you can name the column however you like.
it automatically create an autoincremented primary key for me called _ID
There is nothing automatic about it based on what you've shown so far. You will only have such a column if you executed SQL like this:
CREATE TABLE tableName (_id INTEGER PRIMARY KEY AUTOINCREMENT, ...);
You can just as easily leave it out or give a different name to the primary key. Furthermore, there is nothing that requires your primary key to be auto-incremented; as long as the values are unique, it satisfies the primary key requirement. In other words, this is fine too:
CREATE TABLE tableName (_id INTEGER PRIMARY KEY, ...);
In case I need my own primary key, let's say, _MyID, I guess the primary key will be formed by _ID and my own (_MyID), right?
Not quite. You would have to do something like this:
CREATE TABLE tableName (_id INTEGER, _myId INTEGER, ..., PRIMARY KEY(_id, myId));
This creates a composite key, but note that neither of the two columns are themselves declared as primary key. Honestly though, if you don't need such an arrangement, then stick to one primary key.
One last thing:
If you are planning to use CursorAdapter, you might want to name the column _id for convenience, but even then you don't have to. All that matters is the cursor has a column by that name. The actual column in the table can have a different name, you just have to alias it at query time so that it has the proper name in the cursor:
SELECT _myId as _id, ... FROM ...;
I read that activeandroid generates ids for every record inserted.
I want to retrieve records from latest created to earliest created. I know to use orderBy(COL_NAME, DESC), where COL_NAME is the primary key column, but what is that column name?
I know I can create a pseudo primary key:
#Column(name = "id", unique = true, onUniqueConflict = Column.ConflictAction.REPLACE)
public long id;
and do
orderBy("id DESC").execute()
but I feel it's wasteful when I could just use the real primary key
From the Active Android Github Side:
One important thing to note is that ActiveAndroid creates an id field for your tables. This field is an auto-incrementing primary key.
In your case you could delete your #Column and it will work fine.
Source:
https://github.com/pardom/ActiveAndroid/wiki/Creating-your-database-model
Can anyone tell me how to declare a composite primary key in Android 1.6 which includes an autoincrement _id column? I'm not sure of the syntax. I've ended up just enforcing it in Java when I try to add values (where registrationNumber + date has to be unique in the table):
Cursor fuelUpsCursor = getFuelUps(registrationNumber, date);
if(!fuelUpsCursor.moveToNext())
{
//add registrationNumber and date
}
I don't really need the _id column but it can make life tricky if tables don't have one.
Cheers,
Barry
Your question does not make much sense. Your subject line asks for a "composite foreign key", your first sentence asks for a "composite primary key" with an AUTOINCREMENT that your sample code then ignores.
I am going to interpret your question this way: You want an _ID INTEGER PRIMARY KEY AUTOINCREMENT column in your table to be able to use Android's CursorAdapter, but you want to also make sure that the combination of two other columns is unique.
In that case, I think that you want to use a UNIQUE constraint:
Multiple Unique Columns in SQLite
SQLite table constraint - unique on multiple columns
http://sqlite.org/lang_createtable.html
Is the field "_id" necessary in Android SQLite?
_id is useful when you are using the enhanced Adapters which make use of a Cursor (e.g. ResourceCursorAdapter). It's used by these adapters to provide an ID which can be used to refer to the specific row in the table which relates the the item in whatever the adapter is being used for (e.g. a row in a ListView).
It's not necessary if you're not going to be using classes which need an _id column in a cursor, and you can also use "as _id" to make another column appear as though it's called _id in your cursor.
Why not make use of _ROWID_?
SQLite provides this anyway for every row, so you can just alias it to _id in your select statement.
Technically no the field _id is not required, however if you are making use of the CursorAdapter class (which you probably are, especially if you are working with the Notepad example) then yes
"The Cursor must include a column named "_id" or this class will not
work"
as explained in the documentation here. Unfortunately the code examples do not make this very clear.
It's quite convenient in many cases to have an id field. I prefer mine to be auto-incrementing (as shown below). I'm always finding new uses for the id field :)
When it comes time to attach the data to an adapter, I like to use a table name alias to query the id field as _id. Example: SELECT id _id, msg from message order by id. That way the adapter sees a field called _id and everybody's happy.
Here's a sample of how I define my tables:
CREATE TABLE message (_id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, tripID TEXT, msg TEXT);
From the official docs...
The Cursor must include a column named "_id" or this class will not work. Additionally, using MergeCursor with this class will not work if the merged Cursors have overlapping values in their "_id" columns.
And the Cursor is:
This interface provides random read-write access to the result set returned by a database query.
In other words, you need _id for Android SQLite ( which usually uses Cursor )
If you define your _id column as an autoincrementing integer it is actually an alias for the ROWID column that SQLite provides by default (https://www.sqlite.org/lang_createtable.html#rowid).
Your create statement needs take the form...
CREATE TABLE t(_id INTEGER PRIMARY KEY ASC, y, z);
To prove this works...
UPDATE t SET _id=22 WHERE _id=11;
then
SELECT ROWID, _id FROM t;
and you'll find both _id and ROWID have the same value.
Note, that if you use DESC in the CREATE a new column is created and ROWID is not aliased.
Surely not.
Its a convenience field that some widgets like ListView uses to populate data. See this good article:
http://www.casarini.org/blog/2009/android-contentprovider-on-sqlite-tables-without-the-_id-column/
Of course if you are creating your own UI widget and your own adapter, you don't have to name your primary key as "_id". It can be any name you want. But you would be responsible for managing your collections of UI widgets and binding them to the right row in your database. "_id" is only useful for ListView as Brad has pointed out.
The _id field is indeed necessary in sqlite, it will help you to select a particular data from sqlite.
SELECT name from table_name where _id = ?
And if your are creating a recyclerview/ listview and you want a detailed activity for that list item you indeed need an id for this to fetch data of that item.
if you are creating a class for constants there is a BaseColumn interface in android,
which provide _ID field to that constant class.
//from android documentation..
public static class FeedEntry implements BaseColumns {
public static final String TABLE_NAME = "entry";
public static final String COLUMN_NAME_TITLE = "title";
public static final String COLUMN_NAME_SUBTITLE = "subtitle";
}