NumberFormatException: null - java

I am getting this error:NumberFormatException: null when I am trying to add score/points to my app.
I created separated table for this because I need multiple tables .
I have no clue what the problem is so thanks to you all.
if(count==4) {
my_db=new DBHelper(this);
sqdb = my_db.getWritableDatabase();
Cursor c_oldPoints= sqdb.query(DBHelper.TABLE_NAME2,null,DBHelper.NICKNAME+"=?",new String[]{Username},null,null,null);
int col_Points=c_oldPoints.getColumnIndex(DBHelper.POINTS);
c_oldPoints.moveToFirst();
while (!c_oldPoints.isAfterLast())
{
OldPoints=c_oldPoints.getString(col_Points);
c_oldPoints.moveToNext();
}
sqdb.close();
int OldP = Integer.parseInt(OldPoints);
OldP+=countPoints;
String SoldP = Integer.toString(OldP);
my_db=new DBHelper(this);
sqdb = my_db.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(my_db.POINTS,SoldP);
Cursor c = sqdb.query(DBHelper.TABLE_NAME2,null,DBHelper.NICKNAME+"=?",new String[]{Username},null,null,null);
c.moveToFirst();
while (!c.isAfterLast())
{
sqdb.update(DBHelper.TABLE_NAME2,cv, DBHelper.POINTS+"=?",new String[]{OldPoints});
c.moveToNext();
}
sqdb.close();
countPoints=0;
}
This is the logcat :-
2019-05-15 18:18:14.101 8513-8513/com.example.user.soundsequ E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.user.soundsequ, PID: 8513
java.lang.NumberFormatException: null
at java.lang.Integer.parseInt(Integer.java:483)
at java.lang.Integer.parseInt(Integer.java:556)
at com.example.user.soundsequ.Game.onClick(Game.java:353)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
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:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

