sqlite database inserts data in the wrong order sometimes - java

I'm working with android studio and I need to insert some data into a database. Sometimes, it works, but sometimes, I get this error. I can't really figure out the difference between the times it works and the times it doesn't.
2019-11-09 11:38:34.912 4369-4369/com.example.android.fresh
D/DatabaseHelper: addData: Adding John Smith to people_table
2019-11-09 11:38:34.913 4369-4369/com.example.android.fresh E/SQLiteLog:
(20) statement aborts at 5: [INSERT INTO
people_table(name,amount,contact_no) VALUES (?,?,?)] datatype mismatch
2019-11-09 11:38:34.916 4369-4369/com.example.android.fresh
E/SQLiteDatabase: Error inserting name=John Smith amount=255.0 contact_no= 1234567890 from {P:4369;U:10178}
android.database.sqlite.SQLiteDatatypeMismatchException: datatype mismatch (code 20 SQLITE_MISMATCH)
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:796)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1613)
at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1482)
at com.example.android.fresh.DatabaseHelper.addData(DatabaseHelper.java:51)
at com.example.android.fresh.createPerson.AddData(createPerson.java:94)
at com.example.android.fresh.createPerson$1.onClick(createPerson.java:58)
at android.view.View.performClick(View.java:6669)
at android.view.View.performClickInternal(View.java:6638)
at android.view.View.access$3100(View.java:789)
at android.view.View$PerformClick.run(View.java:26145)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
The relevant code in my database helper class is as follows:
private static final String COL1 = "contact_no";
private static final String COL2 = "name";
private static final String COL3 = "amount";
#Override
public void onCreate(SQLiteDatabase db) {
String createTable = "CREATE TABLE " + TABLE_NAME + " (ID INTEGER PRIMARY KEY AUTOINCREMENT,"
+ COL1 + " TEXT NOT NULL," +
COL2 + " TEXT NOT NULL,"
+ COL3 + " REAL)" ;
db.execSQL(createTable);
public boolean addData(String contactNo, String contactName, double amount) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(COL1, contactNo);
contentValues.put(COL2, contactName);
contentValues.put(COL3, amount);
Log.d(TAG, "addData: Adding " + contactName + " to " + TABLE_NAME);
long result = db.insert(TABLE_NAME, null, contentValues);
if (result == -1) {
return false;
} else {
return true;
}
}
This is the code in the createPerson class:
public void AddData(String contactNo, String contactName, double amount) {
boolean insertData = mDatabaseHelper.addData(contactNo, contactName, amount);
if (insertData) {
check =1;
toastMessage("Contact Inserted");
} else {
toastMessage("Something went wrong");
}
}
And this is the method call statement:
AddData(contactNumber, contactName, amount);
Each one of these is in the order contactNumber, contactName, and amount, but the sqlite error i' m getting is inserting it in the order contactName, amount, and contactNumber. I really can't understand how this is happening.

There is only 1 situation where when a data type mismatch will occur, due to SQLite being able to store any type of data in any type of column (with 1 exception and thus the cause/issue).
you wish to have a read of Datatypes In SQLite Version 3, as this explains about the type and how it is flexible in comparison to typically databases. It also explains about rowid's aliases and AUTOINCREMENT.
The exception is for a column that is an alias of the rowid column. An alias of the rowid column is defined by using INTEGER PRIMARY KEY (specifically INTEGER not INT or not any other value that will result in a derived type of INTEGER).
Note that AUTOINCREMENT is can be added to (and only to) the definition, although it is inefficient to do so, and rarely needed.
The rowid and therefore an alias thereof MUST contain an integer value (up to 64 bit signed).
As such you are somehow trying to insert a non-integer value into an alias of the rowid, this cannot be the case if the SQL as shown is used to create the table.
As such the SQL has not been successfully run and created the table, but what is very likely the cause is that a previous table exsists that was created using different SQL.
Simply changing the create SQL will not result in the table being changed. That is because a database persists (stays there) between runs. As such the onCreate method will only be automatically run once for the lifetime of the database.
If you change the schema (the create SQL), then you have to find some way of running the SQL (which would involve dropping the table). The simplest way when developing an App, is to either
delete the App's data,
to uninstall the App or to
increment the version number
this latter option will only work if the onUpgrade drops the table(s) so they can be recreated and invokes the changed SQL (typically by calling onCreate)).
After doing one of the above the App should then be rerun.
Note the above options will all result in current data being lost. If current data needs to be retained then the process is more complicated and will likely involve ALTER statements to rename tables and statements to copy (INSERT) the data from the original table(s) to the new table(s).

#MikeT already answered this correctly in detail, but nevertheless I'm wondering: why not follow architecture design patterns and implement Android Room?
Using Room, you wouldn't have to worry about this kind of "low level" error. It would tell you what is wrong and that is what it's designed for.
Tip: Let Room do the inserting for you and be happy with persistent data

