I'm trying to create a multi-table database for my android application. I'm following the suggestion given on this site http://androidforbeginners.blogspot.com/2010/01/creating-multiple-sqlite-database.html for doing it. I keep on getting the error below. The error seems to be cause by the onCreate of the databaes tables.
If you look at my onCreate in the the DBHelper class I have two commented out. This allows it to work no matter which one is left uncommeted.
There has to be a way to create a multi-table database because a single table in a database almost defeats the purpose of having a database.
10-23 02:11:35.383: ERROR/AndroidRuntime(300): Caused by: android.database.sqlite.SQLiteException: Can't upgrade read-only database from version 0 to 1: /data/data/com.parkingticket/databases/Tickets.db
Thanks in advance.
Here is my code
package com.parkingticket;
import java.sql.SQLException;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class TicketDBAdapter
{
private static final String DATABASE_NAME="Tickets.db";
private static final int DATABASE_VERSION = 1;
private static final String PARKEDCARS_TABLE = "ParkedCars";
private static final String PARKINGMETERS_TABLE = "ParkingMeters";
private static final String PARKINGTICKETS_TABLE = "ParkingTickets";
private static final String POLICEOFFICERS_TABLE = "PoliceOfficers";
// The name and column index for each column in PARKEDCARS
public static final String KEY_CARID = "carID";
public static final int CARID_COLUMN = 0;
public static final String KEY_CARMAKE = "Make";
public static final int CARMAKE_COLUMN = 1;
public static final String KEY_CARMODEL = "Model";
public static final int CARMODEL_COLUMN = 2;
public static final String KEY_CARCOLOR = "Color";
public static final int CARCOLOR_COLUMN = 3;
public static final String KEY_CARLICENSENUMBER = "LicenseNumber";
public static final int CARLICENSENUMBER_COLUMN = 4;
public static final String KEY_CARMINUTESPARKED = "MinutesParked";
public static final int CARMINUTESPARKED_COLUMN = 5;
// The name and column index for each column in PARKINGMETERS
public static final String KEY_METERID = "meterID";
public static final int METERID_COLUMN = 0;
public static final String KEY_MINUTESPURCHASED = "MinutesPurchased";
public static final int MINUTESPURCHASED_COLUMN = 1;
// The name and column index for each column in PARKINGTICKETS
//TODO create the columns and indexs for parking tickets
// The name and column index for each column in POLICEOFFICERS
public static final String KEY_OFFICERID = "officerID";
public static final int OFFICERID_COLUMN = 0;
public static final String KEY_OFFICERNAME = "Name";
public static final int OFFICERNAME_COLUMN = 1;
public static final String KEY_OFFICERBADGE = "BadgeNumber";
public static final int OFFICERBADE_COLUMN = 2;
//Variable to hold the database instance
private SQLiteDatabase ticketDB;
//Context of the application using the database.
private final Context context;
//Database open/upgrade helper
private TicketDBHelper ticketDBHelper;
public TicketDBAdapter(Context _context)
{
context = _context;
ticketDBHelper = new TicketDBHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void open() throws SQLiteException
{
try
{
ticketDB = ticketDBHelper.getWritableDatabase();
}
catch(SQLiteException ex)
{
ticketDB = ticketDBHelper.getReadableDatabase();
}
}
public void close()
{
ticketDB.close();
}
//Insert a new ParkedCar
public long insertParkedCar(ParkedCar _car)
{
//Create a new row of values to insert
ContentValues newParkedCarValues = new ContentValues();
//Assign values for each row
newParkedCarValues.put(KEY_CARMAKE, _car.getMake());
newParkedCarValues.put(KEY_CARMODEL, _car.getModel());
newParkedCarValues.put(KEY_CARCOLOR, _car.getColor());
newParkedCarValues.put(KEY_CARLICENSENUMBER, _car.getLicenseNumber());
newParkedCarValues.put(KEY_CARMINUTESPARKED, _car.getMinutesParked());
//Insert the row
return ticketDB.insert(PARKEDCARS_TABLE, null, newParkedCarValues);
}
//Remove a ParkedCar based on its index
public boolean removeParkedCar(long _rowIndex)
{
return ticketDB.delete(PARKEDCARS_TABLE, KEY_CARID + "=" + _rowIndex, null)>0;
}
//Update a ParkedCar's MinutesParked
//TODO Create an update for ParkedCar's minutesParked.
public Cursor getAllParkedCarsCursor()
{
return ticketDB.query(PARKEDCARS_TABLE, new String[] {KEY_CARID, KEY_CARMAKE, KEY_CARMODEL, KEY_CARCOLOR, KEY_CARLICENSENUMBER, KEY_CARMINUTESPARKED}, null, null, null, null, null);
}
public Cursor setCursorParkedCar(long _rowIndex) throws SQLException
{
Cursor result = ticketDB.query(true, PARKEDCARS_TABLE, new String []{KEY_CARID}, KEY_CARID + "=" + _rowIndex, null, null, null, null, null);
if ((result.getCount() == 0) || !result.moveToFirst())
{
throw new SQLException("No ParkedCar found for row: " + _rowIndex);
}
return result;
}
public static class TicketDBHelper extends SQLiteOpenHelper
{
public TicketDBHelper(Context context, String name, CursorFactory factory, int version)
{
super(context, name, factory, version);
}
//SQL Statement to create PARKEDCARS table
private static final String PARKEDCARS_CREATE = "create table " + PARKEDCARS_TABLE + " (" + KEY_CARID + " integer primary key autoincrement, " + KEY_CARMAKE + " text not null," + KEY_CARMODEL + " text not null," + KEY_CARCOLOR + " text not null," + KEY_CARLICENSENUMBER + " text not null," + KEY_CARMINUTESPARKED + "int not null);";
//SQL Statement to create ParkingMeters table
private static final String PARKINGMETERS_CREATE = "create table" + PARKINGMETERS_TABLE + " (" + KEY_METERID + " integer primary key autoincrement, " + KEY_MINUTESPURCHASED + " int not null);";
//SQL Statement to create ParkingTickets table
//TODO create the statement for parkingTickets
//SQL Statement to create PoliceOfficers table
private static final String POLICEOFFICERS_CREATE = "create table" + POLICEOFFICERS_TABLE + " (" + KEY_OFFICERID + " integer primary key autoincrement, " + KEY_OFFICERNAME + " text not null," + KEY_OFFICERBADGE + "text not null);";
//Called when no database exists in disk and the helper class needs to create a new one.
#Override
public void onCreate(SQLiteDatabase _db)
{
//_db.execSQL(PARKEDCARS_CREATE);
_db.execSQL(PARKINGMETERS_CREATE);
//_db.execSQL(POLICEOFFICERS_CREATE);
}
//Called when there is a database verion mismatch meaning that the version of the database on disk needs to be upgraded to the current version
#Override
public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion)
{
//Log the version upgrade.
Log.w("TaskDBAdapter", "Upgrading from version " + _oldVersion + " to " + _newVersion + ", which will destroy all old data");
//Upgrade the existing database to conform to the new version
//Multiple previous versions can be handled by comparing _oldVersoin and _newVersion values
//The simplest case is to drop teh old table and create a new one.
_db.execSQL("DROP TABLE IF EXISTS " + PARKEDCARS_TABLE);
_db.execSQL("DROP TABLE IF EXISTS " + PARKINGMETERS_TABLE);
_db.execSQL("DROP TABLE IF EXISTS " + POLICEOFFICERS_TABLE);
onCreate(_db);
}
}
}
I know I'm late,but i think it may help others reading this post.
I had the same problem and it's about spaces,be very careful when you're writing SQL queries about spaces.For example in the code above,you can notice that there's no space in the end :
KEY_OFFICERBADGE + "text not null"
So basically,you have declared a column with no type.
Hope it helped.
Put a Log statement in your open() method when you catch the SQLiteException to see if ticketDBHelper.getWritableDatabase() is failing and subsequently calling ticketDBHelper.getReadableDatabase(), which as the name implies, opens the database as read-only.
I have written a multi table app which works fine. Sounds like you may have a problem opening the existing db file for writing. I would suggest deleting that from your emulator/device, incrementing the version number and rerunning the app. My open method is pasted here.
public DbAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
Anthony Nolan
I have found a solution to the problem but not a direct answer on this site.
http://pheide.com/page/11/tab/24#post13
Why using a abstract class to create the databases the extending that class works I can't figure out for the life of me because it seems I'm doing the same thing in my one class up above. None the less it works as far as I've played around with it.
Related
Using the Jackson Hash Mapper with Flatten=true my Date fields are getting discarded. Is this the correct behaviour or a bug? Is there a way to have Date serialized with Flatten=true?
I've used the following test Pojo:
import java.util.Date;
public class FooClass{
private Boolean foolean;
private Integer barteger;
private String simpleString;
private Date myDate;
public void setFoolean(Boolean value){ foolean = value; }
public Boolean getFoolean(){ return foolean; }
public void setBarteger(Integer value){ barteger = value; }
public Integer getBarteger(){ return barteger; }
public void setSimpleString(String value) { simpleString = value; }
public String getSimpleString(){ return simpleString; }
public void setMyDate(Date value) { myDate = value; }
public Date getMyDate(){ return myDate; }
}
public class Main {
public static void main(String[] args) throws ParseException,
JsonParseException, JsonMappingException, IOException {
Jackson2HashMapper hashMapper = new Jackson2HashMapper(true);
FooClass fooObject = new FooClass();
fooObject.setFoolean(true);
fooObject.setBarteger(10);
fooObject.setSimpleString("Foobar");
fooObject.setMyDate(new Date());
Map<String, Object> hash = hashMapper.toHash(fooObject);
for (String key: hash.keySet())
{
System.out.println("hash contains: " + key + "=" +
hash.get(key.toString()));
}
FooClass newFoo = (FooClass)(hashMapper.fromHash(hash));
System.out.println("FromHash: " + newFoo);
}
}
In this case I get the following output:
hash contains: #class=FooClass
hash contains: foolean=true
hash contains: barteger=10
hash contains: simpleString=Foobar
FromHash: FooClass#117159c0
If I change new Jackson2HashMapper(false); then I get:
hash contains: #class=FooClass
hash contains: foolean=true
hash contains: barteger=10
hash contains: simpleString=Foobar
hash contains: myDate=[java.util.Date, 1547033077869]
FromHash: FooClass#7ed7259e
I was expecting to get the Date field serialized in both cases - perhaps with an additional field describing the date type (flattened).
I traced the reason for this to the following line in the HashMapper code:
typingMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
Where the mapper is configured.
It seems to issue in Jackson2HashMapper.
After digging into the source of Jackson2HashMapper, it seems to issue in Jackson2HashMapper.
created an issue for this, DATAREDIS-1001
Jackson2HashMapper does not serialize Date/Calender fields when flatten = true
i have two Presenters: A DevicePresenter and a ContainerPresenter. I place a PlaceRequest in the DevicePresenter to call the ContainerPresenter with some parameters like this:
PlaceRequest request = new PlaceRequest.Builder()
.nameToken("containersPage")
.with("action","editContainer")
.with("containerEditId", selectedContainerDto.getUuid().toString())
.build();
placeManager.revealPlace(request);
In my ContainersPresenter i have this overridden method:
#Override
public void prepareFromRequest(PlaceRequest placeRequest) {
Log.debug("prepareFromRequest in ContainersPresenter");
super.prepareFromRequest(placeRequest);
String actionString = placeRequest.getParameter("action", "");
String id;
//TODO: Should we change that to really retrieve the object from the server? Or should we introduce a model that keeps all values and inject that into all presenters?
if (actionString.equals("editContainer")) {
try {
id = placeRequest.getParameter("id", null);
for(ContainerDto cont : containerList) {
Log.debug("Compare " + id + " with " + cont.getUuid());
if(id.equals(cont.getUuid())) {
containerDialog.setCurrentContainerDTO(new ContainerDto());
addToPopupSlot(containerDialog);
break;
}
}
} catch (NumberFormatException e) {
Log.debug("id cannot be retrieved from URL");
}
}
}
But when revealPlace is called, the URL in the browser stays the same and the default presenter (Home) is shown instead.
When i print the request, it seems to be fine:
PlaceRequest(nameToken=containersPage, params={action=editContainer, containerEditId=8fa5f730-fe0f-11e3-a3ac-0800200c9a66})
And my NameTokens are like this:
public class NameTokens {
public static final String homePage = "!homePage";
public static final String containersPage = "!containersPage";
public static final String devicesPage = "!devicesPage";
public static String getHomePage() {
return homePage;
}
public static String getDevicesPage() {
return devicesPage;
}
public static String getContainersPage() {
return containersPage;
}
}
What did i miss? Thanks!
In your original code, when constructing your PlaceRequest, you forgot the '!' at the beginning of your nametoken.
.nameToken("containersPage")
while your NameTokens entry is
public static final String containersPage = "!containersPage";
As you noted, referencing the constant in NameTokens is less prone to such easy mistakes to make!
Sometimes the problem exists "between the ears". If i avoid strings but use the proper symbol from NameTokens like
PlaceRequest request = new PlaceRequest.Builder()
.nameToken(NameTokens.containersPage)
.with("action","editContainer")
.with("containerEditId", selectedContainerDto.getUuid().toString())
.build();
it works just fine. Sorry!
I have a problem creating a database with 2 columns.
In the first one there should be a name and in the second one a number.
Creating the name database is no problem, but when I try to add the second column I get the error message "I/Database(13531): sqlite returned: error code = 1, msg = no such column: zahl"
my code is here
first the Manager
package my.studienarbeit.bestimmung.database;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DatenbankManager extends SQLiteOpenHelper {
private static final String DB_NAME = "carbohydrates.db";
private static final int DB_VERSION = 1;
private static final String WEIGHT_CREATE =
"CREATE TABLE weight ("
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "name TEXT NOT NULL, " +
// + "zahl INTEGER" +
")";
public DatenbankManager(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(WEIGHT_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(DatenbankManager.class.getName(),
"Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS SCANITEM");
onCreate(db);
}
}
and my database:
package my.studienarbeit.bestimmung.database;
import my.studienarbeit.bestimmung.R;
import android.app.ListActivity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;
public class Datenbanken extends ListActivity {
private SQLiteDatabase mDatenbank;
private DatenbankManager mHelper;
/*/ private static final String WEIGHT_SELECT_RAW =
"SELECT _id, name, zahl FROM weight" ; //*/
//*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.daten_datenbanken);
mHelper = new DatenbankManager(this);
mHelper.getWritableDatabase();
}
#Override
protected void onPause() {
super.onPause();
mDatenbank.close();
Toast.makeText(this,
getResources().getString(R.string.tx_daten_datenbanken_db_geschlossen),
Toast.LENGTH_SHORT).show();
}
#Override
protected void onResume() {
super.onResume();
mDatenbank = mHelper.getWritableDatabase();
Toast.makeText(
this,
getResources().getString(
R.string.tx_daten_datenbanken_db_geoeffnet
),
Toast.LENGTH_SHORT)
.show();
ladeDaten();
}
private void ladeDaten() {
// Cursor weightCursor = mDatenbank.rawQuery(WEIGHT_SELECT_RAW, null);
Cursor weightCursor = mDatenbank.query("weight",
new String [] {
"_id",
"name",
// "zahl",
},
null, null, null, null, null
);
startManagingCursor(weightCursor);
SimpleCursorAdapter weightAdapter =
new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_2,
weightCursor,
new String [] {
"name",
// "zahl"
},
new int[] {
android.R.id.text1
}
);
setListAdapter(weightAdapter);
}
public void onNavButtonClick(View view) {
EditText et_c =
(EditText) findViewById(R.id.editText_name_c);
// EditText et_w =
// (EditText) findViewById(R.id.editText_weight_c);
ContentValues werte = new ContentValues();
werte.put("name", et_c.getText().toString());
// werte.put("zahl", et_w.getText().toString());
mDatenbank.insert("weight", null, werte);
ladeDaten();
et_c.setText("");
// et_w.setText("");
The 2nd column is not active right now for debug functions.
If you first created your table without the "zahl" column, then added the "zahl" column and rerun your application, the OpenHelper did not do the "onCreate" again with the new values. My suggestion is that you first drop the table (on emulator, go to Settings > Applications > Yourapp > Click "Clear Data" button), then uncomment the "zahl" lines (especially in DatenbankManager.java), then recompile + run. Running your app again, with an empty database, will recreate the table with column "zahl".
We are using fluentnhibernate with automapping and we have a naming convention that all columns that are foreign keys, there column name will end with "Key". So we have a convention that looks like this:
public class ForeignKeyColumnNameConvention : IReferenceConvention
{
public void Apply ( IManyToOneInstance instance )
{
// name the key field
string propertyName = instance.Property.Name;
instance.Column ( propertyName + "Key" );
}
}
This works great until we created a component in which one of its values is a foreign key. By renaming the column here it overrides the default name given to the component column which includes the ComponentPrefix which is defined in the AutomappingConfiguration. Is there a way for me to get the ComponentPrefix in this convention? or is there some other way for me to get the column name for components with a property that is a foreign key to end in the word "Key"?
After a lot of fiddling and trial & error (thus being tempted to use your solution with Reflection) I came up with the following:
This method depends on the order of the execution of the conventions. This convention-order happens via a strict hierarchy. In this example, at first, the convention of the component (IDynamicComponentConvention) is being handled and after that the conventions of the inner properties are being handled such as the References mapping (IReferenceConvention).
The strict order is where we make our strike:
We assemble the correct name of the column in the call to Apply(IDynamicComponentConvention instance), put it on the queue. Note that a Queue<T> is used which is a FIFO (first-in-first-out) collection type thus it keeps the order correctly.
Almost immediately after that, Apply(IManyToOneInstanceinstance) is called. We check if there is anything in the queue. If there is, we take it out of the queue and set it as column name. Note that you should not use Peek() instead of Dequeue() as it does not remove the object from the queue.
The code is as follows:
public sealed class CustomNamingConvention : IDynamicComponentConvention, IReferenceConvention {
private static Queue<string> ColumnNames = new Queue<string>();
public void Apply(IDynamicComponentInstance instance) {
foreach (var referenceInspector in instance.References) {
// All the information we need is right here
// But only to inspect, no editing yet :(
// Don't worry, just assemble the name and enqueue it
var name = string.Format("{0}_{1}",
instance.Name,
referenceInspector.Columns.Single().Name);
ColumnNames.Enqueue(name);
}
}
public void Apply(IManyToOneInstance instance) {
if (!ColumnNames.Any())
// Nothing in the queue? Just return then (^_^)
return;
// Set the retrieved string as the column name
var columnName = ColumnNames.Dequeue();
instance.Column(columnName);
// Pick a beer and celebrate the correct naming!
}
}
I Have figured out a way to do this using reflection to get to the underlying mapping of the IManyToOneInspector exposed by the IComponentInstance but was hoping there was a better way to do this?
Here is some example code of how I achieved this:
#region IConvention<IComponentInspector, IComponentInstance> Members
public void Apply(IComponentInstance instance)
{
foreach (var manyToOneInspector in instance.References)
{
var referenceName = string.Format("{0}_{1}_{2}{3}", instance.EntityType.Name, manyToOneInspector.Property.PropertyType.Name, _autoMappingConfiguration.GetComponentColumnPrefix(instance.Property), manyToOneInspector.Property.Name);
if(manyToOneInspector.Property.PropertyType.IsSubclassOf(typeof(LookupBase)))
{
referenceName += "Lkp";
}
manyToOneInspector.Index ( string.Format ( "{0}_FK_IDX", referenceName ) );
}
}
#endregion
public static class ManyToOneInspectorExtensions
{
public static ManyToOneMapping GetMapping(this IManyToOneInspector manyToOneInspector)
{
var fieldInfo = manyToOneInspector.GetType ().GetField( "mapping", BindingFlags.NonPublic | BindingFlags.Instance );
if (fieldInfo != null)
{
var manyToOneMapping = fieldInfo.GetValue( manyToOneInspector ) as ManyToOneMapping;
return manyToOneMapping;
}
return null;
}
public static void Index(this IManyToOneInspector manyToOneInspector, string indexName)
{
var mapping = manyToOneInspector.GetMapping ();
mapping.Index ( indexName );
}
public static void Column(this IManyToOneInspector manyToOneInspector, string columnName)
{
var mapping = manyToOneInspector.GetMapping ();
mapping.Column ( columnName );
}
public static void ForeignKey(this IManyToOneInspector manyToOneInspector, string foreignKeyName)
{
var mapping = manyToOneInspector.GetMapping();
mapping.ForeignKey ( foreignKeyName );
}
}
public static class ManyToOneMappingExtensions
{
public static void Index (this ManyToOneMapping manyToOneMapping, string indexName)
{
if (manyToOneMapping.Columns.First().IsSpecified("Index"))
return;
foreach (var column in manyToOneMapping.Columns)
{
column.Index = indexName;
}
}
public static void Column(this ManyToOneMapping manyToOneMapping, string columnName)
{
if (manyToOneMapping.Columns.UserDefined.Count() > 0)
return;
var originalColumn = manyToOneMapping.Columns.FirstOrDefault();
var column = originalColumn == null ? new ColumnMapping() : originalColumn.Clone();
column.Name = columnName;
manyToOneMapping.ClearColumns();
manyToOneMapping.AddColumn(column);
}
public static void ForeignKey(this ManyToOneMapping manyToOneMapping, string foreignKeyName)
{
if (!manyToOneMapping.IsSpecified("ForeignKey"))
manyToOneMapping.ForeignKey = foreignKeyName;
}
}
I am creating an android application using Eclipse. I want to ask on how to create a database using SQLite. The database will only have 3 tables which is user_id, user_name, and user_password.
Formulate CREATE statements for your tables.
(a) on desktop create a SQLite db and then push the directory to emulator. Use SQLite command line to execute these statements, or
(b) connect to android shell then you will get SQLite shell. or
(c) in your android activity, fire SQL command (as mentioned by user522751 )
You can try SQLiteOpenHelper, here is what i did in a demo app few weeks ago..
import static com.kevin.fastcard.utils.ColumnConstants.;
import static com.kevin.fastcard.utils.DBConstants.;
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("CREATE TABLE " + TBL_NAME + "(" + _ID
+ " INTEGER PRIMARY KEY AUTOINCREMENT," + QUESTION + " TEXT,"
+ ANSWER + " TEXT," + CATEGORY + " TEXT," + CREATE_TIME
+ " TEXT," +TIME_IN_MILLIS+" INTEGER"+ ");");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.delete(TBL_NAME, null, null);
}
}
I'm new to android, hope that can help you..