The error appears to be in this line
int OldP = Integer.parseInt(OldPoints);
The error is saying that the value of OldPoints is not a string that can be converted to an integer e.g. if it were A or null;
As such either a value extracted from the POINTS column is not a numeric or the value of Username does not match the column NICKNAME in a row. In which case OldPoints will be whatever value it has been set to before the loop.
As the data itself is not available you need to ascertain which of the two situations is causing the issue.
I'd suggest adding some Logging in to determine which.
e.g. by using something like :-
OldPoints = "my debugging value";
Log.d("MYDEBUGGING","OldPoints, before doing anything is " + OldPoints);
Cursor c_oldPoints= sqdb.query(DBHelper.TABLE_NAME2,null,DBHelper.NICKNAME+"=?",new String[]{Username},null,null,null);
int col_Points=c_oldPoints.getColumnIndex(DBHelper.POINTS);
c_oldPoints.moveToFirst();
while (!c_oldPoints.isAfterLast())
{
OldPoints=c_oldPoints.getString(col_Points);
c_oldPoints.moveToNext();
Log.d("MYDEBUGGING","Extracted the value " + OldPoints + " from position + String.valueOf(c_oldPoints.getPosition());
}
sqdb.close();
Log.d("MYDEBUGGING","Trying to convert the value " + OldPoints + " to an integer");
int OldP = Integer.parseInt(OldPoints);
You could also not make the above changes and add a breakpoint (on the line initially indicated) and then use Run/Debug App and inspect the variables (or use multiple breakpoints at suitable places). You may find this useful in regard to debugging Debug your app.
The following code protects against the exception and also protects against an attempt being made to update a non-existent user :-
if (count == 4) {
SQLiteDatabase sqdb = my_db.getWritableDatabase();
Cursor c_oldPoints= sqdb.query(
DBHelper.TABLE_NAME2,null,
DBHelper.NICKNAME+"=?",
new String[]{Username},
null,null,null
);
int col_Points=c_oldPoints.getColumnIndex(DBHelper.POINTS);
if (c_oldPoints.moveToFirst()) {
Oldpoints = c_oldPoints.getString(col_Points);
//Oldpoints = "oops";
int OldP = 0;
boolean can_convert_to_int = true;
try {
OldP = Integer.parseInt(Oldpoints) + countPoints;
can_convert_to_int = true;
} catch (NumberFormatException e) {
e.printStackTrace(); //TODO not necessary probably remove. just for checking the log
}
if (can_convert_to_int) {
ContentValues cv = new ContentValues();
cv.put(DBHelper.POINTS,OldP);
sqdb.update(DBHelper.TABLE_NAME2,cv, DBHelper.NICKNAME + "=?", new String[]{Username});
}
} else {
Log.d("NICKNAMENOTFOUND","No row was found when attemtping to get the old score for User " + Username);
}
}
However
I would suggest that you add a couple of methods to your DBHelper class, these being :-
public int increasePoints(String user, int points_to_add) {
SQLiteDatabase db = this.getWritableDatabase();
SQLiteStatement sql = db.compileStatement(
"UPDATE " + TABLE_NAME2 +
" SET " + POINTS + "=" + POINTS + " +? " +
"WHERE "+ NICKNAME + "=?"
);
sql.bindLong(1,points_to_add);
sql.bindString(2,user);
return sql.executeUpdateDelete();
}
public int getPoints(String user) {
SQLiteDatabase db = this.getWritableDatabase();
int rv = -1;
String whereclause = NICKNAME + "=?";
String[] whereargs = new String[]{user};
Cursor csr = db.query(TABLE_NAME2,new String[]{POINTS},whereclause,whereargs,null,null,null);
if (csr.moveToFirst()) {
rv = csr.getInt(csr.getColumnIndex(POINTS));
}
csr.close();
return rv;
}
The first method increasePoints performs the change to the points via an UPDATE sql statement and does away for the need to convert the points extracted as a string to an integer. It returns the number of rows that have been updated (1 if the NICKNAME column is always a unique value, 0 if nothing was updated).
The second method getPoints does as it says, it gets the points for the given user, if the user doesn't exist it will return -1.
Your code could then be :-
if (count == 4) {
boolean updated = false; //TODO remove when happy
int old_points = my_db.getPoints(Username); //TODO remove when happy
if (my_db.increasePoints(Username,countPoints) > 0) {
updated = true;
}
int new_points = my_db.getPoints(Username); //TODO remove when happy
//TODO remove following code when happy
String result = "The result of the attempt to update the points for user " + Username;
if (updated) {
result = result + " was successful. ";
} else {
result = result + " was unsuccessful.";
}
Log.d("POINTSINCREASE",result +
" Points were " + String.valueOf(old_points) + " points are now " + String.valueOf(new_points));
}
Note where //TODO remove when happy is coded the lines are just for testing, so the above could be :-
if (count == 4) {
my_db.increasePoints(Username,countPoints);
}

Related

Merge user's Android Room database when migrating database

I have a Room database which is stored in assets/database with its preloaded data. I am creating an updated version with more contents in the database for the next update.
Currently, if I add new contents to the database with no schema changes and reinstall the app, these new contents do not show up. The only way I can see the changes is if I uninstall and reinstall the app. However, I need to merge the user's data with the database with the new contents since I need to get the "favorites" of the user which is an integer column of a table with the item contents.
Is this possible?
This is how I create my database.
public static AppDatabase getInMemoryDatabase(Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app_database.db")
.createFromAsset("database/QuotesDB.db")
.addMigrations(MIGRATION_1_2)
.build();
}
}
}
return INSTANCE;
}
I tried to migrate with the following code but it still doesn't update the contents.
/**
* Migrate from:
* version 1 - initial contents.
* to
* version 2 - updated database contents (no schema changes)
*/
#VisibleForTesting
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
// I need to tell Room that it should use the data
// from version 1 ( with the user's favorites ) to version 2.
}
};
Is this possible?
Yes. However it is a little complex.
In short, you can actually do it the other way round. Rather than use the new database from the asset and try to retrive the previous data (is complicated if using Room Migration as you have to effectiviely swap to the newly created/copied database which is further complicated as you're inside a transaction when migrating).
If however you do the schema changes to the database in use rather than the asset database, you can then get the asset database and copy the new non-user data (would be greatly complicated if the user's data were intermingled with the non-user data).
Even this isn't that simple. However, here's a simple exmaple/scanario based upon a slight expansion of your code to :-
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase db) {
final String TAG = "MIGRATE_1_2";
Log.d(TAG,"Database Version when called is " + db.getVersion());
// I need to tell Room that it should use the data
// from version 1 ( with the user's favorites ) to version 2.
// "CREATE TABLE IF NOT EXISTS `userdata` (`userId` INTEGER DEFAULT uid, `name` TEXT, PRIMARY KEY(`userId`))"
//db.execSQL("CREATE TABLE IF NOT EXISTS `userdata_saveuserdata` (`userId` INTEGER, `name` TEXT, PRIMARY KEY(`userId`))");
//db.execSQL("INSERT INTO `userdata_saveuserdata` SELECT * FROM `userdata`");
db.execSQL("ALTER TABLE `otherdata` ADD COLUMN `column2` TEXT");
Log.d(TAG,"Checking Context");
if (sContext != null) {
applyAssetDB(db);
} else {
Log.d(TAG,"Context is null!!!!");
}
}
};
As you can see this changes the otherdata table (not the users table) by adding a new column.
It then checks to see if sContext isn't null.
A valid Context is used rather then hard coding paths.
Then the applyAssetDB is invoked, which is :-
private static void applyAssetDB(SupportSQLiteDatabase sdb) {
String TAG = "APPLYASSETDB";
String mainDatabaseName = (new File(sdb.getPath()).getName());
String assetDatabaseName = mainDatabaseName + "_asset";
String asset_schema = "asset_schema";
Log.d(TAG,"Attempting application of asset data to database."
+ "\n\tActual Database = " + mainDatabaseName
+ "\n\tAsset Database will be " + assetDatabaseName
+ "\n\tSchema for attached database will be " + asset_schema
);
copyDatabaseFromAssets(AppDatabase.sContext,MainActivity.ASSETNAME,assetDatabaseName);
/*
if (sdb.isWriteAheadLoggingEnabled()) {
setAssetDBToWALMode(sContext.getDatabasePath(assetDatabaseName).getPath());
}
Log.d(TAG,"Attempting to ATTACH asset database " + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
sdb.execSQL("ATTACH DATABASE '" + sContext.getDatabasePath(assetDatabaseName).getPath() + "' AS " + asset_schema);
Log.d(TAG,"Attempting INSERTING NEW DATA using\n\t" + "INSERT OR IGNORE INTO `otherdata` SELECT * FROM `otherdata`." + asset_schema);
sdb.execSQL("INSERT OR IGNORE INTO `otherdata` SELECT * FROM `otherdata`." + asset_schema);
Log.d(TAG,"Attempting to DETACH " + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
sdb.execSQL("DETACH DATABASE '" + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
*/
int insertRows = 0;
SQLiteDatabase assetdb = SQLiteDatabase.openDatabase(sContext.getDatabasePath(assetDatabaseName).getPath(),null,SQLiteDatabase.OPEN_READONLY);
Cursor assetCursor = assetdb.query("`otherdata`",null,null,null,null,null,null);
ContentValues cv = new ContentValues();
while (assetCursor.moveToNext()) {
cv.clear();
for (String c: assetCursor.getColumnNames()) {
if (assetCursor.getType(assetCursor.getColumnIndex(c)) == Cursor.FIELD_TYPE_BLOB) {
cv.put(c,assetCursor.getBlob(assetCursor.getColumnIndex(c)));
} else {
cv.put(c,assetCursor.getString(assetCursor.getColumnIndex(c)));
}
}
if (sdb.insert("`otherdata`", OnConflictStrategy.IGNORE,cv) > 0 ) insertRows++;
}
Log.d(TAG,"Inserted " + insertRows + " from the Asset Database");
assetCursor.close();
Log.d(TAG,"Deleting " + sContext.getDatabasePath(assetDatabaseName).getPath());
if ((new File(sContext.getDatabasePath(assetDatabaseName).getPath())).delete()) {
Log.d(TAG,"Copied AssetDatabase successfully deleted.");
} else {
Log.d(TAG,"Copied Asset Database file not deleted????");
}
Log.d(TAG,"Finished");
}
commented out code left in intentionally as issue were encountered when trying to ATTACH the database copied from the assets, so reverted to using seperate connetions.
This copies the database from the asset to the defauly database location via the copyDatabaseFromAssets method (as below). It the extracts all of the non user's data from the asset database and inserts it into the original (but altered according to the changed schema) database relying upon the OnConflictStrategy.IGNORE to only insert new rows. The userdata table is untouched, so the user's data is retianed.
Obviously this would not cater for changed rows.
Here's copyDatabaseFromAssets
private static void copyDatabaseFromAssets(Context context, String assetName, String databaseName) {
String TAG = "COPYDBFROMASSET";
int bufferSize = 1024 * 4, length = 0, read = 0, written = 0, chunks = 0;
byte[] buffer = new byte[bufferSize];
try {
Log.d(TAG,"Attempting opening asset " + assetName + " as an InputFileStream.");
InputStream is = context.getAssets().open(assetName);
Log.d(TAG,"Attempting opening FileOutputStream " + context.getDatabasePath(databaseName).getPath());
OutputStream os = new FileOutputStream(context.getDatabasePath(databaseName));
Log.d(TAG,"Initiating copy.");
while((length = is.read(buffer)) > 0) {
read += length;
os.write(buffer,0,length);
written += length;
chunks++;
}
Log.d(TAG,"Read " + read + "bytes; Wrote " + written + " bytes; in " + chunks);
Log.d(TAG,"Finalising (Flush and Close output and close input)");
os.flush();
os.close();
is.close();
Log.d(TAG,"Finalised");
} catch (IOException e) {
throw new RuntimeException("Error copying Database from Asset " + e.getMessage());
}
}
Here's an example Activity MainActivity that puts this all together (noting that for convenience I've used allowMainThreadQueries ) :-
public class MainActivity extends AppCompatActivity {
//public static final int DBVERSION = 1; //!!!!! ORIGINAL
public static final int DBVERSION = 2;
public static final String DBNAME = "app_database.db";
public static final String ASSETNAME = "database/QuotesDB.db";
AppDatabase appDB;
AllDao adao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
appDB.setContext(this);
appDB = Room.databaseBuilder(this,AppDatabase.class,DBNAME)
.allowMainThreadQueries()
.createFromAsset(ASSETNAME)
.addCallback(AppDatabase.CALLBACK)
.addMigrations(AppDatabase.MIGRATION_1_2)
.build();
adao = appDB.allDao();
appDB.logDBInfo();
if (adao.getUserDataRowCount() == 3) {
adao.insertOneUserData(new UserData("ADDEDU100"));
adao.insertOneUserData(new UserData("ADDEDU200"));
adao.insertOneUserData(new UserData("ADDEDU300"));
}
appDB.logDBInfo();
}
}
When run (after changing the relevant code for the new schema and increasing the version) the result in the log is :-
2019-11-30 10:56:38.768 12944-12944/a.roommigrationwithassets D/MIGRATE_1_2: Database Version when called is 1
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/MIGRATE_1_2: Checking Context
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Attempting application of asset data to database.
Actual Database = app_database.db
Asset Database will be app_database.db_asset
Schema for attached database will be asset_schema
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Attempting opening asset database/QuotesDB.db as an InputFileStream.
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Attempting opening FileOutputStream /data/user/0/a.roommigrationwithassets/databases/app_database.db_asset
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Initiating copy.
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Read 12288bytes; Wrote 12288 bytes; in 3
2019-11-30 10:56:38.771 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Finalising (Flush and Close output and close input)
2019-11-30 10:56:38.772 12944-12944/a.roommigrationwithassets D/COPYDBFROMASSET: Finalised
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Inserted 3 from the Asset Database
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Deleting /data/user/0/a.roommigrationwithassets/databases/app_database.db_asset
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Copied AssetDatabase successfully deleted.
2019-11-30 10:56:38.780 12944-12944/a.roommigrationwithassets D/APPLYASSETDB: Finished
2019-11-30 10:56:38.815 12944-12944/a.roommigrationwithassets D/ONOPEN: Database Version when called is 2
2019-11-30 10:56:38.816 12944-12944/a.roommigrationwithassets D/ONOPEN: Database Version after Super call is 2
2019-11-30 10:56:38.819 12944-12944/a.roommigrationwithassets D/DBINFO: UserData rowcount = 6
ID = 1 NAME = OU1
ID = 2 NAME = OU2
ID = 3 NAME = OU3
ID = 4 NAME = ADDEDU100
ID = 5 NAME = ADDEDU200
ID = 6 NAME = ADDEDU300
OtherData rowcount = 3
ID = 1Column1 = OD1
ID = 2Column1 = OD2
ID = 3Column1 = OD3
2019-11-30 10:56:38.821 12944-12944/a.roommigrationwithassets D/DBINFO: UserData rowcount = 6
ID = 1 NAME = OU1
ID = 2 NAME = OU2
ID = 3 NAME = OU3
ID = 4 NAME = ADDEDU100
ID = 5 NAME = ADDEDU200
ID = 6 NAME = ADDEDU300
OtherData rowcount = 3
ID = 1Column1 = OD1
ID = 2Column1 = OD2
ID = 3Column1 = OD3
The complete code for the AppDatabase class (noting that this includes some redundant code) is :-
#Database(version = MainActivity.DBVERSION, exportSchema = false,entities = {UserData.class,OtherData.class})
abstract class AppDatabase extends RoomDatabase {
abstract AllDao allDao();
static Context sContext;
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase db) {
final String TAG = "MIGRATE_1_2";
Log.d(TAG,"Database Version when called is " + db.getVersion());
// I need to tell Room that it should use the data
// from version 1 ( with the user's favorites ) to version 2.
// "CREATE TABLE IF NOT EXISTS `userdata` (`userId` INTEGER DEFAULT uid, `name` TEXT, PRIMARY KEY(`userId`))"
//db.execSQL("CREATE TABLE IF NOT EXISTS `userdata_saveuserdata` (`userId` INTEGER, `name` TEXT, PRIMARY KEY(`userId`))");
//db.execSQL("INSERT INTO `userdata_saveuserdata` SELECT * FROM `userdata`");
db.execSQL("ALTER TABLE `otherdata` ADD COLUMN `column2` TEXT");
Log.d(TAG,"Checking Context");
if (sContext != null) {
applyAssetDB(db);
} else {
Log.d(TAG,"Context is null!!!!");
}
}
};
static final RoomDatabase.Callback CALLBACK = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
Log.d("ONCREATE","Database Version when called is " + db.getVersion());
super.onCreate(db);
Log.d("ONCREATE","Database Version after Super call is " + db.getVersion());
}
#Override
public void onOpen(#NonNull SupportSQLiteDatabase db) {
Log.d("ONOPEN","Database Version when called is " + db.getVersion());
super.onOpen(db);
Log.d("ONOPEN","Database Version after Super call is " + db.getVersion());
}
#Override
public void onDestructiveMigration(#NonNull SupportSQLiteDatabase db) {
Log.d("ONDESTRMIG","Database Version when called is " + db.getVersion());
super.onDestructiveMigration(db);
Log.d("ONDESTRMIG","Database Version after Super call is " + db.getVersion());
}
};
public void logDBInfo() {
AllDao adao = this.allDao();
List<UserData> allUserDataRows = adao.getAllUserDataRows();
StringBuilder sb = new StringBuilder().append("UserData rowcount = ").append(allUserDataRows.size());
for (UserData u: allUserDataRows) {
sb.append("\n\tID = ").append(u.getId()).append(" NAME = " + u.getName());
}
List<OtherData> allOtherDataRows = adao.getAllOtherDataRows();
sb.append("\n\nOtherData rowcount = ").append(allOtherDataRows.size());
for (OtherData o: allOtherDataRows) {
sb.append("\n\tID = ").append(o.getOtherDataId()).append("Column1 = ").append(o.getColumn1());
}
Log.d("DBINFO",sb.toString());
}
static void setContext(Context context) {
sContext = context;
}
private static void applyAssetDB(SupportSQLiteDatabase sdb) {
String TAG = "APPLYASSETDB";
String mainDatabaseName = (new File(sdb.getPath()).getName());
String assetDatabaseName = mainDatabaseName + "_asset";
String asset_schema = "asset_schema";
Log.d(TAG,"Attempting application of asset data to database."
+ "\n\tActual Database = " + mainDatabaseName
+ "\n\tAsset Database will be " + assetDatabaseName
+ "\n\tSchema for attached database will be " + asset_schema
);
copyDatabaseFromAssets(AppDatabase.sContext,MainActivity.ASSETNAME,assetDatabaseName);
/*
if (sdb.isWriteAheadLoggingEnabled()) {
setAssetDBToWALMode(sContext.getDatabasePath(assetDatabaseName).getPath());
}
Log.d(TAG,"Attempting to ATTACH asset database " + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
sdb.execSQL("ATTACH DATABASE '" + sContext.getDatabasePath(assetDatabaseName).getPath() + "' AS " + asset_schema);
Log.d(TAG,"Attempting INSERTING NEW DATA using\n\t" + "INSERT OR IGNORE INTO `otherdata` SELECT * FROM `otherdata`." + asset_schema);
sdb.execSQL("INSERT OR IGNORE INTO `otherdata` SELECT * FROM `otherdata`." + asset_schema);
Log.d(TAG,"Attempting to DETACH " + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
sdb.execSQL("DETACH DATABASE '" + sContext.getDatabasePath(assetDatabaseName).getPath() + "." + asset_schema);
*/
int insertRows = 0;
SQLiteDatabase assetdb = SQLiteDatabase.openDatabase(sContext.getDatabasePath(assetDatabaseName).getPath(),null,SQLiteDatabase.OPEN_READONLY);
Cursor assetCursor = assetdb.query("`otherdata`",null,null,null,null,null,null);
ContentValues cv = new ContentValues();
while (assetCursor.moveToNext()) {
cv.clear();
for (String c: assetCursor.getColumnNames()) {
if (assetCursor.getType(assetCursor.getColumnIndex(c)) == Cursor.FIELD_TYPE_BLOB) {
cv.put(c,assetCursor.getBlob(assetCursor.getColumnIndex(c)));
} else {
cv.put(c,assetCursor.getString(assetCursor.getColumnIndex(c)));
}
}
if (sdb.insert("`otherdata`", OnConflictStrategy.IGNORE,cv) > 0 ) insertRows++;
}
Log.d(TAG,"Inserted " + insertRows + " from the Asset Database");
assetCursor.close();
Log.d(TAG,"Deleting " + sContext.getDatabasePath(assetDatabaseName).getPath());
if ((new File(sContext.getDatabasePath(assetDatabaseName).getPath())).delete()) {
Log.d(TAG,"Copied AssetDatabase successfully deleted.");
} else {
Log.d(TAG,"Copied Asset Database file not deleted????");
}
Log.d(TAG,"Finished");
}
private static void copyDatabaseFromAssets(Context context, String assetName, String databaseName) {
String TAG = "COPYDBFROMASSET";
int bufferSize = 1024 * 4, length = 0, read = 0, written = 0, chunks = 0;
byte[] buffer = new byte[bufferSize];
try {
Log.d(TAG,"Attempting opening asset " + assetName + " as an InputFileStream.");
InputStream is = context.getAssets().open(assetName);
Log.d(TAG,"Attempting opening FileOutputStream " + context.getDatabasePath(databaseName).getPath());
OutputStream os = new FileOutputStream(context.getDatabasePath(databaseName));
Log.d(TAG,"Initiating copy.");
while((length = is.read(buffer)) > 0) {
read += length;
os.write(buffer,0,length);
written += length;
chunks++;
}
Log.d(TAG,"Read " + read + "bytes; Wrote " + written + " bytes; in " + chunks);
Log.d(TAG,"Finalising (Flush and Close output and close input)");
os.flush();
os.close();
is.close();
Log.d(TAG,"Finalised");
} catch (IOException e) {
throw new RuntimeException("Error copying Database from Asset " + e.getMessage());
}
}
private static void setAssetDBToWALMode(String assetDBPath) {
SQLiteDatabase db = SQLiteDatabase.openDatabase(assetDBPath,null,SQLiteDatabase.OPEN_READWRITE);
db.enableWriteAheadLogging();
db.close();
}
}
Yes, It is possible.
The one precondition is your pre-shipped database and app database version must be the same. For example, your first release will have both databases as version 1. Now for the second release with new data update the pre-shipped and app database version to 2 and your database builder code will look like this:
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app_database.db")
.createFromAsset("database/QuotesDB.db")
.fallbackToDestructiveMigration()
.build();
Prepopulate your Room database Docs
This medium article explains this nicely
Yes it is possible!
increase your SQLite VERSION and add .fallbackToDestructiveMigration() in your Room.databaseBuilder()
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app_database.db")
.createFromAsset("database/QuotesDB.db")
.fallbackToDestructiveMigration()
.build();