Related

"SQLiteDatabase.insert(TABLE_NAME,null,ContentValues);" showing "datatype mismatch" error

I know basics about SQLite .When i was working on Reverse engineering of SQLite for android development--> Even i have copied most of the code and paste it in mine , my app show error in logcat about data mismatch.
(The error is in setNotes method down)
public class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context c){
super(c,"Notes_Database",null,1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(Note.CREATE_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + Note.TABLE_NAME);
onCreate(db);
}
public long setNotes(String wordv,String reversev){
SQLiteDatabase db=this.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("Word",wordv);
values.put("Reverse",reversev);
//This below line where the Error occurs.
long id=db.insert(Note.TABLE_NAME,null,values);//<--
db.close();
return id;
}
My table name is :
public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME + "("
+ "Word" + " TEXT,"
+ "Reverse" + " TEXT"+")";
And Here is My Error(That is "[INSERT INTO notes(Reverse,Word) VALUES (?,?)] datatype mismatch") :-(
> 2019-04-14 20:52:05.404 9597-9597/com.example.sqlwithjava E/SQLiteLog: (20) statement aborts at 5: ***[INSERT INTO notes(Reverse,Word) VALUES (?,?)] datatype mismatch***
2019-04-14 20:52:05.406 9597-9597/com.example.sqlwithjava E/SQLiteDatabase: Error inserting Reverse=gfdhg Word=ghdfg
android.database.sqlite.SQLiteDatatypeMismatchException: datatype mismatch (code 20)
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:782)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1474)
at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1343)
at com.example.sqlwithjava.DatabaseHelper.setNotes(DatabaseHelper.java:47)
at com.example.sqlwithjava.MainActivity.ReverseText(MainActivity.java:30)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
at android.view.View.performClick(View.java:5610)
at android.view.View$PerformClick.run(View.java:22265)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
And Thanks in advance.
The Issue
There is only one reason for a datatype mismatch and that is when an attempt is made to insert a non integer value into the rowid column or an alias of the rowid column. Any other column can store any type of value .
As such column Reverse or column Word has been defined as an alias of the rowid column.
An alias of the rowid is defined when you specifically code column_name INTEGER PRIMARY KEY (with or without the AUTOINCREMENT keyword) or an equivalent as per - ROWIDs and the INTEGER PRIMARY KEY.
Therefore
public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME + "("
+ "Word" + " TEXT,"
+ "Reverse" + " TEXT"+")";
Cannot be what has been used to define the table.
A common cause of the table not being defined as expected is the frequent mis-conception that the Database Helper's onCreate method runs every time the App is run. The onCreate will only automatically be run once when the database is created. All subsequent runs will ascertain that the database exists and the onCreate method will then not run automatically.
The Fix
The fix, at least when developing the App, to apply a changed table definition is to either delete the database or to force the onCreate method to run after deleting any tables (otherwise the creation would fail, or if the table definition includes CREATE TABLE IF NOT EXISTS ........ that the attempt to create the table would be skipped).
As such the likely fix is to do one of the following :-
Delete the App's data.
Uninstall the App.
or (in your case as the onUpgrade mnethod appears to be fine (drops the table and then calls onCreate)) increase the version number (4th parameter to the SQLiteOpenHelper super call) e.g. change super(c,"Notes_Database",null,1); to super(c,"Notes_Database",null,2);
After doing one of the above, the App can then be rerun, which should introduce the changed table.

How to Insert a value where not exist in SQLite Android

I want to insert values into my table only when values don't exist in the table.
String CREATETABLE = "CREATE TABLE contacts ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT, "+
"phone TEXT )";
Here is my code to create the table, I used UNIQUE(phone), and it doesn't work.
And to add a new contact I am using this code:
ContentValues values = new ContentValues();
values.put("name", contact.getName()); // get title
values.put("phone", contact.getNumero()); // get author
// 3. insert
db.insert("contacts", // table
null, //nullColumnHack
values);
I don't think there is a way of doing that without retrieving the row first. You query the database looking for that particular contact (which you have to specify at least one column to be unique, or any combination of columns to be primary key) otherwise how would you handle two person with the same name?.
So you query and search for the desired person, if you find it, check if the column is null and a) insert if it is, b) ignore if it isn't. If the query doesn't find any person, you cant just insert.
About the unique constraint, its like this:
String CREATETABLE = "CREATE TABLE contacts ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT, " +
"phone TEXT UNIQUE)";
In your sample you are using SQLiteDatabase.insert(), try to use SQLiteDatabase.insertWithOnConflict() with one of values CONFLICT_IGNORE, CONFLICT_REPLACE or others.
Also have a look about implementing database as ContentProvider which is much harder to understand but really good to know.

