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
Related
In my Android application I use Room library for persistency.
Assuming, I have an Entity defined like this:
#Entity(tableName = "my_entity")
public class MyEntity {
#ColumnInfo(name = "id")
#PrimaryKey(autoGenerate = true)
private int id;
//...
}
can I rely on the fact, that id will be increased monotonically, i.e. that for newly inserted row id will always be higher, than for all previously created rows?
I think, that it is unlikely, but I can imagine, that Room (or SQLite - I am not sure, who is responsible in this case) could e.g. try to reuse the IDs of the previously deleted rows...
As far as I can see, the official documentation does not tell anything about it PrimaryKey.AutoGenerate().
This answer is the expanded comment from JensV.
As suggested by JensV, the generated schema json file contains (among others):
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ... <other fields>)"
So looking at the SQLite docs of AUTOINCREMENT we get, that it is guaranteed to be monotonic.
In fact, this flag serves exactly for this purpose: to ensure, that the generated value is monotonic (without this flag, the value still will be generated to be unique, but will not be necessarily monotonic). Taking into account, that Room uses the flag, it is strange, that they don't mention it in the documentation.
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.
I used to create a column called 'id' which is auto incremented in any table I create. While in some cases I found that this column is useless while another column like 'citizen_ssn' is a better candidate to be the primary key.
So, what is the best practice in choosing the table's primary key ?
Should I use what will fit the need or create the auto-increamented column or another opinion ?
Any attribute which is unique and not null can be used as a primary key. Generally id is used as a primary key by many developers. But there's no such rule except mentioned above.
What is a Primary Key?
Apart from Auto-Incrementing it, a primary key is used to Identify a row. As every row is unique, so is the Primary key. Two or more rows with a column number n can have the same value, let's say "Australia" but the column with Primary Key constraint will have the unique value.
So, it's not important to use the id column as a primary key.
One can use phoneNumber as a PK too.
Depends on the usage.
I hope I've made myself clear.
In Realm there is no real reason to have an auto-increment primary key. (although if your server returns primary key info, then it's useful to know that you're referring to the same object).
In SQLite (or any relational database) the reason you need them is that that is the only way you can link two objects together (through JOINs) - for example, a many-many relationship is done with a Join Table that contains the primary key of both linked tables as a foreign key.
In Realm, you can link objects together as a field like private OtherObject otherObject;, and many relation as private RealmList<OtherObject> otherObjects. So you don't need primary key to create relationship.
What you might need them for in Realm is if you want to use insertOrUpdate() method to update an existing object in Realm by its primary key, overwriting it with an unmanaged object. You can also edit objects by calling the managed object's setters, so this is not entirely necessary.
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.
I have a table with a composite primary key and I am having trouble inserting. The code used to create the table is:
CREATE TABLE ClassEvent (
EventName varchar(10) NOT NULL,
CourseId varchar(10) NOT NULL,
EventType varchar(20),
EventWeight number(3),
DueDate DATE NOT NULL,
FOREIGN KEY (CourseId) REFERENCES Courses(CourseId),
PRIMARY KEY (CourseId, EventName));
The problem I am having is when I want to insert records that have values that may not be unique for the columns CourseId or EventName, but are a unique combination of the 2.
for example, if I try to run the following 2 inserts:
INSERT INTO ClassEvent VALUES('Assignment 1','60-415','Assignment',10,'12/10/2010');
INSERT INTO ClassEvent VALUES('Project 1','60-415','Project',15,'5/12/2010');
I get the following error:
Error: columns CourseId, EventName are not unique.
and the second insert does not make it into the DB. Why does this error out? I thought that a composite primary key requires that the combination of both values are unique. In my above inserts, the values for the EventName column are different even though the values for CourseId are the same. Shouldn't this be seen as 2 unique combinations and thus 2 different primary keys?
My table needs to be able to hold several different events for each CourseId, but each Event must be unique for each Course. I need to be able to insert values into the table like:
EventName CourseId
Assignment 1 60-415
Project 1 60-415
Assignment2 60-415
Project 2 60-415
Assignment 1 60-367
Project 1 60-367
and so on. Can anyone tell me how I can get this to work? Why are these composite PK's not being seen as unique entries? Any help would be much appreciated.
Here is the java function I am using for the insert:
public void addNewClassEvent(ContentValues values) {
SQLiteDatabase db = openConnection();
db.insert("ClassEvent", null, values);
db.close();
}
Could this be causing the problem?
You can have a composite primary key in SQLite, but you
have to create the key when you create the table:
CREATE TABLE example1(
field1 FLOAT,
field2 TEXT,
PRIMARY KEY(field1, field2)
);
You cannot create the primary key after the fact using ALTER TABLE.
On the other hand, you can create a UNIQUE INDEX after the fact
which has essentially the same effect as a PRIMARY KEY:
CREATE UNIQUE INDEX pk_index ON "table1"("field1","field2");
I am not sure how you have created, the tables, and if you have added the primary index later, but grab the database to your desktop, and check out how works in a desktop environment.
You can't make combinations like that, but you don't need them. What is stopping you from just having a truly id column ?