null object reference - confused with Objects in Java [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 5 years ago.
I'm confused with the objects and constructor in Java. I query from a SQLiteDatabase but I couldn't get the correct object/answer. I know my codes look messy and I need to clean it up but I don't know where to start...
public static class QObject {
public String word;
public String definition;
public QObject(String word, String definition) {
this.word = word;
this.definition = definition;
}
public QObject getAnswer(String message) {
QObject quizObject = null;
String query = "select * from " + TABLE_NAME + " where " + COL_WORD + " = '" + message + "'";
Cursor cursor = this.getDbConnection().rawQuery(query, null);
if(cursor.moveToFirst()) {
do {
String myword = cursor.getString(cursor.getColumnIndexOrThrow(COL_WORD));
String mydefinition = cursor.getString(cursor.getColumnIndexOrThrow(COL_DEFINITION));
quizObject = new QObject(word, definition);
} while (cursor.moveToNext());
}
cursor.close();
return quizObject;
}
private SQLiteDatabase getDbConnection() {
return dbHelper.getReadableDatabase();
}
}
}
public void searchName(View view) {
String word = null;
String definition = null;
DatabaseTable db = new DatabaseTable(this);
DatabaseBackend dbBackend = new DatabaseBackend(MainActivity.this);
DatabaseObject dbo = new DatabaseObject(this);
DatabaseB.QObject quizobject = new DatabaseB.QObject(word, definition);
DatabaseB.QObject allQuizQuestions = quizobject.getAnswer(message);
String answer = allQuizQuestions.definition;
TextView textView = findViewById(R.id.textView2);
textView.setText(answer);
}
The error message is null object reference:
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
...
Caused by: java.lang.NullPointerException: Attempt to read from field 'java.lang.String com.justkitting.orion.databasetest.MainActivity$DatabaseB$QObject.definition' on a null object reference
at com.justkitting.orion.databasetest.MainActivity.searchName(MainActivity.java:139)
at java.lang.reflect.Method.invoke(Native Method)
...
Many many thanks.
Your exception is thrown at this line: String answer = allQuizQuestions.definition; and it means that allQuizQuestions is null. So in the line above (DatabaseB.QObject allQuizQuestions = quizobject.getAnswer(message);) you get null from getAnswer(message) method. And this can happen if cursor.moveToFirst() returns false and you never call this line of code: quizObject = new QObject(word, definition);.
One more thing I found: constructor in this line quizObject = new QObject(word, definition); is not using Strings you found in your DB, but values of word and definition from QObject class, which are null at this point. You should use myword and mydefinition instead.
Can you post the code with the line numbers(If you are on eclipse right click on the far left side of the editor and select 'show line numbers' ). it looks like you are calling a method on an object which is null. You night need to do a null check before method invocation