Cannot read value from database

This is my first attempt to write/read to a database using Android and SQLite.
The code below appears to be inserting data (I can see an increment in number of rows) but when I try to call the value, an exception is thrown
E/CursorWindow﹕ Failed to read row 0, column -1 from a CursorWindow which has 6 rows, 2 columns.
I can't see why the below is failing.
public void Save(String name, String email) {
_db.execSQL("CREATE TABLE IF NOT EXISTS MailingList (Email VARCHAR, Name VARCHAR);");
_db.execSQL("INSERT INTO MailingList (Email, Name) VALUES('" + email + "', '" + name + "');");
ReadDatabase();
_db.close();
//_db.deleteAll();
}
private void ReadDatabase() {
Cursor cursor = _db.rawQuery("SELECT * FROM MailingList", null);
int i = cursor.getCount();
ArrayList<String> results = new ArrayList<String>();
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor.getColumnIndex("Name")); //ERROR
String email = cursor.getString(cursor.getColumnIndex("Email"));
results.add("Name: " + name + ", Email: " + email);
} while (cursor.moveToNext());
}
}
ListView myList = (ListView) findViewById(R.id.listViewFromDB);
myList.setAdapter(new ArrayAdapter<String>(this, R.layout.item_layout, results));
}
LogCat
Caused by: java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
at android.database.CursorWindow.nativeGetString(Native Method)
at android.database.CursorWindow.getString(CursorWindow.java:435)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
at com.lmsites.dave.lifecrymailinglist.MyActivity.ReadDatabase(MyActivity.java:106)
at com.lmsites.dave.lifecrymailinglist.MyActivity.Save(MyActivity.java:88)
at com.lmsites.dave.lifecrymailinglist.MyActivity.SaveClick(MyActivity.java:73)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at android.view.View$1.onClick(View.java:3830)
            at android.view.View.performClick(View.java:4450)
            at android.view.View$PerformClick.run(View.java:18600)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5026)
            at java.lang.reflect.Method.invokeNative(Native Method)
E/CursorWindow﹕ Failed to read row 0, column -1 from a CursorWindow which has 6 rows, 2 columns.
This error indicates that you were trying to get the column index of a column which does not exist. getColumnIndex() returns -1 when it cannot find the specific column. I don't see any error in your code so I think there might be something wrong with the database itself.
When developing you have to remember that the SQLite database is only really deleted when you uninstall the app or wipe all data in the application manager.
So if you at some point made a mistake in an sql script or you added, removed or renamed some columns you need to uninstall and then reinstall the app for those changes to take effect in the database.
This of course is a problem for many apps and when developing you are going to run into this all the time. But as soon as your app is in the app store people are going to use it and you cannot ask everyone to reinstall your app every time you update it. That would be the ridiculous and people aren't going to like it, but there is a way around this.
You need to pass a version number into your SQLiteOpenHelper. Increase this version number every time you change something about the database and then when Android notices that the database version has increased the onUpgrade() callback will be called to upgrade your database accordingly! Try something like this:
public class ExampleSQLiteOpenHelper extends SQLiteOpenHelper {
public ExampleSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
#Override
public void onCreate(SQLiteDatabase db) {
// The create query here always has to be for the most up to
// date version of the database
db.execSQL("CREATE TABLE MailingList (Email VARCHAR, Name VARCHAR, PhoneNumber VARCHAR);");
db.execSQL("CREATE TABLE SomeTable (_id PRIMARY KEY AUTOINCREMENT, SomeColumn VARCHAR)");
...
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This will be called when you are starting your app for the first time
// after increasing the version number.
// This loops through all the version between the current version of
// the database and the newest version. upgradeTo is called with each
// version in between.
for(int i = oldVersion + 1; i <= newVersion; i++) {
upgradeTo(db, i);
}
}
private void upgradeTo(SQLiteDatabase db, int version) {
// With this switch you can execute the upgrade scripts for each version
// of the databse
switch (version) {
case 2:
// in this version we added a new column to MailingList so we
// use ALTER TABLE to add the new column
db.execSQL("ALTER TABLE MailingList ADD COLUMN PhoneNumber VARCHAR");
break;
case 3:
// In this version we added a new table
db.execSQL("CREATE TABLE SomeTable (_id PRIMARY KEY AUTOINCREMENT, SomeColumn VARCHAR)");
break;
case 4:
...
break;
case 5:
...
break;
}
}
}
The simplest solution of course is to drop all tables and recreate them in onUpgrade but than all the data in the database is lost. If you don't want that you can use something like the code above to gradually upgrade your database!

How to insert time stamp into an SQLite database column? Using the function time('now')?

