Contacts are saved multiple times in Database - android

Hi guys i have code to add data into Database and that code is called on oncreate. but every time fragment is created its saves data into database again and again. I dont want that. how can i prevent that here is my code to get data and save it to database.
public void getcontacts(){
Cursor phones;
phones = getActivity().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null, null);
while (phones.moveToNext()){
String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
DataBaseOperations hell = new DataBaseOperations(getActivity());
SQLiteDatabase db = hell.getWritableDatabase();
hell.SaveContacts(name, phoneNumber, db);
}
phones.close();
}
here is code for saving in database.
public void SaveContacts(String Name,String phone,SQLiteDatabase db ){
ContentValues sv = new ContentValues();
sv.put(mDatabase.Tableinfo.Contacts_name, Name);
sv.put(mDatabase.Tableinfo.Contacts_phone, phone);
db.insert(mDatabase.Tableinfo.contacts, null, sv);
}
EDIT- Here is what i have done so far but how do i set a constraint on which it will conflict.I have added an auto increment primary key as unique key but how will they conflict?? but still its saving values again in database.where do i set a primery key as constraint?????
public void SaveContacts(String Name,String phone,SQLiteDatabase db ){
ContentValues sv = new ContentValues();
sv.put(mDatabase.Tableinfo.Contacts_name, Name);
sv.put(mDatabase.Tableinfo.Contacts_phone, phone);
db.insertWithOnConflict(mDatabase.Tableinfo.contacts, null, sv, SQLiteDatabase.CONFLICT_REPLACE);
EDIT- 2 - how would i make unique columns in this kind of query???
private static final String Contacts_Table = "CREATE TABLE "+
mDatabase.Tableinfo.contacts +"("
+mDatabase.Tableinfo.Contacts_name+" TEXT,"
+mDatabase.Tableinfo.Contacts_phone+" TEXT NOT NULL UNIQUE,"
+mDatabase.Tableinfo.isChattris+" TEXT,"
+mDatabase.Tableinfo.status_contact+" TEXT,"
+mDatabase.Tableinfo.Contact_pic+" BLOB"+")";
just Added NOT NULL UNIQUE.
This is the constraint.

Keep the status of the contacts saving in unrelated storage, e.g SharedPreferences. You don't want to rely on any lifecycle triggers, not even Application.onCreate, since that will happen again and again when the app is launched.
Try to update existing entries instead of adding new ones. Decide what the database key should be, and then you can use insertWithOnConflict with the CONFLICT_REPLACE flag to insert if not already in the table or update if it is.
EDIT: to define the constraints so the conflict behavior will trigger, change your CREATE TABLE statement. e.g this will cause duplicate (name,phone) pairs to trigger it. You may want to use some kind of contact ID instead though.
CREATE TABLE mytable (
name TEXT,
phone TEXT,
UNIQUE(name, phone)
)

Related

Dynamic table creation on button click in SQLite

I am creating a login application and I wanted to create a table for each user when they click the register button. The problem is, I am using their unique email id as table name. Later I learned that table names cannot have special characters like "#". Please suggest me a way to create table with names having special characters like "#". Or else, please suggest me a way to create tables with unique names for each user when they click the register button.
Please Note :"TABLE_NAME" in the code given below is another table containing registered user details.
The code is given below:
public boolean insertData(String name, String email, String mobile, String pass){
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("create table "+email+"(Choice TEXT, Q1 INTEGER, Q2 INTEGER, Q3 INTEGER, Q4 INTEGER, Q5 INTEGER)");
ContentValues cv = new ContentValues();
cv.put(NAME, name);
cv.put(EMAIL, email);
cv.put(MOBILE, mobile);
cv.put(PASSWORD, pass);
long result = db.insert(TABLE_NAME,null,cv);
db.close();
if(result == -1)
return false;
else
return true;
}
In SQL, identifiers can be quoted with double quotes. Any double quotes inside the identifier must be doubled to escape them; and in Java strings, double quotes must be escaped with a backslash:
String sqlTableName = "\"" + email.replace("\"", "\"\"") + "\"";
db.execSql("CREATE TABLE " + sqlTableName + "...");
However, putting data into the table name is a bad idea, because it cannot be queried and modified like all your other data.
Better use a single table, and put that information into another column:
CREATE TABLE Users(EMail TEXT, Choice TEXT, Q...);

Delete specific record in sqlite table based on two criteria: _id and column

I have created a sqlite table for my android app, this table has 5 columns and multiple rows, the columns being: _id, column1, column2, column3, column4.
I want to delete a specific record, for instance the record stored in column3 corresponding to _id (in a different class are the getters and setters, for this I've named the class "TableHandler")
I guess that I'm a bit confused, following is what I was planning, but for column3 I'm not sure what should be the argument, I just want to delete whatever is in that column position corresponding to _id
public void deleteValueColumn3(TableHandler value){
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, KEY_ID + " = ? AND " + KEY_COLUMN3 + " = ?",
new String[] {String.valueOf(value.getID()), ?????????);
db.close();
}
The ???????? is that I'm stuck there, maybe the whole method needs to be rewritten, I would appreciate your input.
Thanks
If you want to delete the whole record, just use the _id of the record in delete method, because that is the primary key for your table and therefore is unique. If you'd rather keep the record, you con always use the SQLiteDatabase.update method, specifying null as the new value that will replace column3 value; check out that column3 declaration has no NOT NULL tag, otherwise that could easily throw exception at you.
SQLite does not allow you to delete columns for a specific row.
You can only delete ROWS of data (delete the row that has the column _ID = 1).
Here's a quick tutorial on SQL.
How about updating that column with a null value, rather than using delete()?
ContentValues cv = new ContentValues();
cv.putNull(KEY_COLUMN3);
db.getWritableDatabase().update(
TABLE_NAME,
cv,
KEY_ID + "=?",
new String[]{String.valueOf(keyIdValue)});

SQLite Database Error(Glitch in bridge between two tables)

I have two tables as USER_TABLE where i have all the user details as username,firstname etc and similarly i have another table named RESUME_TABLE where i have aim,degree,university etc.
The columns common to both are KEY_USERID(created dynamically) and i use this id to match the two tables.
Question :
I register a user and his details gets stored in the USER_TABLE and say his KEY_USERID automatically generated(primary key) is 1 and then i create a resume for him in the RESUME_TABLE using the same KEY_USERID 1. I pass on the KEY_USERID obtained from the first table as a argument while inserting and retrieving the resume contents from the RESUME_TABLE.
Say, i do this again for another user thereby generating KEY_USERID 2 but i fill only the
USER_TABLE and the RESUME_TABLE IS EMPTY.
When i repeat this again filling both the tables, then the RESUME_TABLE is not able to fetch the data cause the second user did not fill the details in RESUME_TABLE and that acts as a glitch while retrieving the KEY_USERID for the 3rd user.
How do i rectify this ?
CODE :
DBAdapter.java :
public String get_user_id(String name) {
String[] column = new String[]{KEY_USERID};
String where = KEY_USERNAME + "=?";
Cursor c = mDB.query(DATABASE_TABLE, column, where,new String[]{""+ name + ""}, null, null,null);
String user_id = "";
int iuser_id = c.getColumnIndex(KEY_USERID);
if (c != null)
c.moveToFirst();
user_id = user_id + c.getString(iuser_id) + "\t";
c.close();
return user_id;
}
ViewResume.java
if(get_from_create!=null)
{
if(get_from_create.hasExtra("UserId_Create"))
{
String user_id_create = get_from_create.getExtras().getString("UserId_Create"); // This is where i get the user id from the first table which is created dynamically.
dbAdapter.open();
Log.i("In_View Resume_from_create",user_id_create);
objective.setText(dbAdapter.getObjective(user_id_create));
degree.setText(dbAdapter.getDegree(user_id_create));
passed_out.setText(dbAdapter.getPassedOut(user_id_create));
university.setText(dbAdapter.getUniversity(user_id_create));
field.setText(dbAdapter.getField(user_id_create));
years_of_experience.setText(dbAdapter.getyears_of_experience(user_id_create));
areas_of_interest.setText(dbAdapter.getareas_of_interest(user_id_create));
dbAdapter.close();
}
NOTE:
KEY_USERID is auto incremented only for USER_TABLE and not for RESUME_TABLE.
You can't have two automatic keys referring same elements on different tables.
You should only have automatic primary key in USER_TABLE. KEY_USERID can not be automatic, it must alwys be matched with USER_TABLE.KEY_USERID - effectively, a foreign key.
Furthermore, to understand why you are getting wrong id in code, you must post get_from_create function.

Android/SQLite: Insert-Update table columns to keep the identifier

Currently, I am using the following statement to create a table in an SQLite database on an Android device.
CREATE TABLE IF NOT EXISTS 'locations' (
'_id' INTEGER PRIMARY KEY AUTOINCREMENT, 'name' TEXT,
'latitude' REAL, 'longitude' REAL,
UNIQUE ( 'latitude', 'longitude' )
ON CONFLICT REPLACE );
The conflict-clause at the end causes that rows are dropped when new inserts are done that come with the same coordinates. The SQLite documentation contains further information about the conflict-clause.
Instead, I would like to keep the former rows and just update their columns. What is the most efficient way to do this in a Android/SQLite environment?
As a conflict-clause in the CREATE TABLE statement.
As an INSERT trigger.
As a conditional clause in the ContentProvider#insert method.
... any better you can think off
I would think it is more performant to handle such conflicts within the database. Also, I find it hard to rewrite the ContentProvider#insert method to consider the insert-update scenario. Here is code of the insert method:
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long id = db.insert(DatabaseProperties.TABLE_NAME, null, values);
return ContentUris.withAppendedId(uri, id);
}
When data arrives from the backend all I do is inserting the data as follows.
getContentResolver.insert(CustomContract.Locations.CONTENT_URI, contentValues);
I have problems figuring out how to apply an alternative call to ContentProvider#update here. Additionally, this is not my favored solution anyways.
Edit:
#CommonsWare: I tried to implement your suggestion to use INSERT OR REPLACE. I came up with this ugly piece of code.
private static long insertOrReplace(SQLiteDatabase db, ContentValues values, String tableName) {
final String COMMA_SPACE = ", ";
StringBuilder columnsBuilder = new StringBuilder();
StringBuilder placeholdersBuilder = new StringBuilder();
List<Object> pureValues = new ArrayList<Object>(values.size());
Iterator<Entry<String, Object>> iterator = values.valueSet().iterator();
while (iterator.hasNext()) {
Entry<String, Object> pair = iterator.next();
String column = pair.getKey();
columnsBuilder.append(column).append(COMMA_SPACE);
placeholdersBuilder.append("?").append(COMMA_SPACE);
Object value = pair.getValue();
pureValues.add(value);
}
final String columns = columnsBuilder.substring(0, columnsBuilder.length() - COMMA_SPACE.length());
final String placeholders = placeholderBuilder.substring(0, placeholdersBuilder.length() - COMMA_SPACE.length());
db.execSQL("INSERT OR REPLACE INTO " + tableName + "(" + columns + ") VALUES (" + placeholders + ")", pureValues.toArray());
// The last insert id retrieved here is not safe. Some other inserts can happen inbetween.
Cursor cursor = db.rawQuery("SELECT * from SQLITE_SEQUENCE;", null);
long lastId = INVALID_LAST_ID;
if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {
lastId = cursor.getLong(cursor.getColumnIndex("seq"));
}
cursor.close();
return lastId;
}
When I check the SQLite database, however, equal columns are still removed and inserted with new ids. I do not understand why this happens and thought the reason is my conflict-clause. But the documentation states the opposite.
The algorithm specified in the OR clause of an INSERT or UPDATE
overrides any algorithm specified in a CREATE TABLE. If no algorithm
is specified anywhere, the ABORT algorithm is used.
Another disadvantage of this attempt is that you loose the value of the id which is return by an insert statement. To compensate this, I finally found an option to ask for the last_insert_rowid. It is as explained in the posts of dtmilano and swiz. I am, however, not sure if this is safe since another insert can happen inbetween.
I can understand the perceived notion that it is best for performance to do all this logic in SQL, but perhaps the simplest (least code) solution is the best one in this case? Why not attempt the update first, and then use insertWithOnConflict() with CONFLICT_IGNORE to do the insert (if necessary) and get the row id you need:
public Uri insert(Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String selection = "latitude=? AND longitude=?";
String[] selectionArgs = new String[] {values.getAsString("latitude"),
values.getAsString("longitude")};
//Do an update if the constraints match
db.update(DatabaseProperties.TABLE_NAME, values, selection, null);
//This will return the id of the newly inserted row if no conflict
//It will also return the offending row without modifying it if in conflict
long id = db.insertWithOnConflict(DatabaseProperties.TABLE_NAME, null, values, CONFLICT_IGNORE);
return ContentUris.withAppendedId(uri, id);
}
A simpler solution would be to check the return value of update() and only do the insert if the affected count was zero, but then there would be a case where you could not obtain the id of the existing row without an additional select. This form of insert will always return to you the correct id to pass back in the Uri, and won't modify the database more than necessary.
If you want to do a large number of these at once, you might look at the bulkInsert() method on your provider, where you can run multiple inserts inside a single transaction. In this case, since you don't need to return the id of the updated record, the "simpler" solution should work just fine:
public int bulkInsert(Uri uri, ContentValues[] values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String selection = "latitude=? AND longitude=?";
String[] selectionArgs = null;
int rowsAdded = 0;
long rowId;
db.beginTransaction();
try {
for (ContentValues cv : values) {
selectionArgs = new String[] {cv.getAsString("latitude"),
cv.getAsString("longitude")};
int affected = db.update(DatabaseProperties.TABLE_NAME,
cv, selection, selectionArgs);
if (affected == 0) {
rowId = db.insert(DatabaseProperties.TABLE_NAME, null, cv);
if (rowId > 0) rowsAdded++;
}
}
db.setTransactionSuccessful();
} catch (SQLException ex) {
Log.w(TAG, ex);
} finally {
db.endTransaction();
}
return rowsAdded;
}
In truth, the transaction code is what makes things faster by minimizing the number of times the database memory is written to the file, bulkInsert() just allows multiple ContentValues to be passed in with a single call to the provider.
One solution is to create a view for the locations table with a INSTEAD OF trigger on the view, then insert into the view. Here's what that would look like:
View:
CREATE VIEW locations_view AS SELECT * FROM locations;
Trigger:
CREATE TRIGGER update_location INSTEAD OF INSERT ON locations_view FOR EACH ROW
BEGIN
INSERT OR REPLACE INTO locations (_id, name, latitude, longitude) VALUES (
COALESCE(NEW._id,
(SELECT _id FROM locations WHERE latitude = NEW.latitude AND longitude = NEW.longitude)),
NEW.name,
NEW.latitude,
NEW.longitude
);
END;
Instead of inserting into the locations table, you insert into the locations_view view. The trigger will take care of providing the correct _id value by using the sub-select. If, for some reason, the insert already contains an _id the COALESCE will keep it and override an existing one in the table.
You'll probably want to check how much the sub-select affects performance and compare that to other possible changes you could make, but it does allow you keep this logic out of your code.
I tried some other solutions involving triggers on the table itself based on INSERT OR IGNORE, but it seems that BEFORE and AFTER triggers only trigger if it will actually insert into the table.
You might find this answer helpful, which is the basis for the trigger.
Edit: Due to BEFORE and AFTER triggers not firing when an insert is ignored (which could then have been updated instead), we need to rewrite the insert with an INSTEAD OF trigger. Unfortunately, those don't work with tables - we have to create a view to use it.
INSERT OR REPLACE works just like ON CONFLICT REPLACE. It will delete the row if the row with the unique column already exists and than it does an insert. It never does update.
I would recommend you stick with your current solution, you create table with ON CONFLICT clausule, but every time you insert a row and the constraint violation occurs, your new row will have new _id as origin row will be deleted.
Or you can create table without ON CONFLICT clausule and use INSERT OR REPLACE, you can use insertWithOnConflict() method for that, but it is available since API level 8, requires more coding and leads to the same solution as table with ON CONFLICT clausule.
If you still want to keep your origin row, it means you want to keep the same _id you will have to make two queries, first one for inserting a row, second to update a row if insertion failed (or vice versa). To preserve consistency, you have to execute queries in a transaction.
db.beginTransaction();
try {
long rowId = db.insert(table, null, values);
if (rowId == -1) {
// insertion failed
String whereClause = "latitude=? AND longitude=?";
String[] whereArgs = new String[] {values.getAsString("latitude"),
values.getAsString("longitude")};
db.update(table, values, whereClause, whereArgs);
// now you have to get rowId so you can return correct Uri from insert()
// method of your content provider, so another db.query() is required
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Use insertWithOnConflict and set the last parameter (conflictAlgorithm) to CONFLICT_REPLACE.
Read more at the following links:
insertWithOnConflict documentation
CONFLICT_REPLACE flag
for me, none of the approaches are work if I don't have "_id"
you should first call update, if the affected rows are zero, then insert it with ignore:
String selection = MessageDetailTable.SMS_ID+" =?";
String[] selectionArgs = new String[] { String.valueOf(md.getSmsId())};
int affectedRows = db.update(MessageDetailTable.TABLE_NAME, values, selection,selectionArgs);
if(affectedRows<=0) {
long id = db.insertWithOnConflict(MessageDetailTable.TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_IGNORE);
}
Use INSERT OR REPLACE.
This is the correct way to do it.

My Android application is crushing if the database is not populated

I got some problem with my database in my Android application. Perhaps, my application is working but let me explain you.
Here are just parts of the code from my app.. i don't want to paste the whole it's not needed.
private SQLiteStatement insertStmt;
private static final String INSERT = "insert into "
+ TABLE_NAME + "(name,surname) values (?,?)";
public DataHelper(Context context) {
this.context = context;
OpenHelper openHelper = new OpenHelper(this.context);
this.db = openHelper.getWritableDatabase();
this.insertStmt = this.db.compileStatement(INSERT);
}
public long insert(String name, String surname) {
this.insertStmt.bindString(1, name);
this.insertStmt.bindString(2, surname);
return this.insertStmt.executeInsert();
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME +
"(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, surname TEXT)");
}
So when i try to run my app i get Force close, but when i put manually :
dh.insert("test", "ooo");
..in the onCreate method in my main activity everything is fine and working.
So, the conclusion is that i must put some value for the first time i run the app so it can work properly. I thought maybe to update that row with the new informations that i insert later through some TextView's from the app but i'm wondering if there is smarter solution than this?
Obviously there is no error with your insert, at least it's not what crashes your App.
Since you're getting a CursorIndexOutOfBoundsException, it means you have somewhere a select query and try to move or set it before checking if it's has any rows at all.
This usually happens if you move the cursor to a non-existing index, i.e. cursor.moveToFirst() while your cursor is empty or cursor.move(n) where n is bigger than cursor.getCount()-1
Before moving or accessing a cursor you ALWAYS have to check if any rows are returned!
Cursor c = db.query(...);
if(c!=null && c.getCount()>0) {
c.moveToFirst(); // (or c.move(0))
// Do your cursor operations here!
}
Obviously your insert fails for some reason, you can easily check this, with:
long insertId = dh.insert("test", "ooo");
if(insertId > 0) {
// insert was successfull
} else {
// insert failed
}

Categories

Resources