Dynamic Variable Initialization Error in Java

I have this piece of code which is giving an error saying
variable priorityCheck is not initialized
The output that I require is that the query executes with the given value of priorityCheck in a loop. Here is the code:
String query;
String StrComp;
int PerFound;
Statement stmt = null;
ResultSet rs = null;
String priorityCheck;
// running the queries
for (int i = 0; i < names.size(); i++) {
StrComp = types.get(i).toString();
PerFound = StrComp.indexOf("%");
if (StrComp.indexOf("P1") != -1) {
priorityCheck =
"a.SubscriptionType=0 and a.applyticketpriorityfilterflag = 1 and a.P1 & a.P2 = 1";
} else if (StrComp.indexOf("P2") != -1) {
priorityCheck =
"a.SubscriptionType=0 and a.applyticketpriorityfilterflag = 1 and a.P3 & a.P4 = 1";
} else if (StrComp.indexOf("WO") != -1) {
priorityCheck = "a.SubscriptionType=2";
}
if (PerFound == -1) {
query =
"SELECT DATEDIFF(minute,a.LastStatusDate,GETUTCDATE()) FROM SASubscriptionPIIView a,SAWorkflowToolPIIView b WHERE (a.toolid=b.id and a.active=1 and a.SubscriptionCategory=0 and "
+ priorityCheck
+ " and b.toolname like "
+ types.get(i)
+ ")";
writeLog(query);
} else {
query =
"SELECT DATEDIFF(minute,a.LastStatusDate,GETUTCDATE()) FROM SASubscriptionPIIView a,SAWorkflowToolPIIView b WHERE (a.toolid=b.id and a.active=1 and a.SubscriptionCategory=0 and "
+ priorityCheck
+ " and b.toolname like "
+ types.get(i)
+ ")";
writeLog(query);
}
}
This error basically means: you are using (reading!) a variable in your code, but there are paths in your code that do not initialize your variable.
Simplified:
String foo;
if (whatever) {
foo = "from if";
}
bar = foo;
That is what your code is doing: it contains a way to reach a "read" for priorityCheck without a previous "write" to that variable. One simple fix:
String foo = "not initialized";
In other words: think what the variable should contain as "default". Or make sure that there are no paths without assigning a value.
Beyond that: your real problem is that you are writing overly complicated code. If you really intend to write "database code" at this point; consider reading a bit about "clean coding practices"; for example the Single Layer of Abstraction principle. Your problem is that you are writing code that is so complex that you can't see any more what the code is doing.
Please initialize variable priorityCheck with some default value - "", for example.
The problem is that in code
if (StrComp.indexOf("P1")!=-1)
{
priorityCheck="a.SubscriptionType=0 and a.applyticketpriorityfilterflag = 1 and a.P1 & a.P2 = 1";
}
else if (StrComp.indexOf("P2")!=-1)
{
priorityCheck="a.SubscriptionType=0 and a.applyticketpriorityfilterflag = 1 and a.P3 & a.P4 = 1";
}
else if (StrComp.indexOf("WO")!=-1)
{
priorityCheck="a.SubscriptionType=2";
}
variable priorityCheck can be not initialized - else statement is missing.

