I have a recyclerview with a list of plants, and when you click on a plant it brings up info about it and there is a delete button to remove the plant from the list and database. When this is clicked, the plant disappears from the recyclerview like expected. If I stop the emulator and then run it again without closing Android studio, this change persists, but if I restart Android studio the deleted row is back. Also, when I look at the plants.db file using DB Browser I see no change. Any help is appreciated, thank you!
Here is my delete function from DatabaseHelper.java (full code below:)
public void deleteFromShelf(String get_ID)
{
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
String deleteString = "DELETE FROM PLANTS_OWNED_TABLE WHERE PLANT_ID ='"+get_ID+"'";
db.execSQL(deleteString);
db.setTransactionSuccessful();
// db.execSQL("DROP TABLE IF EXISTS PLANTS_OWNED_TABLE");
db.endTransaction();
Toast.makeText(myContext, "ere i am", Toast.LENGTH_SHORT).show();
}
And here is the adapter I'm calling it from (the removeButton function is toward the end):
public class ShelfFragment2 extends Fragment {
private Plant plant;
private RecyclerView rvShelf2;
private ShelfAdapter2 adShelf2;
private RecyclerView.LayoutManager lmShelf2;
DataBaseHelper dpHelper;
public ShelfFragment2() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dpHelper = new DataBaseHelper(getActivity());
dpHelper.initializeDataBase();
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_shelf2, container, false);
ImageView ivPlant= v.findViewById(R.id.ivPlantW);
TextView tvName = v.findViewById(R.id.tvNameW);
TextView tvSName = v.findViewById(R.id.tvSNameW);
Bundle bundle = getArguments();
plant = bundle.getParcelable("Selected");
Log.i("here", plant.getName());
tvName.setText(plant.getName());
tvSName.setText(plant.getScientific_Name());
rvShelf2 = v.findViewById(R.id.rvShelf2);
rvShelf2.setHasFixedSize(true);
lmShelf2 = new LinearLayoutManager(v.getContext());
adShelf2 = new ShelfAdapter2(getContext(), plant);
rvShelf2.setLayoutManager(lmShelf2);
rvShelf2.setAdapter(adShelf2);
Button removeButton = (Button) v.findViewById(R.id.btRemovePlant);
removeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ShelfFragment shelfFragment = new ShelfFragment();
dpHelper.deleteFromShelf(String.valueOf(plant.getID()));
Toast.makeText(getContext(), "Removed from shelf!", Toast.LENGTH_SHORT).show();
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.flContainer, shelfFragment)
.addToBackStack(null).commit();
}
});
return v;
};
public void setArguments(Context context, Bundle bundle) {
}
}
Entire code of DatabaseHelper.java:
package com.example.plantapp;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.example.plantapp.fragments.SearchFragment;
import com.example.plantapp.objects.Plant;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DataBaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "plants.db";
private static final String DATABASE_DIRECTORY = "/data/data/com.example.plantapp/" +
"databases/";
private static String DATABASE_PATH = DATABASE_DIRECTORY + DATABASE_NAME;
private static String OLD_DATABASE_PATH = DATABASE_DIRECTORY + "old_" + DATABASE_NAME;
private final Context myContext;
private boolean createDatabase = false;
private boolean upgradeDatabase = false;
// PLANT_TABLE
public static final String PLANT_TABLE = "PLANT_TABLE";
public static final String COLUMN_PLANT_ID = "ID";
public static final String COLUMN_PLANT_NAME = "NAME";
public static final String COLUMN_PLANT_SCI_NAME = "SCIENTIFIC_NAME";
public static final String COLUMN_PLANT_LIGHT = "LIGHT";
public static final String COLUMN_PLANT_WATER = "WATER";
public static final String COLUMN_PLANT_FERTILIZER = "FERTILIZER";
public static final String COLUMN_PLANT_TEMPERATURE = "TEMPERATURE";
public static final String COLUMN_PLANT_HUMIDITY = "HUMIDITY";
public static final String COLUMN_PLANT_FLOWERING = "FLOWERING";
public static final String COLUMN_PLANT_PESTS = "PESTS";
public static final String COLUMN_PLANT_DISEASES = "DISEASES";
public static final String COLUMN_PLANT_SOIL = "SOIL";
public static final String COLUMN_PLANT_POT_SIZE = "POT_SIZE";
public static final String COLUMN_PLANT_PRUNING = "PRUNING";
public static final String COLUMN_PLANT_PROPAGATION = "PROPAGATION";
public static final String COLUMN_PLANT_POISON = "POISONOUS_PLANT_INFO";
// USER_TABLE - keeping this table in case we want to provide a more "personalized"
// experience by using the user's name
public static final String USER_TABLE = "USER_TABLE";
public static final String COLUMN_USER_NAME = "USER_NAME";
// WISHLIST_TABLE
public static final String WISHLIST_TABLE = "WISHLIST_TABLE";
public static final String COLUMN_WISHLIST_ID = "WISHLIST_ID";
public static final String COLUMN_WISHLIST_PLANT_ID = "PLANT_ID"; // references PLANT_TABLE
// PLANTS_OWNED_TABLE
public static final String PLANTS_OWNED_TABLE = "PLANTS_OWNED_TABLE";
public static final String COLUMN_PLANTS_OWNED_ID = "PLANTS_OWNED_ID";
public static final String COLUMN_PLANTS_OWNED_PLANT_ID = "PLANT_ID"; // references PLANT_TABLE
/*
* Constructor takes and keeps a reference of the passed context in order to
* access the application assets and resources
*
* */
public DataBaseHelper(#Nullable Context context) {
super(context, DATABASE_NAME, null, 1);
myContext = context;
// Get the path of the database that is based on the context.
DATABASE_PATH = myContext.getDatabasePath(DATABASE_NAME).getAbsolutePath();
}
/*
* Upgrade the database in internal storage if it exists but is not current.
* Create a new empty database in internal storage if it does not exist.
* */
public void initializeDataBase() {
/*
* Creates or updates the database in internal storage if it is needed
* before opening the database. In all cases opening the database copies
* the database in internal storage to the cache.
* */
getWritableDatabase();
if (createDatabase) {
/*
* If the database is created by the copy method, then the creation
* code needs to go here. This method consists of copying the new
* database from assets into internal storage and then caching it.
* */
try {
/*
* Write over the empty data that created in internal
* storage with the one in assets and then cache it.
* */
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
} else if (upgradeDatabase) {
/*
* If the database is upgraded by the copy and reload method, then
* the upgrade code needs to go here. This method consists of
* renaming the old database in internal storage, create an empty
* new database in internal storage, copying the database from
* assets to the new database in internal storage, caching the new
* database from internal storage, loading the data from the old
* database into the new database in the cache and then deleting the
* old database from internal storage.
* */
try {
FileHelper.copyFile(DATABASE_PATH, OLD_DATABASE_PATH);
copyDataBase();
SQLiteDatabase old_db = SQLiteDatabase.openDatabase(OLD_DATABASE_PATH, null, SQLiteDatabase.OPEN_READWRITE);
SQLiteDatabase new_db = SQLiteDatabase.openDatabase(DATABASE_PATH, null, SQLiteDatabase.OPEN_READWRITE);
/*
* Add code to load data into the new database from the old
* database and then delete the old database from internal
* storage after all data has been transferred
* */
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/*
* Copies your database from your local assets-folder to the just created
* empty database in the system folder, from where it can be accessed and
* handled. This is done by transferring bytestream.
* */
private void copyDataBase() throws IOException {
/*
* Close SQLiteOpenHelper so it will commit the created empty database
* to internal storage.
* */
close();
/*
* Open the database in the assets folder as the input stream.
*/
InputStream myInput = myContext.getAssets().open(DATABASE_NAME);
/*
* Open the empty db in internal storage as the output stream.
*/
OutputStream myOutput = new FileOutputStream(DATABASE_PATH);
/*
* Copy over the empty db in internal storage with the database in the
* assets folder.
* */
FileHelper.copyFile(myInput, myOutput);
/*
* Access the copied database so SQLiteHelper will cache it and mark it
* as created.
* */
getWritableDatabase().close();
}
/*
* This is where the creation of tables and the initial population of the
* tables should happen, if a database is being created from scratch instead
* of being copied from the application package assets. Copying a database
* from the application package assets to internal storage inside this
* method will result in a corrupted database.
* NOTE: This method is normally only called when a database has not already
* been created. when the database has been copied, then this method is
* called the first time a reference to the database is retrieved after the
* database is copied since the database last cached by SQLiteOpenHelper is
* different than the database in internal storage.
* */
#Override
public void onCreate(SQLiteDatabase db) {
/*
* Signal that a new database needs to be copied. The copy process must
* be performed after the database in the cache has been closed causing
* it to be committed to internal storage. Otherwise the database in
* internal storage will not have the same creation timestamp as the one
* in the cache causing the database in internal storage to be marked as
* corrupted.
* */
createDatabase = true;
/*String createTableStatement = "CREATE TABLE " + PLANT_TABLE + " (" + COLUMN_PLANT_ID + " INTEGER PRIMARY KEY," +
COLUMN_PLANT_NAME + " TEXT, " + COLUMN_PLANT_SCI_NAME + " TEXT, " +
COLUMN_PLANT_LIGHT + " TEXT, " + COLUMN_PLANT_WATER + " TEXT, " +
COLUMN_PLANT_FERTILIZER + " TEXT, " + COLUMN_PLANT_TEMPERATURE + " TEXT, " +
COLUMN_PLANT_HUMIDITY + " TEXT, " + COLUMN_PLANT_FLOWERING + " TEXT, " +
COLUMN_PLANT_PESTS + " TEXT, " + COLUMN_PLANT_DISEASES + " TEXT, " +
COLUMN_PLANT_SOIL + " TEXT, " + COLUMN_PLANT_POT_SIZE + " TEXT, " +
COLUMN_PLANT_PRUNING + " TEXT, " + COLUMN_PLANT_PROPAGATION + " TEXT, " +
COLUMN_PLANT_POISON + " TEXT)";
db.execSQL(createTableStatement);*/
}
/*
* Called only if version number was changed and the database has already
* been created. Copying a database from the application package assets to
* the internal data system inside this method will result in a corrupted
* database in the internal data system.
* */
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
/*
* Signal that the database needs to be upgraded for the copy method of
* creation. The copy process must be performed after the database has
* been opened or the database will be corrupted.
* */
upgradeDatabase = true;
/*
* Code to update the database via execution of sql statements goes
* here.
* */
}
/*
* Called everytime the database is opened by getReadableDatabase or
* getWritableDatabase. This is called after onCreate or onUpgrade is
* called.
* */
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
public List<String> getPlantNamesLike(String whatUserTyped) {
//List<PlantModel> returnList = new ArrayList<>();
List<String> returnList = new ArrayList<>();
String[] nameArg = {whatUserTyped + "%"};
// get data from the database
String queryString = "SELECT " + COLUMN_PLANT_NAME + " FROM " + PLANT_TABLE +
" WHERE " + COLUMN_PLANT_NAME + " LIKE ?";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(queryString, nameArg);
if (cursor.moveToFirst()) {
do{
String plantName = cursor.getString(0);
//PlantModel newPlant = new PlantModel(plantName);
//returnList.add(newPlant);
returnList.add(plantName);
} while (cursor.moveToNext());
}
else {
// failure. do not add anything to list
returnList.add("No matching plant found");
}
cursor.close();
db.close();
return returnList;
}
public List<Plant> getPlants()
{
List<Plant> plants = new ArrayList<>();
String queryString = "SELECT * FROM " + PLANT_TABLE;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(queryString, null);
if(cursor.moveToFirst()) {
do {
Plant currentPlant = new Plant();
currentPlant.setID(cursor.getInt(0));
currentPlant.setName(cursor.getString(1));
currentPlant.setScientific_Name(cursor.getString(2));
currentPlant.setLight(cursor.getString(3));
currentPlant.setWater(cursor.getString(4));
currentPlant.setFertilizer(cursor.getString(5));
currentPlant.setTemperature(cursor.getString(6));
currentPlant.setHumidity(cursor.getString(7));
currentPlant.setFlowering(cursor.getString(8));
currentPlant.setPests(cursor.getString(9));
currentPlant.setDiseases(cursor.getString(10));
currentPlant.setSoil(cursor.getString(11));
currentPlant.setPot_size(cursor.getString(12));
currentPlant.setPruning(cursor.getString(13));
currentPlant.setPropagation(cursor.getString(14));
currentPlant.setPoisonous_plant_info(cursor.getString(15));
plants.add(currentPlant);
} while (cursor.moveToNext());
}
return plants;
}
public List<Plant> getOwnedPlants()
{
List<Plant> ownedPlants = new ArrayList<>();
String queryString = "SELECT * FROM PLANT_TABLE INNER JOIN PLANTS_OWNED_TABLE ON PLANTS_OWNED_TABLE.PLANT_ID = PLANT_TABLE.ID";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(queryString, null);
android.util.Log.d("CURSOR", String.format("size = %d", cursor.getCount()));
if(cursor.moveToFirst()) {
do {
Plant currentPlant = new Plant();
currentPlant.setID(cursor.getInt(0));
currentPlant.setName(cursor.getString(1));
currentPlant.setScientific_Name(cursor.getString(2));
currentPlant.setLight(cursor.getString(3));
currentPlant.setWater(cursor.getString(4));
currentPlant.setFertilizer(cursor.getString(5));
currentPlant.setTemperature(cursor.getString(6));
currentPlant.setHumidity(cursor.getString(7));
currentPlant.setFlowering(cursor.getString(8));
currentPlant.setPests(cursor.getString(9));
currentPlant.setDiseases(cursor.getString(10));
currentPlant.setSoil(cursor.getString(11));
currentPlant.setPot_size(cursor.getString(12));
currentPlant.setPruning(cursor.getString(13));
currentPlant.setPropagation(cursor.getString(14));
currentPlant.setPoisonous_plant_info(cursor.getString(15));
ownedPlants.add(currentPlant);
} while (cursor.moveToNext());
}
return ownedPlants;
}
public List<Plant> getWishlistPlants()
{
List<Plant> wishlistPlants = new ArrayList<>();
String queryString = "SELECT * FROM PLANT_TABLE INNER JOIN WISHLIST_TABLE ON WISHLIST_TABLE.PLANT_ID = PLANT_TABLE.ID";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(queryString, null);
android.util.Log.d("CURSOR", String.format("size = %d", cursor.getCount()));
if(cursor.moveToFirst()) {
do {
// TODO: make this code less repetitive
Plant currentPlant = new Plant();
currentPlant.setID(cursor.getInt(0));
currentPlant.setName(cursor.getString(1));
currentPlant.setScientific_Name(cursor.getString(2));
currentPlant.setLight(cursor.getString(3));
currentPlant.setWater(cursor.getString(4));
currentPlant.setFertilizer(cursor.getString(5));
currentPlant.setTemperature(cursor.getString(6));
currentPlant.setHumidity(cursor.getString(7));
currentPlant.setFlowering(cursor.getString(8));
currentPlant.setPests(cursor.getString(9));
currentPlant.setDiseases(cursor.getString(10));
currentPlant.setSoil(cursor.getString(11));
currentPlant.setPot_size(cursor.getString(12));
currentPlant.setPruning(cursor.getString(13));
currentPlant.setPropagation(cursor.getString(14));
currentPlant.setPoisonous_plant_info(cursor.getString(15));
wishlistPlants.add(currentPlant);
} while (cursor.moveToNext());
}
return wishlistPlants;
}
public void deleteFromShelf(String get_ID)
{
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
String deleteString = "DELETE FROM PLANTS_OWNED_TABLE WHERE PLANT_ID ='"+get_ID+"'";
db.execSQL(deleteString);
db.setTransactionSuccessful();
// db.execSQL("DROP TABLE IF EXISTS PLANTS_OWNED_TABLE");
db.endTransaction();
Toast.makeText(myContext, "ere i am", Toast.LENGTH_SHORT).show();
}
}
Related
Im trying to store a particular columns data in a method and call that method in another classes String [] where a spinner will access that array to display the columns rows for the user to select which will then get stored in another database.
Im getting nulpointerexception and when I try and open() the database the database gets locked. Sorry for sounding like a complete amateur, relatively new to android. Thank you in advance for any help.
Here is my code when I call getInstance() and getCPnames() in my main class
String[] carParks = CarParkDb.getInstance().getCpnames();
Here is my code for the database:
package com.example.parkangel;
import java.util.ArrayList;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class CarParkDb {
public static final String KEY_ID = "_id";
public static final String KEY_CPNAME = "cpname";
public static final String KEY_COST = "cost";
private static final String DATABASE_NAME = "CPDB";
private static final String DATABASE_TABLE = "CPTable";
private static final int DATABASE_VERSION = 1;
private CPDbHelper cpdbHelper;
private Context ourContext;
private SQLiteDatabase ourDatabase;
private static CarParkDb instance;
private static class CPDbHelper extends SQLiteOpenHelper{
public CPDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// TODO Auto-generated constructor stub
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("CREATE TABLE " + DATABASE_TABLE + " (" +
KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_CPNAME + " TEXT NOT NULL, " + KEY_COST + " TEXTNOT NULL);");
db.execSQL("INSERT INTO " + DATABASE_TABLE + " Values('1','LearningResource Center','2');");
db.execSQL("INSERT INTO " + DATABASE_TABLE + " Values ('2','ParkandRide','1');");
db.execSQL("INSERT INTO " + DATABASE_TABLE + " Values ('3','deHavilland Campus','2');");
db.execSQL("INSERT INTO " + DATABASE_TABLE + " Values('4','MultiStorey Building','2');");
db.execSQL("INSERT INTO " + DATABASE_TABLE + " Values('5','Reception','2');");
}
public void onOpen(final SQLiteDatabase db) {
super.onOpen(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);
onCreate(db);
}
}
public CarParkDb (final Context c)
{
this.cpdbHelper= new CPDbHelper(c);
establishDb();
//ourContext = c;
}
public void establishDb()
{
if (this.ourDatabase == null)
{
this.ourDatabase = this.cpdbHelper.getWritableDatabase();
}
}
public CarParkDb() {
// TODO Auto-generated constructor stub
}
public CarParkDb open() throws SQLException
{
System.out.println ("running open");
cpdbHelper = new CPDbHelper(ourContext);
ourDatabase = cpdbHelper.getWritableDatabase();
return this;
}
public void close()
{
ourDatabase.close();
}
/*public long createEntry(String cpname, String cost){
ContentValues cv = new ContentValues();
cv.put(KEY_CPNAME, cpname);
cv.put(KEY_COST, cost);
return ourDatabase.insert(DATABASE_TABLE, null, cv);
}*/
public String getData() {
// TODO Auto-generated method stub
//open();
String[] columns = new String[] {KEY_ID, KEY_CPNAME, KEY_COST};
Cursor c = ourDatabase.query(DATABASE_TABLE, columns, null,null,null, null, null);
String result = " ";
int iRow = c.getColumnIndexOrThrow(KEY_ID);
int iCpname = c.getColumnIndexOrThrow(KEY_CPNAME);
int iCost = c.getColumnIndexOrThrow(KEY_COST);
for(c.moveToFirst(); !c.isAfterLast(); c.moveToNext()){
result = result + c.getString(iRow) + " " +c.getString(iCpname) + " " + c.getString(iCost) + " " + "\n";
c.close();
ourDatabase.close();
}
return result;
}
public static CarParkDb getInstance()
{
synchronized(CarParkDb.class)
{
if (instance == null)
{
instance = new CarParkDb();
}
return instance;
}
}
public String[] getCpnames()
{
//open();
if (ourDatabase == null) System.out.println ("is null");
Cursor c = null;
ArrayList<String> list = new ArrayList<String>();
ourDatabase = cpdbHelper.getReadableDatabase();
//SQLiteDatabase db = cpdbHelper.getReadableDatabase();
String query = "SELECT " + KEY_CPNAME + " FROM " + DATABASE_TABLE;
{
c = this.ourDatabase.rawQuery(query, null);
int iCpname = c.getColumnIndexOrThrow(KEY_CPNAME);
if (c.moveToFirst())
{
do
{
list.add(c.getString(iCpname));;
}
while (c.moveToNext());
}
if (c != null && !c.isClosed())
{
c.close();
ourDatabase.close();
}
return list.toArray(new String[]{});
}
}
}
**LogCat**
03-12 01:32:39.759: E/AndroidRuntime(4176): Caused by:java.lang.NullPointerException
03-12 01:32:39.759: E/AndroidRuntime(4176):
at com.example.parkangel.CarParkDb.getCpnames(CarParkDb.java:191)
03-12 01:32:39.759: E/AndroidRuntime(4176):
at com.example.parkangel.BookTicket.<init>(BookTicket.java:22)
03-12 01:32:39.759: E/AndroidRuntime(4176):
at java.lang.Class.newInstanceImpl(Native Method)
03-12 01:32:39.759: E/AndroidRuntime(4176):
at java.lang.Class.newInstance(Class.java:1208)
The database doesn't get locked. The "locked" thinkg is just a NPE in a method called getDatabaseLocked() and it's caused by a null Context passed to SQLiteOpenHelper in constructor that manifests itself with getWritableDatabase() or getReadableDatabase().
To fix the NPE in getDatabaseLocked(), make sure ourContext in open() is not null. As of now, you've never initialized ourContext and it's always null. Uncomment the //ourContext = c; in constructor and move it above the establishDb(), remove the other no-argument constructor and pass in a Context for example as suggested by Merlevede.
The NPE stacktrace in your question is when you call getCpNames() with open() commented out and are trying to call a method on a null ourDatabase object reference. Uncomment the open() there once it's fixed. The stacktrace also says you're trying to call getCpNames() in object initialization phase e.g. when initializing a member variable. That might be too early, for example an activity cannot be used as a Context until onCreate().
Some other things in your code you'd hit next:
in onCreate(): + KEY_COST + " TEXTNOT NULL) - add space between TEXT and NOT
in getData(): you're closing the cursor and database in the loop where you're accessing the cursor. Do it after the loop. The code shouldn't compile as you're not returning anything from a non-void function in case moveToFirst() returns false.
Try updating your getInstance() code with this:
public static CarParkDb getInstance(Context c) // <-- added context as parameter
{
synchronized(CarParkDb.class)
{
if (instance == null)
{
instance = new CarParkDb(c); // <-- used context in constructor
}
return instance;
}
}
The problem seems to be that you're using a constructor (for CarParkDb) that does nothing. You have another constructor that takes a Context as parameter and initializes some of the objects that you're using later.
I'm trying to load an existing SQLite database into Android Studio. However, the database adapter is returning an empty database instead of giving me a copy of my populated database.
I've got my permissions
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
My DBAdapter
package com.example.testbed;
import android.app.SearchManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
public class DBAdapter extends SQLiteOpenHelper{
public static final String TAG = DBAdapter.class.getSimpleName();
public static int flag;
private static SQLiteDatabase db;
private static String DB_NAME = "recipes.db";
public static String TABLE_NAME = "DrinkRecipes_Loaded";
public static String DB_PATH = "";
public static String COL_WORD = "WORD";
String outFileName = "";
public static Context myContext;
private String loadedTable = "DrinkRecipes_Loaded";
private String specialtyTable = "DrinkRecipes_Specialty";
private String beautyTable = "DrinkRecipes_Beauty";
private String kidsTable = "DrinkRecipes_Kids";
private String shakesTable = "DrinkRecipes_Shakes";
public DBAdapter(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
ContextWrapper cw = new ContextWrapper(context);
DB_PATH = "/data/data" + BuildConfig.APPLICATION_ID + "/databases/";
Log.e(TAG, "Databasehelper: DB_PATH " + DB_PATH);
outFileName = DB_PATH + DB_NAME;
File file = new File(DB_PATH);
Log.e(TAG, "Databasehelper: " + file.exists());
if (!file.exists()) {
file.mkdir();
}
}
/**
* Creates a empty database on the system and rewrites it with your own database.
*/
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if (dbExist) {
//do nothing - database already exist
} else {
//By calling this method and empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/**
* Check if the database already exist to avoid re-copying the file each time you open the application.
*
* #return true if it exists, false if it doesn't
*/
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
checkDB = SQLiteDatabase.openDatabase(outFileName, null, SQLiteDatabase.OPEN_READONLY);
} catch (SQLiteException e) {
try {
copyDataBase();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (checkDB != null) {
checkDB.close();
}
return checkDB != null ? true : false;
}
/**
* Copies your database from your local assets-folder to the just created empty database in the
* system folder, from where it can be accessed and handled.
* This is done by transfering bytestream.
*/
private void copyDataBase() throws IOException {
Log.i("Database",
"New database is being copied to device!");
byte[] buffer = new byte[1024];
OutputStream myOutput = null;
int length;
// Open your local db as the input stream
InputStream myInput = null;
try {
myInput = myContext.getAssets().open(DB_NAME);
// transfer bytes from the inputfile to the
// outputfile
myOutput = new FileOutputStream(DB_PATH + DB_NAME);
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myOutput.close();
myOutput.flush();
myInput.close();
Log.i("Database",
"New database has been copied to device!");
} catch (IOException e) {
e.printStackTrace();
}
}
public void openDataBase() throws SQLException {
//Open the database
String myPath = DB_PATH + DB_NAME;
db = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
Log.e(TAG, "openDataBase: Open " + db.isOpen());
}
// we have created a new method for reading loaded recipes.
public ArrayList<recipeModal> readLoadedRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor cursorRecipes = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
// on below line we are creating a new array list.
ArrayList<recipeModal> recipeModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (cursorRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
recipeModalArrayList.add(new recipeModal(cursorRecipes.getString(0),
cursorRecipes.getString(1),
cursorRecipes.getString(2),
cursorRecipes.getString(3),
cursorRecipes.getString(4),
cursorRecipes.getString(5),
cursorRecipes.getString(6),
cursorRecipes.getString(7),
cursorRecipes.getString(8)));
} while (cursorRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
cursorRecipes.close();
return recipeModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<beautyModal> readBeautyRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor beautyRecipes = db.rawQuery("SELECT * FROM " + beautyTable, null);
// on below line we are creating a new array list.
ArrayList<beautyModal> beautyModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (beautyRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
beautyModalArrayList.add(new beautyModal(beautyRecipes.getString(0),
beautyRecipes.getString(1),
beautyRecipes.getString(2),
beautyRecipes.getString(3),
beautyRecipes.getString(4),
beautyRecipes.getString(5)));
} while (beautyRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
beautyRecipes.close();
return beautyModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<specialtyModal> readSpecialtyRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor specialtyRecipes = db.rawQuery("SELECT * FROM " + specialtyTable, null);
// on below line we are creating a new array list.
ArrayList<specialtyModal> specialtyModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (specialtyRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
specialtyModalArrayList.add(new specialtyModal(specialtyRecipes.getString(0),
specialtyRecipes.getString(1),
specialtyRecipes.getString(2),
specialtyRecipes.getString(3),
specialtyRecipes.getString(4),
specialtyRecipes.getString(5),
specialtyRecipes.getString(6),
specialtyRecipes.getString(7)));
} while (specialtyRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
specialtyRecipes.close();
return specialtyModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<kidsModal> readKidsRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor kidsRecipes = db.rawQuery("SELECT * FROM " + kidsTable, null);
// on below line we are creating a new array list.
ArrayList<kidsModal> kidsModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (kidsRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
kidsModalArrayList.add(new kidsModal(kidsRecipes.getString(0),
kidsRecipes.getString(1),
kidsRecipes.getString(2),
kidsRecipes.getString(3),
kidsRecipes.getString(4)));
} while (kidsRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
kidsRecipes.close();
return kidsModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<shakesModal> readShakesRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor shakesRecipes = db.rawQuery("SELECT * FROM " + shakesTable, null);
// on below line we are creating a new array list.
ArrayList<shakesModal> shakesModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (shakesRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
shakesModalArrayList.add(new shakesModal(shakesRecipes.getString(0),
shakesRecipes.getString(1),
shakesRecipes.getString(2),
shakesRecipes.getString(3),
shakesRecipes.getString(4)));
} while (shakesRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
shakesRecipes.close();
return shakesModalArrayList;
}
#Override
public synchronized void close() {
if (db != null)
db.close();
super.close();
}
public void onCreate(SQLiteDatabase arg0) {
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
}
}
And when I call it, the only thing that I get is a crashing application and an empty database written to the correct path. I'm very desperate for some solutions to this problem and would appreciate any feedback.
I've tried a few different solutions that seemed promising on Google, but no luck.
EDIT: Stack trace at error
E/SQLiteLog: (1) no such table: DrinkRecipes_Loaded in "SELECT * FROM DrinkRecipes_Loaded"
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testbed, PID: 4732
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.testbed/com.example.testbed.ViewRecipes}: android.database.sqlite.SQLiteException: no such table: DrinkRecipes_Loaded (code 1 SQLITE_ERROR[1]): , while compiling: SELECT * FROM DrinkRecipes_Loaded
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3851)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4027)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:247)
at android.app.ActivityThread.main(ActivityThread.java:8676)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: android.database.sqlite.SQLiteException: no such table: DrinkRecipes_Loaded (code 1 SQLITE_ERROR[1]): , while compiling: SELECT * FROM DrinkRecipes_Loaded
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1463)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:901)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:590)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:62)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:46)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2063)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:2002)
at com.example.testbed.DBAdapter.readLoadedRecipes(DBAdapter.java:128)
at com.example.testbed.ViewRecipes.onCreate(ViewRecipes.java:84)
at android.app.Activity.performCreate(Activity.java:8215)
at android.app.Activity.performCreate(Activity.java:8199)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3824)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4027)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:247)
at android.app.ActivityThread.main(ActivityThread.java:8676)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
There are a few issues with your code.
You are trying to make the database file a directory rather than making the parent the directory.
You are not catering for later android versions that default to using WAL (write-ahead logging) as opposed to journal logging by using getReabableDatabase when creating (copying the asset).
getReadableDatabase will do as it says and create the database. The copy then overwrites this (if it works).
However, in WAL mode two additional files are created for the WAL logging and populated accordingly. Then:
After the asset has been copied SQLite determines that the WAL file is not the WAL file for the copied database and therefore fails to open the potentially corrupt database.
The Android API detects this failure but does what it has been asked and creates an empty database and hence the Table not found.
Using getReadableDatabase just to circumvent the databases directory not existing is overkill/inefficient as all the processing to create a new database is undertaken.
Here's a rewrite of your code noting that:-
all of the access methods have been commented out (rather than create all the classes)
the createDatabase and checkDatabase methods have been commented out. They have been replaced by the getDatabase method.
the CopyDatabase method has been changed to return false if an io error occurs (getDatabase then throws a runTimeException)
The getDatabase method
checks to see if the database exists and returns if it does. Otherwise, it
checks to see if the parentFile (databases directory) exists,
if not then it makes the directory (and potentially other ones although they should always exist)
calls the CopyDatabase method
So the code (with much of the original code commented out) is :-
public class DBAdapter extends SQLiteOpenHelper {
public static final String TAG = DBAdapter.class.getSimpleName();
public static int flag;
private static SQLiteDatabase db;
private static String DB_NAME = "recipes.db";
public static String TABLE_NAME = "DrinkRecipes_Loaded";
public static String DB_PATH = "";
public static String COL_WORD = "WORD";
String outFileName = "";
public static Context myContext;
private String loadedTable = "DrinkRecipes_Loaded";
private String specialtyTable = "DrinkRecipes_Specialty";
private String beautyTable = "DrinkRecipes_Beauty";
private String kidsTable = "DrinkRecipes_Kids";
private String shakesTable = "DrinkRecipes_Shakes";
public DBAdapter(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
//ContextWrapper cw = new ContextWrapper(context); Not used
DB_PATH = context.getDatabasePath(DB_NAME).getPath(); // no need to hard code anything bar the DB name
/*
//DB_PATH = "/data/data" + BuildConfig.APPLICATION_ID + "/databases/";
Log.e(TAG, "Databasehelper: DB_PATH " + DB_PATH);
outFileName = DB_PATH + DB_NAME;
File file = new File(DB_PATH);
Log.e(TAG, "Databasehelper: " + file.exists());
if (!file.exists()) {
file.getParentFile().mkdirs(); // CHANGED you don't want the database to be a directory
}
*/
getDatabase(context);
}
/*<<<<< ADDED >>>>>*/
private void getDatabase(Context context) {
File dbFile = new File(context.getDatabasePath((DB_NAME)).getPath());
if (dbFile.exists()) return; // Database found so all done
// Otherwise ensure that the database directory exists (does not by default until later versions)
if (!dbFile.getParentFile().exists()) {
dbFile.getParentFile().mkdirs();
}
if (!copyDataBase()) {
throw new RuntimeException("Unable to copy database from the asset (check the stack-trace).");
}
}
/**
* Creates a empty database on the system and rewrites it with your own database.
*/
/* NOTE NEEDED/UNSAFE as getDatabase create a database and for later version of Android it will be in WAL mode
thus the additional WAL files are created. When the database is then opened SQLite see that the WAL files are not
for the copied database and thus the open fails. Android's API then creates an new empty database file
*/
/*
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if (dbExist) {
//do nothing - database already exist
} else {
//By calling this method and empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
this.getReadableDatabase(); // Causes issues if in WAL mode and result in database
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
*/
/**
* Check if the database already exist to avoid re-copying the file each time you open the application.
*
* #return true if it exists, false if it doesn't
*/
/*
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
checkDB = SQLiteDatabase.openDatabase(outFileName, null, SQLiteDatabase.OPEN_READONLY);
} catch (SQLiteException e) {
try {
copyDataBase();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (checkDB != null) {
checkDB.close();
}
return checkDB != null ? true : false;
}
*/
/**
* Copies your database from your local assets-folder to the just created empty database in the
* system folder, from where it can be accessed and handled.
* This is done by transfering bytestream.
*/
private boolean /* <<<<< CHANGED SIGNATURE */ copyDataBase() {
Log.i("Database",
"New database is being copied to device!");
byte[] buffer = new byte[4096]; //Probably more efficient as default page size will be 4k
OutputStream myOutput = null;
int length;
// Open your local db as the input stream
InputStream myInput = null;
try {
myInput = myContext.getAssets().open(DB_NAME);
// transfer bytes from the inputfile to the
// outputfile
myOutput = new FileOutputStream(DB_PATH);
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myOutput.close();
myOutput.flush();
myInput.close();
Log.i("Database",
"New database has been copied to device!");
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
public void openDataBase() throws SQLException {
//Open the database
String myPath = DB_PATH + DB_NAME;
db = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
Log.e(TAG, "openDataBase: Open " + db.isOpen());
}
/*
// we have created a new method for reading loaded recipes.
public ArrayList<recipeModal> readLoadedRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor cursorRecipes = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
// on below line we are creating a new array list.
ArrayList<recipeModal> recipeModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (cursorRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
recipeModalArrayList.add(new recipeModal(cursorRecipes.getString(0),
cursorRecipes.getString(1),
cursorRecipes.getString(2),
cursorRecipes.getString(3),
cursorRecipes.getString(4),
cursorRecipes.getString(5),
cursorRecipes.getString(6),
cursorRecipes.getString(7),
cursorRecipes.getString(8)));
} while (cursorRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
cursorRecipes.close();
return recipeModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<beautyModal> readBeautyRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor beautyRecipes = db.rawQuery("SELECT * FROM " + beautyTable, null);
// on below line we are creating a new array list.
ArrayList<beautyModal> beautyModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (beautyRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
beautyModalArrayList.add(new beautyModal(beautyRecipes.getString(0),
beautyRecipes.getString(1),
beautyRecipes.getString(2),
beautyRecipes.getString(3),
beautyRecipes.getString(4),
beautyRecipes.getString(5)));
} while (beautyRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
beautyRecipes.close();
return beautyModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<specialtyModal> readSpecialtyRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor specialtyRecipes = db.rawQuery("SELECT * FROM " + specialtyTable, null);
// on below line we are creating a new array list.
ArrayList<specialtyModal> specialtyModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (specialtyRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
specialtyModalArrayList.add(new specialtyModal(specialtyRecipes.getString(0),
specialtyRecipes.getString(1),
specialtyRecipes.getString(2),
specialtyRecipes.getString(3),
specialtyRecipes.getString(4),
specialtyRecipes.getString(5),
specialtyRecipes.getString(6),
specialtyRecipes.getString(7)));
} while (specialtyRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
specialtyRecipes.close();
return specialtyModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<kidsModal> readKidsRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor kidsRecipes = db.rawQuery("SELECT * FROM " + kidsTable, null);
// on below line we are creating a new array list.
ArrayList<kidsModal> kidsModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (kidsRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
kidsModalArrayList.add(new kidsModal(kidsRecipes.getString(0),
kidsRecipes.getString(1),
kidsRecipes.getString(2),
kidsRecipes.getString(3),
kidsRecipes.getString(4)));
} while (kidsRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
kidsRecipes.close();
return kidsModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<shakesModal> readShakesRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor shakesRecipes = db.rawQuery("SELECT * FROM " + shakesTable, null);
// on below line we are creating a new array list.
ArrayList<shakesModal> shakesModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (shakesRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
shakesModalArrayList.add(new shakesModal(shakesRecipes.getString(0),
shakesRecipes.getString(1),
shakesRecipes.getString(2),
shakesRecipes.getString(3),
shakesRecipes.getString(4)));
} while (shakesRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
shakesRecipes.close();
return shakesModalArrayList;
}
*/
#Override
public synchronized void close() {
if (db != null)
db.close();
super.close();
}
public void onCreate(SQLiteDatabase arg0) {
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
}
}
The above has been tested but using a substitute database renamed to recipes.db (NorthWind as it's pretty large) the log for a new install :-
2022-03-27 10:22:11.223 I/Database: New database is being copied to device!
2022-03-27 10:22:11.455 I/Database: New database has been copied to device!
AppInspection confirms that the database has been successfully copied (albeit not your database):-
Likewise Device File Explorer shows :-
EDIT: I still can't find a solution to this issue. For some reason, the database that is queried is empty, despite being full and in the correct place. If you can see any issue with my database helper or anything else that I could try, I would be very grateful.
I am developing an app that returns information from an SQLite database relating to species of bird. When I query this database, I am getting error messages: 'SQLiteLog: (1) no such column: 'size'' etc.
I have verified that my rawQuery() queries are well formed and should be returning the info that I expect by running the queries in an SQLite browser, and by consulting advice on Stackoverflow. I have tried to use the alternative database asset class, SQLiteAssetHelper, but have had the same problems as I have with SQLiteOpenHelper.
I am thinking it may be related to:
the testing phone - Moto g6 play - this phone is not rooted. Could this be an issue migrating the database over to the phone for use by the app during testing?
The formation of the database - this was populated by a python script written by me. Could some metadata etc be malformed or incompatible?
package com.example.newbuild;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DatabaseHelper extends SQLiteOpenHelper {
//db info
private static final String DATABASE_NAME = "birdsDB.db";
// FileInputStream fis = new FileInputStream(new File(DATABASE_NAME));
// Log info for debugging:
private static final String TAG = "DatabaseHelper";
// set variables to name database
private static final int DATABASE_VERSION = 3;
// name of table 1:
private static final String MAIN_TABLE = "main";
// name of bird image table:
private static final String PIC_TABLE = "picLinks";
// names of MAIN columns:
private static final String ID = "id";
private static final String COMMON = "common";
private static final String SCINAME = "sciname";
private static final String FAMILY = "family";
private static final String BIRDCATEG = "category";
private static final String SIZE = "size";
private static final String DESC = "description";
private static final String RANGEPIC = "rangepicid";
private static final String SIGHTED = "sighted";
// names of BIRD IMAGE columns
private static final String BIRD_IMAGE_NO = "picKey";
private static final String BIRD_ID = "birdId";
private static final String IMAGE_LINK = "link";
private Context mContext;
private SQLiteDatabase mDB;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Log.i(TAG, "now calling database helper");
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.d(TAG, "attempting to create table from onCreate");
String CREATE_MAIN_TABLE =
"CREATE TABLE " +
MAIN_TABLE +
"(" +
ID + "INTEGER PRIMARY KEY," +
COMMON + " TEXT," +
SCINAME + " TEXT," +
FAMILY + "TEXT," +
BIRDCATEG + "TEXT," +
SIZE + "TEXT," +
DESC + "TEXT," +
RANGEPIC + "TEXT," +
SIGHTED + "TEXT" +
")";
db.execSQL(CREATE_MAIN_TABLE);
Log.d("table", CREATE_MAIN_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
Log.i(TAG, "now calling onUpgrade");
db.execSQL("DROP TABLE IF EXISTS " + MAIN_TABLE);
onCreate(db);
}
}
public ArrayList<String> getCategory(String[] name) {
String TABLE_BIRDS = "main";
ArrayList<String> categories = new ArrayList<>();
if (name[0] != null) {
Log.d(LOG_TAG, name[0]);
} else {
Log.d(LOG_TAG, "name[0] has not been passed");
}
Log.d(LOG_TAG, "SELECT DISTINCT " + name[0] + " FROM " + TABLE_BIRDS);
Cursor x = db.rawQuery("SELECT DISTINCT " + name[0] + " FROM " + TABLE_BIRDS, null);
if (x.getCount() == 0) {
Log.i(LOG_TAG, "The cursor is not returning any data");
}
while (x.moveToNext()) {
String category = x.getString(0);
categories.add(category);
Log.i("cursor loop", category);
}
return categories;
}
When the above code is passed the string 'category', my database should return six strings of different categories of bird species. Instead, I find error messages including 'E/SQLiteLog: (1) no such column: category'.
I am thinking it may be related to: the testing phone - Moto g6 play -
this phone is not rooted. Could this be an issue migrating the
database over to the phone for use by the app during testing?
I don't think so as you would typically get a table not found before a column not found.
I'd suggest temporarily changing the query to :-
Cursor x = db.rawQuery("SELECT * FROM " + TABLE_BIRDS + " LIMIT 10", null);
Followed by :-
DatabaseUtils.dumpCursor(x);
The first change will extract all columns from 10 rows from the table.
The second line will output the data in the cursor to the log, including the column names.
I suspect that the column names are incorrect or missing. In which case you need to ensure that the file in the assets folder is correct, when it is make sure that you delete the database (delete the Apps data or uninstall the App) and then rerun the App.
I am writing an Android application that queries a database with 170,000 dictionary words. (It has 31 columns: _id, word, sortedWord, length, and count_A, count_B, count_C, ... count_Z)
Here is the code for my Dictionary database helper
public class DictionaryDbHelper extends SQLiteOpenHelper {
private static final String TAG = "DataBaseHelper";
// The Android's default system path of your application database.
private static String DB_PATH = "/data/data/com.rabbitfighter.wordsleuth/databases/";
// Database name
private static String DB_NAME = "dictionary.db";
// Database, and context
private SQLiteDatabase myDataBase;
private final Context myContext;
// Table name
public static final String TABLE_NAME = "dictionary";
// Table columns
public static final String UID = "_id";
// Word
public static final String COLUMN_NAME_WORD = "word";
// Length
public static final String COLUMN_NAME_WORD_LENGTH = "length";
// For all the letters, A-Z.
public static final String COLUMN_NAME_COUNT_A = "count_A";
public static final String COLUMN_NAME_COUNT_B = "count_B";
public static final String COLUMN_NAME_COUNT_C = "count_C";
public static final String COLUMN_NAME_COUNT_D = "count_D";
public static final String COLUMN_NAME_COUNT_E = "count_E";
public static final String COLUMN_NAME_COUNT_F = "count_F";
public static final String COLUMN_NAME_COUNT_G = "count_G";
public static final String COLUMN_NAME_COUNT_H = "count_H";
public static final String COLUMN_NAME_COUNT_I = "count_I";
public static final String COLUMN_NAME_COUNT_J = "count_J";
public static final String COLUMN_NAME_COUNT_K = "count_K";
public static final String COLUMN_NAME_COUNT_L = "count_L";
public static final String COLUMN_NAME_COUNT_M = "count_M";
public static final String COLUMN_NAME_COUNT_N = "count_N";
public static final String COLUMN_NAME_COUNT_O = "count_O";
public static final String COLUMN_NAME_COUNT_P = "count_P";
public static final String COLUMN_NAME_COUNT_Q = "count_Q";
public static final String COLUMN_NAME_COUNT_R = "count_R";
public static final String COLUMN_NAME_COUNT_S = "count_S";
public static final String COLUMN_NAME_COUNT_T = "count_T";
public static final String COLUMN_NAME_COUNT_U = "count_U";
public static final String COLUMN_NAME_COUNT_V = "count_V";
public static final String COLUMN_NAME_COUNT_W = "count_W";
public static final String COLUMN_NAME_COUNT_X = "count_X";
public static final String COLUMN_NAME_COUNT_Y = "count_Y";
public static final String COLUMN_NAME_COUNT_Z = "count_Z";
/**
* Constructor
* Takes and keeps a reference of the passed context in order to access to the application assets and resources.
* #param context
*/
public DictionaryDbHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
/* ------------------------ */
/* --- Override Methods --- */
/* ------------------------ */
/**
* On creation
* #param db - the database
*/
public void onCreate(SQLiteDatabase db) {
//Nothing to do here
}
/**
* On upgrade
* #param db - the database
* #param oldVersion - the old database version int
* #param newVersion - the new database version int
*/
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i(TAG, "Database " + db + " version " + oldVersion + " upgraded to " + newVersion);
}
/**
* On downgrade
* #param db - the database
* #param oldVersion - the old database version int
* #param newVersion - the new database version int
*/
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i(TAG, "Database " + db + " version " + oldVersion + " downgraded to " + newVersion);
}
/**
* Creates a empty database on the system and rewrites it with your own database.
* */
public void createDataBase() throws IOException{
if(checkDataBase()){
//do nothing - database already exist
Log.i(TAG, "Database already exists... Nothing to do.");
}else{
//By calling this method and empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
this.getReadableDatabase();
try {
copyDataBase();
Log.i(TAG, "Successfully copied database");
} catch (IOException e) {
Log.i(TAG, "Error copying");
throw new Error("Error copying database");
}
}
}
/**
* Check if the database already exist to avoid re-copying the file each time you open the application.
* #return true if it exists, false if it doesn't
*/
private boolean checkDataBase(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}catch(SQLiteException e){
//database doesn't exist yet.
Log.i(TAG, "Database doesn't exist yet");
}
// Check for null db
if(checkDB != null){
checkDB.close();
Log.i(TAG, "Closed dict database");
}
// Return null or not null
return checkDB != null;
}
/**
* Copies your database from your local assets-folder to the just created empty database in the
* system folder, from where it can be accessed and handled.
* This is done by transfering bytestream.
* */
private void copyDataBase() throws IOException {
//Open your local db as the input stream
InputStream myInput = myContext.getAssets().open(DB_NAME);
// Path to the just created empty db
String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
//transfer bytes from the input file to the output file
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
public void openDataBase() throws SQLException {
//Open the database
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}
#Override
public synchronized void close() {
if(myDataBase != null) {
Log.i(TAG, "Closing dictionary database");
myDataBase.close();
}
super.close();
}
/* -------------------------- */
/* --- Database retrieval --- */
/* -------------------------- */
/**
* Get the Anagrams in the database
* #return the number of anagrams
*/
public ArrayList<Result> getMatches(
// Params. Yeah, I know...
int count_A, int count_B, int count_C, int count_D, int count_E, int count_F,
int count_G, int count_H, int count_I, int count_J, int count_K, int count_L,
int count_M, int count_N, int count_O, int count_P, int count_Q, int count_R,
int count_S, int count_T, int count_U, int count_V, int count_W, int count_X,
int count_Y, int count_Z, int count_WILDCARDS
) {
// Get the database helper to get access to everything...
SQLiteDatabase db = this.getWritableDatabase();
// List to hold matches
ArrayList<Result> resultList = new ArrayList<>();
// Here are the columns we care about in our search
String[] columns = {
UID,
COLUMN_NAME_WORD,
COLUMN_NAME_WORD_LENGTH,
COLUMN_NAME_COUNT_A,
COLUMN_NAME_COUNT_B,
COLUMN_NAME_COUNT_C,
COLUMN_NAME_COUNT_D,
COLUMN_NAME_COUNT_E,
COLUMN_NAME_COUNT_F,
COLUMN_NAME_COUNT_G,
COLUMN_NAME_COUNT_H,
COLUMN_NAME_COUNT_I,
COLUMN_NAME_COUNT_J,
COLUMN_NAME_COUNT_K,
COLUMN_NAME_COUNT_L,
COLUMN_NAME_COUNT_M,
COLUMN_NAME_COUNT_N,
COLUMN_NAME_COUNT_O,
COLUMN_NAME_COUNT_P,
COLUMN_NAME_COUNT_Q,
COLUMN_NAME_COUNT_R,
COLUMN_NAME_COUNT_S,
COLUMN_NAME_COUNT_T,
COLUMN_NAME_COUNT_U,
COLUMN_NAME_COUNT_V,
COLUMN_NAME_COUNT_W,
COLUMN_NAME_COUNT_X,
COLUMN_NAME_COUNT_Y,
COLUMN_NAME_COUNT_Z,
};
String[] selectionArgs = null;
// The selection (WHERE ...)
String selection =
COLUMN_NAME_COUNT_A +"<="+ count_A + " AND " +
COLUMN_NAME_COUNT_B +"<="+ count_B + " AND " +
COLUMN_NAME_COUNT_C +"<="+ count_C + " AND " +
COLUMN_NAME_COUNT_D +"<="+ count_D + " AND " +
COLUMN_NAME_COUNT_E +"<="+ count_E + " AND " +
COLUMN_NAME_COUNT_F +"<="+ count_F + " AND " +
COLUMN_NAME_COUNT_G +"<="+ count_G + " AND " +
COLUMN_NAME_COUNT_H +"<="+ count_H + " AND " +
COLUMN_NAME_COUNT_I +"<="+ count_I + " AND " +
COLUMN_NAME_COUNT_J +"<="+ count_J + " AND " +
COLUMN_NAME_COUNT_K +"<="+ count_K + " AND " +
COLUMN_NAME_COUNT_L +"<="+ count_L + " AND " +
COLUMN_NAME_COUNT_M +"<="+ count_M + " AND " +
COLUMN_NAME_COUNT_N +"<="+ count_N + " AND " +
COLUMN_NAME_COUNT_O +"<="+ count_O + " AND " +
COLUMN_NAME_COUNT_P +"<="+ count_P + " AND " +
COLUMN_NAME_COUNT_Q +"<="+ count_Q + " AND " +
COLUMN_NAME_COUNT_R +"<="+ count_R + " AND " +
COLUMN_NAME_COUNT_S +"<="+ count_S + " AND " +
COLUMN_NAME_COUNT_T +"<="+ count_T + " AND " +
COLUMN_NAME_COUNT_U +"<="+ count_U + " AND " +
COLUMN_NAME_COUNT_V +"<="+ count_V + " AND " +
COLUMN_NAME_COUNT_W +"<="+ count_W + " AND " +
COLUMN_NAME_COUNT_X +"<="+ count_X + " AND " +
COLUMN_NAME_COUNT_Y +"<="+ count_Y + " AND " +
COLUMN_NAME_COUNT_Z +"<="+ count_Z;
String groupBy = null;
String having = null;
String orderBy = null;
// Query the database
Cursor cursor = db.query(
TABLE_NAME, // Table name
columns, // Columns
selection, // Selection
selectionArgs, // Selection arguments
groupBy, // Group by...
having, // having
orderBy // Order by
);
while (cursor.moveToNext()) {
int columnResult = cursor.getColumnIndex(COLUMN_NAME_WORD);
// Add the result to the list to return
resultList.add(new Result(cursor.getString(columnResult)));
}
cursor.close();
// Return the list
return resultList;
}
}//EOF
And here is where I query the Dictionary in another class:
How can I query for wildcards given my current setup? I am new to sql, so any suggestions would be greatly appreciated... I am looking for a parameter for the WHERE clause to allow for up to 2 wildcards, each of which could be anything from A-Z.
Here is an example of how i'm doing a lookup now, from my search service:
DictionaryDbHelper helper = DictionaryDbHelper(this);
ArrayList<Result> matches = helper.getMatches(
this.getQuery().getCount_A(), this.getQuery().getCount_B(),
this.getQuery().getCount_C(), this.getQuery().getCount_D(),
this.getQuery().getCount_E(), this.getQuery().getCount_F(),
this.getQuery().getCount_G(), this.getQuery().getCount_H(),
this.getQuery().getCount_I(), this.getQuery().getCount_J(),
this.getQuery().getCount_K(), this.getQuery().getCount_L(),
this.getQuery().getCount_M(), this.getQuery().getCount_N(),
this.getQuery().getCount_O(), this.getQuery().getCount_P(),
this.getQuery().getCount_Q(), this.getQuery().getCount_R(),
this.getQuery().getCount_S(), this.getQuery().getCount_T(),
this.getQuery().getCount_U(), this.getQuery().getCount_V(),
this.getQuery().getCount_W(), this.getQuery().getCount_X(),
this.getQuery().getCount_Y(), this.getQuery().getCount_Z(),0 // wildcards not in use yet
)
I have a Berkeley-db database where both 'key' and 'value' are of type integer. Is there any way to traverse the database in descending order of 'value'?
I'm using Berkeley-db je-5.0.58 API. The sample code that i'm using from its documentation is shown below.
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2004-2010 Oracle. All rights reserved.
*
*/
package je;
import java.io.File;
import com.sleepycat.bind.tuple.IntegerBinding;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;
/**
* SimpleExample creates a database environment, a database, and a database
* cursor, inserts and retrieves data.
*/
class SimpleExample {
private static final int EXIT_SUCCESS = 0;
private static final int EXIT_FAILURE = 1;
private int numRecords; // num records to insert or retrieve
private int offset; // where we want to start inserting
private boolean doInsert; // if true, insert, else retrieve
private File envDir;
public SimpleExample(int numRecords,
boolean doInsert,
File envDir,
int offset) {
this.numRecords = numRecords;
this.doInsert = doInsert;
this.envDir = envDir;
this.offset = offset;
}
/**
* Usage string
*/
public static void usage() {
System.out.println("usage: java " +
"je.SimpleExample " +
"<dbEnvHomeDirectory> " +
"<insert|retrieve> <numRecords> [offset]");
System.exit(EXIT_FAILURE);
}
/**
* Main
*/
public static void main(String argv[]) {
if (argv.length < 2) {
usage();
return;
}
File envHomeDirectory = new File(argv[0]);
boolean doInsertArg = false;
if (argv[1].equalsIgnoreCase("insert")) {
doInsertArg = true;
} else if (argv[1].equalsIgnoreCase("retrieve")) {
doInsertArg = false;
} else {
usage();
}
int startOffset = 0;
int numRecordsVal = 0;
if (doInsertArg) {
if (argv.length > 2) {
numRecordsVal = Integer.parseInt(argv[2]);
} else {
usage();
return;
}
if (argv.length > 3) {
startOffset = Integer.parseInt(argv[3]);
}
}
try {
SimpleExample app = new SimpleExample(numRecordsVal,
doInsertArg,
envHomeDirectory,
startOffset);
app.run();
} catch (DatabaseException e) {
e.printStackTrace();
System.exit(EXIT_FAILURE);
}
System.exit(EXIT_SUCCESS);
}
/**
* Insert or retrieve data
*/
public void run() throws DatabaseException {
/* Create a new, transactional database environment */
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setTransactional(true);
envConfig.setAllowCreate(true);
Environment exampleEnv = new Environment(envDir, envConfig);
/*
* Make a database within that environment
*
* Notice that we use an explicit transaction to
* perform this database open, and that we
* immediately commit the transaction once the
* database is opened. This is required if we
* want transactional support for the database.
* However, we could have used autocommit to
* perform the same thing by simply passing a
* null txn handle to openDatabase().
*/
Transaction txn = exampleEnv.beginTransaction(null, null);
DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setTransactional(true);
dbConfig.setAllowCreate(true);
dbConfig.setSortedDuplicates(true);
Database exampleDb = exampleEnv.openDatabase(txn,
"simpleDb",
dbConfig);
txn.commit();
/*
* Insert or retrieve data. In our example, database records are
* integer pairs.
*/
/* DatabaseEntry represents the key and data of each record */
DatabaseEntry keyEntry = new DatabaseEntry();
DatabaseEntry dataEntry = new DatabaseEntry();
if (doInsert) {
/* put some data in */
for (int i = offset; i < numRecords + offset; i++) {
/*
* Note that autocommit mode, described in the Getting
* Started Guide, is an alternative to explicitly
* creating the transaction object.
*/
txn = exampleEnv.beginTransaction(null, null);
/* Use a binding to convert the int into a DatabaseEntry. */
IntegerBinding.intToEntry(i, keyEntry);
IntegerBinding.intToEntry(i+1, dataEntry);
OperationStatus status =
exampleDb.put(txn, keyEntry, dataEntry);
/*
* Note that put will throw a DatabaseException when
* error conditions are found such as deadlock.
* However, the status return conveys a variety of
* information. For example, the put might succeed,
* or it might not succeed if the record alread exists
* and the database was not configured for duplicate
* records.
*/
if (status != OperationStatus.SUCCESS) {
throw new RuntimeException("Data insertion got status " +
status);
}
txn.commit();
}
} else {
/* retrieve the data */
Cursor cursor = exampleDb.openCursor(null, null);
while (cursor.getNext(keyEntry, dataEntry, LockMode.DEFAULT) ==
OperationStatus.SUCCESS) {
System.out.println("key=" +
IntegerBinding.entryToInt(keyEntry) +
" data=" +
IntegerBinding.entryToInt(dataEntry));
}
cursor.close();
}
exampleDb.close();
exampleEnv.close();
}
}
you can use a custom comparator and use a cursor to traverse the data in descending order.
In java you have to implement a custom Comparator:
EDIT:
import java.util.Comparator;
import com.sleepycat.bind.tuple.IntegerBinding;
import com.sleepycat.je.DatabaseEntry;
public class IntegerSorter implements Comparator<byte[]>
{
#Override
public int compare(byte[] o1, byte[] o2)
{
return
IntegerBinding.entryToInt(new DatabaseEntry(o1)) -
IntegerBinding.entryToInt(new DatabaseEntry(o2));
}
}
(...)
dbConfig.setBtreeComparator(IntegerSorter.class);
(...)