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.
Related
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();
}
}
hello i have a problem with android. In my app i get data from a webservice and i read it into a sql lite database . In my activity i have multiple spinners who are filled with data from the DB. After selecting all the spinners and clicking the button i go to an other activty where i click a link and the data is written into another table.
I will explain what the app is like a form where a student can choose with school , study area,.. till you have the tuition you want. You can subscribe than for the course(that data is written to another table).
In the other app you can see the tuition whit the number of subscribers.
the first app works but my problem is that i need the table of subscibers used in app 1 for app2
i have read about COntent providers ,shared user id but none of it works.
Can somebody help me?
public class SchemaHelper extends SQLiteOpenHelper {
private final static String DATABASE_NAAM ="av_helpdesk.be.db";
private static final int DATABASE_VERSIE = 10;
public SchemaHelper(Context context) {
super(context, DATABASE_NAAM, null, DATABASE_VERSIE);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "
+ GegevensTabel.TABEL_NAAM
+ " (" + GegevensTabel.ID
+ " INTEGER PRIMARY KEY,"
+ GegevensTabel.INSTELLING
+ " TEXT,"
+ GegevensTabel.OPLEIDINGSTYPE
+ " TEXT,"
+ GegevensTabel.OPLEIDINGSGEBIED
+ " TEXT,"+ GegevensTabel.OPLEIDING+" TEXT,"
+ GegevensTabel.MODULE+" TEXT,"
+ GegevensTabel.LESPLAATS_ADRES+" TEXT,"
+ GegevensTabel.AAVANGS_DATUM+" TEXT"+");"
);
db.execSQL("CREATE TABLE "
+ InschrijvingTabel.TABEL_NAAM
+ " (" + InschrijvingTabel.ID
+ " INTEGER PRIMARY KEY,"
+ InschrijvingTabel.LESPLAATS
+ " TEXT, "
+ InschrijvingTabel.OPLEIDING
+ " TEXT, "
+ InschrijvingTabel.Module
+ " TEXT, "+ InschrijvingTabel.AANTAL+" INTEGER"+");"
);
}
public interface InschrijvingTabel {
String ID="_id";
String Module = "_module";
String TABEL_NAAM = "TBL_INSCHRIJVING1";
String AANTAL="_aantal";
String OPLEIDING="_opleiding";
String LESPLAATS="_lesplaats";
}
public class InschrijvingDB extends SchemaHelper {
public InschrijvingDB(Context context) {
super(context);
}
public void adVakken()
{
SQLiteDatabase sd=getWritableDatabase();
// String xx=x.getModule();
// sd.rawQuery("insert into "+InschrijvingTabel.TABEL_NAAM+"("+ InschrijvingTabel.Module+") values("+xx+")",null);
// sd.rawQuery("insert into "+InschrijvingTabel.TABEL_NAAM+"(_module) values('cc')",null);
}
public class contentPV extends ContentProvider {
static final String PROVIDER_NAME = "com.example.derae.lessenrooster.databank.contentPV";
static final String URL = "content://" + PROVIDER_NAME + "/*";
static final Uri CONTENT_URI = Uri.parse(URL);
#Override
public boolean onCreate() {
Context context = getContext();
InschrijvingDB x= new InschrijvingDB(context);
return false;
}
#Nullable
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
#Nullable
#Override
public String getType(Uri uri) {
return null;
}
#Nullable
#Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
If you're using 1 database that has all the tables you want to use and you're logging in to that database from app 2, then you should be able to access every table there.
Try debugging techniques to see what's going wrong with app 2, like print debugging.
If you can connect to your database normally from app2, then check if there's anything wrong with your Queries to get data from the desired table. Break your problem down into parts.
-I'm sorry if this is not helpful enough, I'm no expert myself, but I hope my tips help you discover the problem yourself.
I am asking this because I am not quite sure of how to work with Android Content Providers. I have a subset of my database with 8 tables and I need to create complex queries to get some of the data. My content provider works fine with simple queries. For example, I have a table Person on my PersonModel.java class and I get the data using:
String [] projection = {PersonModel.C_FIRST_NAME, PersonModel.C_LAST_NAME};
Cursor cursor = context.getContentResolver().query(
MyProvider.CONTENT_URI_PERSONS, projection, null,
null, null);
and it works perfectly.
On MyProvider I have a bunch of CONTENT_URI constants, on for each of my tables.
public class MyProvider extends ContentProvider {
MyDbHelper dbHelper;
SQLiteDatabase db;
private static final String AUTHORITY = "com.myapp.models";
//Paths for each tables
private static final String PATH_PROFILE_PICTURES = "profile_pictures";
private static final String PATH_PERSONS = "persons";
private static final String PATH_USERS = "users";
....
//Content URIs for each table
public static final Uri CONTENT_URI_PROFILE_PICTURES = Uri
.parse("content://" + AUTHORITY + "/" + PATH_PROFILE_PICTURES);
public static final Uri CONTENT_URI_PERSONS = Uri.parse("content://"
+ AUTHORITY + "/" + PATH_PERSONS);
public static final Uri CONTENT_URI_USERS = Uri.parse("content://"
+ AUTHORITY + "/" + PATH_USERS);
...
private static final int PROFILE_PICTURES = 1;
private static final int PROFILE_PICTURE_ID = 2;
private static final int PERSONS = 3;
private static final int PERSON_ID = 4;
private static final int USERS = 5;
private static final int USER_ID = 6;
private static final UriMatcher sURIMatcher = new UriMatcher(
UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY, PATH_PROFILE_PICTURES, PROFILE_PICTURES);
sURIMatcher.addURI(AUTHORITY, PATH_PROFILE_PICTURES + "/#",
PROFILE_PICTURE_ID);
sURIMatcher.addURI(AUTHORITY, PATH_PERSONS, PERSONS);
sURIMatcher.addURI(AUTHORITY, PATH_PERSONS + "/#", PERSON_ID);
sURIMatcher.addURI(AUTHORITY, PATH_USERS, USERS);
sURIMatcher.addURI(AUTHORITY, PATH_USERS + "/#", USER_ID);
...
}
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// Uisng SQLiteQueryBuilder instead of query() method
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// check if the caller has requested a column which does not exists
//checkColumns(projection);
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case PROFILE_PICTURES:
queryBuilder.setTables(ProfilePictureModel.TABLE_PROFILE_PICTURE);
break;
case PROFILE_PICTURE_ID:
// Adding the ID to the original query
queryBuilder.appendWhere(ProfilePictureModel.C_ID + "="
+ uri.getLastPathSegment());
case PERSONS:
queryBuilder.setTables(PersonModel.TABLE_PERSON);
break;
case PERSON_ID:
// Adding the ID to the original query
queryBuilder.appendWhere(PersonModel.C_ID + "="
+ uri.getLastPathSegment());
case USERS:
queryBuilder.setTables(UserModel.TABLE_USER);
break;
case USER_ID:
// Adding the ID to the original query
queryBuilder.appendWhere(UserModel.C_ID + "="
+ uri.getLastPathSegment());
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
db = dbHelper.getWritableDatabase();
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, null, null, sortOrder);
// make sure that potential listeners are getting notified
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
That is a small part of my content provider. So my questions are:
1) How do I implement a rawQuery() in my content provider? or how do I use properly my queryBuilder?, let's say I want to execute this query using several tables, renaming them and also passing the p1.id as a parameter?
SELECT p1.first_name, p1_last_name
FROM Person p1, Person P2, Relationship r
WHERE p1.id = ? AND
p1.id = r.relative_id AND
p2.id = r.related_id;
I tried so by doing this: On my query() method (shown above) I have a new case, called GET_RELATIVES:
case GET_RELATIVES:
db = dbHelper.getWritableDatabase();
queryBuilder.setTables(PersonModel.TABLE_PERSON + " p1, "
+ PersonModel.TABLE_PERSON + " p2, "
+ RelationshipModel.TABLE_RELATIONSHIP + " r");
queryBuilder.appendWhere("p2."+PersonModel.C_ID + "=" + uri.getLastPathSegment());
queryBuilder.appendWhere("p2."+PersonModel.C_ID + "=" + "r.related_id");
queryBuilder.appendWhere("p1."+PersonModel.C_ID + "=" + "r.relative_id");
so I defined a new PATH, CONTENT URI and add it to the UriMatcher, like this:
private static final String PATH_GET_RELATIVES = "get_relatives";
public static final Uri CONTENT_URI_GET_RELATIVES = Uri
.parse("content://" + AUTHORITY + "/"
+ PATH_GET_RELATIVES);
private static final int GET_RELATIVES = 22;
private static final UriMatcher sURIMatcher = new UriMatcher(
UriMatcher.NO_MATCH);
static {
...
sURIMatcher.addURI(AUTHORITY, PATH_GET_RELATIVES, GET_RELATIVES);
}
but this does not seem to work so I think I'm probably defining something wrong on my content provider or inside the query method.
2) I am not quite sure what is the point on having for each table a constant called TABLE_ID and adding it to the switch-case. What is that used for? How do I call it?
Hope anyone can help me with this, thanks in advance!
I actually found the answer to my question in the most obvious place: the android documentation.
First Question: Implement a rawQuery. Did it like this:
Inside of my switch-case in the content provider I added a new URI, which for me is a JOIN between to tables, so I created a new ContentUri constant for it, a new ID, and registered it on the UriMatcher and then wrote the rawQuery. So MyProvider now looks a litte bit like this:
public class MyProvider extends ContentProvider {
...
// JOIN paths
private static final String PATH_RELATIONSHIP_JOIN_PERSON_GET_RELATIVES =
"relationship_join_person_get_relatives";
...
public static final Uri CONTENT_URI_RELATIONSHIP_JOIN_PERSON_GET_RELATIVES = Uri
.parse("content://" + AUTHORITY + "/"
+ PATH_RELATIONSHIP_JOIN_PERSON_GET_RELATIVES);
...
private static final int RELATIONSHIP_JOIN_PERSON_GET_RELATIVES = 21;
private static final UriMatcher sURIMatcher = new UriMatcher(
UriMatcher.NO_MATCH);
static {
...
//JOINS
sURIMatcher.addURI(AUTHORITY, PATH_RELATIONSHIP_JOIN_PERSON_GET_RELATIVES + "/#",
RELATIONSHIP_JOIN_PERSON_GET_RELATIVES);
...
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// Uisng SQLiteQueryBuilder instead of query() method
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// check if the caller has requested a column which does not exists
//checkColumns(projection);
int uriType = sURIMatcher.match(uri);
switch (uriType) {
...
case RELATIONSHIP_JOIN_PERSON_GET_RELATIVES:
db = dbHelper.getWritableDatabase();
String[] args = {String.valueOf(uri.getLastPathSegment())};
Cursor cursor = db.rawQuery(
"SELECT p1.first_name, p1.last_name " +
"FROM Person p1, Person p2, Relationship r " +
"WHERE p1.id = r.relative_id AND " +
"p2.id = r.related_id AND " +
"p2.id = ?", args);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
...
}
And to call the query() method and pass the id ad a parameter I did this in my controller:
String[] projection = { PersonModel.C_FIRST_NAME,
PersonModel.C_LAST_NAME };
Cursor cursor = context.getContentResolver().query(
ContentUris.withAppendedId(
AkdemiaProvider.CONTENT_URI_RELATIONSHIP_JOIN_PERSON_GET_RELATED, id),
projection, null, null, null);
Second question: Having the TABLE_ID constant is useful to have a query for each table passing an id as a parameter, I didn't know how to call the query method passing such id and this is how the Android Developer Documentation explains how to do so using ContentUris.withAppendedId
// Request a specific record.
Cursor managedCursor = managedQuery(
ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2),
projection, // Which columns to return.
null, // WHERE clause.
null, // WHERE clause value substitution
People.NAME + " ASC"); // Sort order.
I you guys want to see the whole documentation go to this link.
Hope this helps to anyone else having the same problem to understand ContentProvider, ContentUris and all that :)
Below code worked for me. Inside your Application's Content Provider:
public static final String PATH_JOIN_TWO_TABLES = "my_path";
public static final Uri URI_JOIN_TWO_TABLES =
Uri.parse("content://" + AUTHORITY + "/" + PATH_JOIN_TWO_TABLES);
private static final int ID_JOIN_TWO_TABLES = 1001;
private static final UriMatcher sURIMatcher = new UriMatcher(
UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY,
PATH_JOIN_TWO_TABLES + "/#", ID_JOIN_TWO_TABLES );
}
#Nullable
#Override
public Cursor query(#NonNull Uri uri, String[] projection, String selection,String[] selectionArgs,
String sortOrder, CancellationSignal cancellationSignal) {
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case ID_JOIN_TWO_TABLES:
return getWritableDatabase()
.rawQuery("select * from " +
"table_one" + " LEFT OUTER JOIN "
+ "table_two" + " ON ("
+ "table_one.ID"
+ " = " + "table_two.id" + ")", null);
}
return super.query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
}
And while making the Query inside your Activity or Fragment:
Cursor cursor = getActivity().getContentResolver()
.query(ContentUris.withAppendedId(MYContentProvider.URI_JOIN_TWO_TABLES, MyContentProvider.ID_JOIN_TWO_TABLES), null, null, null, null);
Hope it works for you.
For simple queries use selectionArgs in ContentProvider. It works like below
String[] args = { "first string", "second#string.com" };
Cursor cursor = db.query("TABLE_NAME", null, "name=? AND email=?", args, null);
Having the TABLE_ID inside the to create a different queries for each table.
Refer following class for all multiple table in content providers
Vogella Tutorial 1
Vogella Tutorial 2
Best practices for exposing multiple tables using content providers in Android
I have a table with two columns first is date and the second is a counter. I do not have a primary key but the date acts as one. So my code checks if the current date exists in the table, if so it will only increment the counter other wise it will add a new entry. The existence of the current date is only possible at the last row. here is the code:
SQLiteOpenHelper implementation:
public class SqliteHelperInstance extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "app_stats";
public static final String TABLE_NAME = "day_stats";
public static final String COLUMN_DATE = "date";
public static final String COLUMN_CIGCOUNT = "cigcount";
public static final int DATABASE_VERSION = 1;
private static final String CREATE_DB = "create table " + TABLE_NAME + " ( " + COLUMN_DATE + " text, "
+ COLUMN_CIGCOUNT + " integer );";
public SqliteHelperInstance(Context context){
super(context,DATABASE_NAME , null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(CREATE_DB);
}
}
The implementation of database manager:
public class DbManager {
private SqliteHelperInstance sqlHelper;
private SQLiteDatabase database;
private String[] columns = { SqliteHelperInstance.COLUMN_DATE, SqliteHelperInstance.COLUMN_CIGCOUNT};
public DbManager(Context context){
sqlHelper = new SqliteHelperInstance(context);
}
public void addEntry(Calendar cal){
ContentValues values = new ContentValues();
String dateEntry = dateFormatter(cal);
Cursor cursor = database.query(SqliteHelperInstance.TABLE_NAME, null, null, null, null, null,
SqliteHelperInstance.COLUMN_DATE + " DESC LIMIT 1");
//check if the last entered date is equal to the current date
if(cursor.moveToFirst()){
String lastDate = cursor.getString(0);
if(lastDate.equalsIgnoreCase(dateEntry)){
//the cigNum should be updated for the current date
int cigNum = cursor.getInt(1);
cigNum ++;
values.put(SqliteHelperInstance.COLUMN_DATE,lastDate);
values.put(SqliteHelperInstance.COLUMN_CIGCOUNT, cigNum);
int i = database.update(SqliteHelperInstance.TABLE_NAME, values,SqliteHelperInstance.COLUMN_DATE + " = " + lastDate , null);
if(i == 0){
Log.d("Tag", "No row is affected");
}
}
else{
//the last date is different than current day so we need to add a new entry
DbEntry newEntry = new DbEntry();
newEntry.setDate(dateEntry);
values.put(SqliteHelperInstance.COLUMN_DATE, dateEntry);
values.put(SqliteHelperInstance.COLUMN_CIGCOUNT, newEntry.getCigNum());
database.insert(SqliteHelperInstance.TABLE_NAME, null, values);
}
}
else{
//its the first time that the application is loading
DbEntry newEntry = new DbEntry();
newEntry.setDate(dateEntry);
newEntry.setCignum(1);
values.put(SqliteHelperInstance.COLUMN_DATE, dateEntry);
values.put(SqliteHelperInstance.COLUMN_CIGCOUNT, newEntry.getCigNum());
Log.i(null, "values are set");
database.insert(SqliteHelperInstance.TABLE_NAME, null, values);
}
}
But when I run the application, I keep getting 0 row affected by the update function.I assume the comparison in the where clause is not returning any particular row. can anyone help me with this issue?
Try getting the lastDate like this:
String lastDate = cursor.getString(cursor.getColumnIndex(SqliteHelperInstance.COLUMN_DATE));
Also, you should use parameterized queries:
int i = database.update(SqliteHelperInstance.TABLE_NAME, values,SqliteHelperInstance.COLUMN_DATE + " = ?", new String[]{ lastDate });
Other than that, I'm not sure what could be wrong.
I am trying to insert into my database table using the database helper class.
this is where I create the object to insert.
Item item1;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
db = new MyDBAdapter(this);
db.insertEntry(item1 = new Item("Bathtub", "Bathroom", "Typical", "Clean", "fill, wash", "Round, deep", "Bathroom", "Toilet, Bathroom", R.drawable.ic_launcher));
Log.i("item", "item: " + item1.toString());
And this is the Item class
public class Item {
//private variables
int _id;
String _item_name;
String _item_classification;
String _group;
String _use;
String _action;
String _properties;
String _location;
String _association;
int _img_id;
//Empty constructor
public Item(){
}
//constructor
public Item (int _id, String _item_name, String _group, String _item_classification, String _use, String _action, String _properties, String _location, String _association, int _img_id) {
this._id = _id;
this._item_name = _item_name;
this._item_classification = _item_classification;
this._group = _group;
this._use = _use;
this._action = _action;
this._properties = _properties;
this._location = _location;
this._association = _association;
this._img_id = _img_id;
}
public Item (String _item_name, String _group, String _item_classification, String _use, String _action, String _properties, String _location, String _association, int _img_id) {
this._item_name = _item_name;
this._item_classification = _item_classification;
this._group = _group;
this._use = _use;
this._action = _action;
this._properties = _properties;
this._location = _location;
this._association = _association;
this._img_id = _img_id;
with getters and setters of course.Now when i use the insert method for my database helper i get an error that is saying syntax error near "group". Why is my insert failing?
public class MyDBAdapter {
private static final String DATABASE_NAME = "myDatabase.db";
private static final String DATABASE_TABLE = "mainTable";
private static final int DATABASE_VERSION = 1;
//main table columns
public static final String KEY_ITEM = "item_name";
public static final String KEY_GROUP = "group";
public static final String ITEM_CLASSIFICATION = "classification";
public static final String KEY_USE = "use";
public static final String KEY_ACTION = "action";
public static final String KEY_PROPERTIES = "properties";
public static final String KEY_ASSOCIATION = "association";
public static final String KEY_IMG_ID = "img_id";
// The index (key) column name for use in where clauses.
public static final String KEY_ID="_id";
// The name and column index of each column in your database.
public static final int NAME_COLUMN = 1;
public static final int GROUP_COLUMN = 2;
public static final int CLASSIFICATION_COLUMN = 3;
public static final int USE_COLUMN = 4;
public static final int ACTION_COLUMN = 5;
public static final int PROPERTIES_COLUMN = 6;
public static final int ASSOCIATION_COLUMN = 7;
public static final int IMG_ID_COLUMN = 8;
// TODO: Create public field for each column in your table.
// SQL Statement to create a new database.
private static final String DATABASE_CREATE = "create table " +
DATABASE_TABLE + " ("
+ KEY_ID + " integer primary key autoincrement, "
+ KEY_ITEM + " TEXT, "
+ KEY_GROUP + " TEXT, "
+ ITEM_CLASSIFICATION + " TEXT, "
+ KEY_USE + " TEXT, "
+ KEY_ACTION + " TEXT, "
+ KEY_PROPERTIES + " TEXT, "
+ KEY_ASSOCIATION + " TEXT, "
+ KEY_IMG_ID + " INTEGER);";
// Variable to hold the database instance
private SQLiteDatabase db;
// Context of the application using the database.
private final Context context;
// Database open/upgrade helper
private myDbHelper dbHelper;
public MyDBAdapter(Context _context) {
context = _context;
dbHelper = new myDbHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public MyDBAdapter open() throws SQLException {
try {
db = dbHelper.getWritableDatabase();
} catch (SQLiteException e) {
db = dbHelper.getReadableDatabase();
}
return this;
}
public void close() {
db.close();
}
public void insertEntry(Item item) {
// TODO: Create a new ContentValues to represent my row
// and insert it into the database.
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_ITEM, item.get_item_name());
values.put(KEY_GROUP, item.get_group());
values.put(ITEM_CLASSIFICATION, item.get_item_classification());
values.put(KEY_USE, item.get_use());
values.put(KEY_ACTION, item.get_action());
values.put(KEY_PROPERTIES, item.get_properties());
values.put(KEY_ASSOCIATION, item.get_association());
values.put(KEY_IMG_ID, item.get_img_id());
// insert row to table
try{
db.insertOrThrow(DATABASE_TABLE, null, values);
}catch (Exception e){Log.w("insertFail", "insert failed: " + e.toString());}
db.close();
}
I get a syntax error-->sqlite returned: error code = 1, msg = near "group": syntax error
You are forgetting a space after your column names. Change,
private static final String DATABASE_CREATE = "create table " +
DATABASE_TABLE + " (" + KEY_ID +
" integer primary key autoincrement, " +
KEY_ITEM + " text not null, " + KEY_GROUP + "TEXT, " +
ITEM_CLASSIFICATION + "TEXT, " + KEY_USE + "TEXT, " +
KEY_ACTION + "TEXT, " + KEY_PROPERTIES + "TEXT" +
KEY_ASSOCIATION + "TEXT," + KEY_IMG_ID + "INTEGER" + ");";
to
private static final String DATABASE_CREATE = "create table " +
DATABASE_TABLE + " (" + KEY_ID +
" integer primary key autoincrement, " +
KEY_ITEM + " text not null, " + KEY_GROUP + " TEXT, " +
ITEM_CLASSIFICATION + " TEXT, " + KEY_USE + " TEXT, " +
KEY_ACTION + " TEXT, " + KEY_PROPERTIES + " TEXT, " +
KEY_ASSOCIATION + " TEXT," + KEY_IMG_ID + " INTEGER" + ");";
(note the space added before TEXT and INTEGER).
You are creating a string like:
...integer primary key autoincrement, item_name text not null, groupTEXT, classificationTEXT, useTEXT, actionTEXT, propertiesTEXT... (sic).
See the problem?
I was using "group" for on of my column names in my table. Apparently there is an issue with this. I am guessing that "group" is a reserved word for SQLite for android. so if anyone else runs into this issue try changing the column name. Hope this is helpful.