Database insertion synchronization

I have a java code that generates a request number based on the data received from database, and then updates the database for newly generated
synchronized (this.getClass()) {
counter++;
System.out.println(counter);
System.out.println("start " + System.identityHashCode(this));
certRequest
.setRequestNbr(generateRequestNumber(certInsuranceRequestAddRq
.getAccountInfo().getAccountNumberId()));
System.out.println("outside funcvtion"+certRequest.getRequestNbr());
reqId = Utils.getUniqueId();
certRequest.setRequestId(reqId);
System.out.println(reqId);
ItemIdInfo itemIdInfo = new ItemIdInfo();
itemIdInfo.setInsurerId(certRequest.getRequestId());
certRequest.setItemIdInfo(itemIdInfo);
dao.insert(certRequest);
addAccountRel();
counter++;
System.out.println(counter);
System.out.println("end");
}
the output for System.out.println() statements is `
1
start 27907101
com.csc.exceed.certificate.domain.CertRequest#a042cb
inside function request number66
outside funcvtion66
AF88172D-C8B0-4DCD-9AC6-12296EF8728D
2
end
3
start 21695531
com.csc.exceed.certificate.domain.CertRequest#f98690
inside function request number66
outside funcvtion66
F3200106-6033-4AEC-8DC3-B23FCD3CA380
4
end
In my case I get a call from two threads for this code.
If you observe both the threads run independently. However the data for request number is same in both the cases.
is it possible that before the database updation for first thread completes the second thread starts execution.
`
the code for generateRequestNumber() is as follows:
public String generateRequestNumber(String accNumber) throws Exception {
String requestNumber = null;
if (accNumber != null) {
String SQL_QUERY = "select CERTREQUEST.requestNbr from CertRequest as CERTREQUEST, "
+ "CertActObjRel as certActObjRel where certActObjRel.certificateObjkeyId=CERTREQUEST.requestId "
+ " and certActObjRel.certObjTypeCd=:certObjTypeCd "
+ " and certActObjRel.certAccountId=:accNumber ";
String[] parameterNames = { "certObjTypeCd", "accNumber" };
Object[] parameterVaues = new Object[] {
Constants.REQUEST_RELATION_CODE, accNumber };
List<?> resultSet = dao.executeNamedQuery(SQL_QUERY,
parameterNames, parameterVaues);
// List<?> resultSet = dao.retrieveTableData(SQL_QUERY);
if (resultSet != null && resultSet.size() > 0) {
requestNumber = (String) resultSet.get(0);
}
int maxRequestNumber = -1;
if (requestNumber != null && requestNumber.length() > 0) {
maxRequestNumber = maxValue(resultSet.toArray());
requestNumber = Integer.toString(maxRequestNumber + 1);
} else {
requestNumber = Integer.toString(1);
}
System.out.println("inside function request number"+requestNumber);
return requestNumber;
}
return null;
}
Databases allow multiple simultaneous connections, so unless you write your code properly you can mess up the data.
Since you only seem to require a unique growing integer, you can easily generate one safely inside the database with for example a sequence (if supported by the database). Databases not supporting sequences usually provide some other way (such as auto increment columns in MySQL).

