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";
}
Related
I have a table in MySql and I named it FAQs and inside the table, There are two columns, Question column and Answer column, I want to get the data who inside the FAQs table and store it in the offline database but I got this message An entity must have at least 1 field annotated with #PrimaryKey
The Table
#Entity(tableName = "FAQs")
public class FAQModel {
private String question, answer;
public String getQuestion() {
return question;
}
public String getAnswer() {
return answer;
}
}
Is it possible to create a table in the Room without a primary key?
Yes you can, with some difficulty, but not via room annotation, and even still it would have a primary key so really the answer is No.
It is possible (e.g. via a callback) to create a table that does not appear to have a primary key column e.g. CREATE TABLE IF NOT EXISTS the_table (question TEXT, answer TEXT). However,
it would have a primary key on the column rowid which is normally hidden.
such a table would not be able to be readily used, as you would have to avoid Room's compilation time SQL statement checking.
you could also not take direct advantage of Room's underlying table to/from object handling.
However, you make the comment
But in the android app I only get the question and answer column and I am not getting faqId because I don't want to use it inside my app.
So you could have a POJO class that excludes the faqId column e.g.
class FAQModelLessIdColumn {
String question,answer;
}
Assuming that FAQModel is the entity and thus the tablename then with the above you could have an #Query such as:-
#Query("SELECT * FROM faqmodel")
abstract List<FAQModelLessIdColumn> getFAQModelsLessTheFaqIdColumn();
However, over time, you will probably learn that the primary key column, is highly beneficial as it MUST uniquely identify a single row in the table. So from a single value you can perform CRUD operations efficiently.
As an analogy consider the table as a list where the questions have been written down in no particular order. To find a question and it's answer How many days in a leap year then you would have to search the entire list until you found the question. If however the list was split into lists according to the first character of the question and these were maintained in order then by skipping to the H list would likely make finding the question easier. Thus indexes can greatly reduce search times. So having the faqId available enables searches via the primary index and they will be faster.
Why do CursorAdapter subclasses requires the primary key to be necessarily _id ?
Isn't there a method to override, or something like that, to change this behaviour ?
I have read this trick many times, and I am aware of that ... I just want to understand better !
Thanks
Why does CursorAdapter subclasses requires the primary key to be necessarily _id ?
It turns around and provides that value in various places, such as the long id value in getView().
Isn't there a method to override, or something like that, to change this behaviour ?
No, sorry. If you do not have a suitable column, just add ROWID AS _ID to your list of columns to return in rawQuery():
SELECT ROWID AS _id, foo, bar FROM really_important_table ORDER BY foo;
ROWID is automatically added to all SQLite tables by default, and is a unique integer, which fits the _id requirements nicely.
This is the query that I use to create a table
create table site_table(
_id integer primary key autoincrement,
name_site text,
url text,
login text,
pass text
);
I called Cursor.getColumnNames() and noticed that columns order are id, login, pass, name, url.
So, if I want a value I have to get it by the index Cursor.getString(index). Until I debugged I was messing up calling the wrong index, but now I wonder, why SQLite saves that way? Why it does not follow that way I created id, name_site, url, login and pass?
Thanks
So, if I want a value I have to get it by the index
Cursor.getString(index)
So for example for this reason you should always use
c.getString(c.getColumnIndex("ColName")); // or better getColumnIndex(CONSTANT)
This method saves all of us and ensure that you never get wrong results. Generally this method is recommended and also storing COLUMN_NAMES as CONSTANTS in separated class is very, very useful and efficient practise.
Note: Order depends on projection i.e. select name, lastname from table
That data is ordered by the order your requested it in your query, not the order you created the table with. So you probably changed the order in your query that generated said cursor.
Columns order in your cursor depends on projection. To be sure you use correct column index use c.getString(c.getColumnIndexOrThrow("COLUMN_NAME")) where c is your cursor.
I just made the experience first hand:
The indices of the columns of the cursor as a result of a
SELECT * FROM mytable WHERE ...
query have sometimes (not always) a different order that what SQLITE Database Browser shows as column order in the Database Structure tab. So referencing the columns via getColumnIndex seems to be the only safe way.
I am using a SQLite database to store data that can be used to reconstruct some objects that I am using in the application I am developing. I am storing CheckIns, Recipients, and ContactMethods.
These objects are related as follows:
CheckIn <--many -- to -- many--> Recipient
Recipient <--one -- to -- many--> ContactMethod
In Java, these objects' fields are defined as follows:
public class CheckIn {
private int id;
private boolean isEnabled;
private Date startTime;
private Repetition repetition;
private Set<Recipient> recipients;
}
public class Recipient {
private String name;
private Set<ContactMethod> contactMethods;
}
public class ContactMethod {
private String type;
private String address;
}
The database schema I have come up with for these objects is defined as follows:
CREATE TABLE checkIn(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
isEnabled INTEGER,
startTime INTEGER,
repetitionNum INTEGER,
repetitionUnits TEXT
);
CREATE TABLE recipient(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
);
CREATE TABLE contactMethod(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT,
address TEXT,
recipientID INTEGER,
FOREIGN KEY(recipientID) REFERENCES recipient(_id) ON DELETE CASCADE
);
CREATE TABLE checkIn_recipient(
checkInID INTEGER,
recipientID INTEGER,
FOREIGN KEY(checkInID) REFERENCES checkIn(_id),
FOREIGN KEY(recipientID) REFERENCES recipient(_id)
);
I have two questions.
1. How do I efficiently INSERT a CheckIn to the database, along with its related objects?
To be more specific, if I have a CheckIn object in Java, not yet committed to the database, how can I structure an INSERT statement that will insert the CheckIn to the CheckIn table, but also store the new CheckIn's relation to one or more Recipients? It seems like to store the relation to the Recipients, I would need to already know the checkIn._id, which hasn't been set yet, since the CheckIn hasn't been entered into the database yet.
2. In Android, what is the best way to rebuild a CheckIn object, for example, from a database query?
I think I know the SQL query that I will need to get the right data:
SELECT checkIn.*, recipient.name, contactMethod.type, contactMethod.address
FROM checkIn
JOIN checkIn_recipient
ON (checkIn._id = checkIn_recipient.checkInID)
JOIN recipient
ON (checkIn_recipient.recipientID = recipient._id)
JOIN contactMethod
ON (recipient._id = contactMethod.recipientID)
This query will get me rows of data containing all of the information I need to build an object for every CheckIn and Recipient in the database, and I know how to get a Cursor object that will iterate through these rows. However, since the data required for a single CheckIn appears on multiple rows, I am confused about the best way to construct individual CheckIn objects. If I am trying to write a method like public Set<CheckIn> getAllCheckIns() that will return a set of all CheckIns that are stored in the database, do I need to run the query above, then loop through each row with the same checkIn._id, and within that loop, every row with the same recipient._id, and so forth? Is there any better way to do this?
I am sorry for the long question. I have only been working with SQL beyond single table databases since this morning, and I didn't want to leave out any important information. Let me know if I missed something.
Answer to Question 1: There are 2 possible ways.
a. You find the ID of the inserted row and use that to insert into the 2nd table. You can find the ID of inserted row if you are using the Android Insert method as documented here:
http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#insert%28java.lang.String,%20java.lang.String,%20android.content.ContentValues%29
Here you must ensure that all DB tables are committed or none.
b. You can create triggers. Triggers are database operations that are automatically performed when a specified database event occurs.
Learn how to create triggers: http://www.sqlite.org/lang_createtrigger.html
So for e.g. you can create a AFTER INSERT trigger that will be fired when a row in inserted in check in table. Then in that trigger you can insert a row into another table.
When the trigger is fired, you have access to the newly inserted row.
Answer to question 2:
I usually do the following :
Select from table 1 - Check In table
Iterate over the cursor and prepare the Check In object.
Within the loop, select from table 2 - Recipient table
Iterate over the recipient table and prepare the Check in object.
This would involve too many DB selects.
Alternatively, you could select all data once and then iterate to prepare the objects.
The point I am trying to make is that you have to iterate :D
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.