I am working on an android app and I am creating a database called HealthDev.db that has a table called rawData that has 4 columns:
_id, foreignUserId, data, timeStamp
I have worked with the program sqlite3 in the bash shell and have figured out that I can have a time stamp column with the following column schema parameter:
timeStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
so when I created the table I used:
create table rawData(_id integer primary key autoincrement, foreignUserId integer, data real, timeStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
This worked fine in the bash.
Then I practiced in the sqlite3 and know that when inserting into the timeStamp column and using the function time('now') as a value to store it actually stores a time stamp in the form HH:MM:SS in Universal Coordinated Time.
So now translating that into java for the android app, I used the following code below. This way the table automatically generates about 20 rows when the onCreate is called. This is just for testing if I am passing the time('now') correctly in java.
// Below are variables to the database table name and the
// database column names.
public static final String TABLE_RAW_DATA = "rawData";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_FOREIGN_USER_ID = "foreignUserId";
public static final String COLUMN_DATA = "data";
public static final String COLUMN_TIME_STAMP = "timeStamp";
// Database creation sql statement.
private static final String DATABASE_CREATE = "create table "
+ TABLE_RAW_DATA
+ "("
+ COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_FOREIGN_USER_ID + " integer, "
+ COLUMN_DATA + " real, "
+ COLUMN_TIME_STAMP + " TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
+ ");";
// initializes the columns of the database given by passing the DATABASE_CREATE
// sql statement to the incoming database.
public static void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
// For testing
ContentValues contentValues = new ContentValues();
System.out.println("The database is open? " + database.isOpen());
for (int i = 0; i < 20; i++)
{
contentValues.put( COLUMN_FOREIGN_USER_ID, 8976);
contentValues.put( COLUMN_DATA, Math.random()*100 );
contentValues.put( COLUMN_TIME_STAMP, " time('now') " );
database.insert( TABLE_RAW_DATA, null, contentValues );
//contentValues = new ContentValues();
}
}
After running this code in an eclipse emulator I then pulled the database file from the file explorer in DDMS view mode for eclipse android projects. Then I opened the database in a bash shell and then selected all the columns from the table rawData to show it on the shell. I noticed that the time('now') was treated as a string and not a function. To prove that the time('now') function worked I manually inserted a new row using time('now') for the timeStamp value. Then re selected all the columns to show them again. It successfully printed the time stampe as HH:MM:SS.
I am thinking there might be a difference in the enviroments? The bash shell recognizes the function time('now'), which was written in c right?, because I have the sqlite3 program in the bash? Yet in eclipse when I use a SQL database and use the insert it treats the time('now') as a string. Keep in mind I am working in a Windows 7 os. I am accessing the bash as a client (SSH Secure Shell) from my school which is the host.
My main question is it possible to code it so that way it recognizes the time('now') function?
Since the default for the column is CURRENT_TIMESTAMP, what if you leave out entirely this line:
contentValues.put( COLUMN_TIME_STAMP, " time('now') " );
Won't it now insert the current timestamp into that column by default?

Reset the row number count in an android DataBase

I need to reset the row number count to 1.
How can I do that?
An example for an update query in my code:
public boolean update (long rowId, String title, String body, String reminderDateTime, String loca, String type, String settime, String lat, String llong) {
ContentValues args = new ContentValues();
args.put(KEY_TITLE, title);
args.put(KEY_BODY, body);
args.put(KEY_DATE_TIME, reminderDateTime);
args.put(KEY_LOCATION, loca);
args.put(KEY_TYPE, type);
args.put(KEY_SETTIME, settime);
args.put(KEY_LAT, lat);
args.put(KEY_LONG, llong);
return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
I tried to do that but the eclipse showing me an error:"sqlite_sequence cannot be resolved to a variable"
public void resetAutoNumbering ()
{
mDb.update(sqlite_sequence, args, KEY_ROWID + "=" + rowId, null);
}
What can I do and how?
Found this as answer for a similar question SQLite Reset Primary Key Field:
delete from your_table;
delete from sqlite_sequence where name='your_table';
SQLite Autoincrement
SQLite keeps track of the largest ROWID that a table has ever held using the special SQLITE_SEQUENCE table. The SQLITE_SEQUENCE table is created and initialized automatically whenever a normal table that contains an AUTOINCREMENT column is created. The content of the SQLITE_SEQUENCE table can be modified using ordinary UPDATE, INSERT, and DELETE statements. But making modifications to this table will likely perturb the AUTOINCREMENT key generation algorithm. Make sure you know what you are doing before you undertake such changes.
-axel
You use sqlite_sequence in your procedure but before this you used DATABASE_TABLE instead of sqlite_sequence. Change your sqlite_sequence to DATABASE_TABLE.

Categories