Can anyone help with a java problem regarding record stores?

I have this code. And basically this returns the correct data without the town qualities. When I add the town qualities the method returns nothing, not even the orginal data that it has been and I dont know why. Can anyone see a problem?
protected void listRecords() {
mListForm.deleteAll(); // clear the form
try {
RecordStore rs = RecordStore.openRecordStore("Details", true);
RecordEnumeration re = rs.enumerateRecords(null, new RecordSorter(), false);
while (re.hasNextElement()) {
byte [] recordBuffer = re.nextRecord();
String record = new String(recordBuffer);
// extract the name and the age from the record
int endOfName = record.indexOf(";");
int endOfDesc = record.indexOf(";" , endOfName + 1);
int endOfTown = record.indexOf (";", endOfDesc + 1);
String name = record.substring(0, endOfName);
String desc = record.substring(endOfName + 1, endOfDesc);
String town = record.substring(endOfDesc +1, endOfTown);
mListForm.append(name + " aged: "+ desc + " " + town);
}
rs.closeRecordStore();
}
catch(Exception e){
mAlertConfirmDetailsSaved.setString("Couldn't read details");
System.err.println("Error accessing database");
}
mDisplay.setCurrent(mListForm);
}
Have you tried running it in the debugger? Is the exception happening? Are the three semicolons present in the record? Is there a limit on mDisplay's string size? When setCurrent is called, is the mListForm correct?
In other words, what have you done so far and where is it definitely right, and where does it become wrong?

Categories

Resources