For my current project I decided to use ORMLite (4.46). Currently, I'm experiencing some issues when saving data of a foreign object. Below, a example of my code. The field called "field" is not saved properly (The old data is displayed). Anybody an idea, what's wrong with this code?
Thank you!
Regards
Andreas
Some class:
DatabaseHelper databaseHelper = OpenHelperManager.getHelper(context, DatabaseHelper.class)
branch.field = 123423;
Log.d("...", "field:" + branch.field); <-- new value of field is displayed
databaseHelper.getBranchDao().update(branch);
Other class:
DatabaseHelper databaseHelper = OpenHelperManager.getHelper(context, DatabaseHelper.class);
databaseHelper.getFloorDao().refresh(floor);
databaseHelper.getBranchDao().refresh(floor.branch);
Log.d("...", "branch field:" + floor.branch.field); <-- old value of field is displayed
DatabaseHelper:
...
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
// name of the database file for your application -- change to something appropriate for your app
private static final String DATABASE_NAME = "xy.db";
// any time you make changes to your database objects, you may have to increase the database version
private static final int DATABASE_VERSION = 1;
// the DAO objects we use to access the tables
private Dao<Branch, Integer> branchDao = null;
private Dao<Floor, Integer> floorDao = null;
private Context appContext;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION, R.raw.ormlite_config);
appContext = context;
}
/**
* This is called when the database is first created. Usually you should call createTable statements here to create
* the tables that will store your data.
*/
#Override
public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
try {
Log.i(DatabaseHelper.class.getName(), "onCreate");
// Create tables for each domain object here!
TableUtils.createTable(connectionSource, Branch.class);
TableUtils.createTable(connectionSource, Floor.class);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't create database", e);
throw new RuntimeException(e);
}
}
/**
* This is called when your application is upgraded and it has a higher version number. This allows you to adjust
* the various data to match the new version number.
*/
#Override
public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
try {
Log.i(DatabaseHelper.class.getName(), "onUpgrade");
// Drop all tables of domain objects here!
TableUtils.dropTable(connectionSource, Branch.class, true);
TableUtils.dropTable(connectionSource, Floor.class, true);
// after we drop the old databases, we create the new ones
onCreate(db, connectionSource);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e);
throw new RuntimeException(e);
}
}
/**
* Delete and re-create database when user is changed
*/
public void reCreateDB(){
//delete the database (onCreate() will be called if database is needed again)
appContext.deleteDatabase(DATABASE_NAME);
}
/**
* Returns the Database Access Object (DAO) for our domain class. It will create it or just give the cached
* value.
*/
public Dao<Branch, Integer> getBranchDao() throws SQLException {
if (branchDao == null) {
branchDao = getDao(Branch.class);
}
return branchDao;
}
public Dao<Floor, Integer> getFloorDao() throws SQLException {
if (floorDao == null) {
floorDao = getDao(Floor.class);
}
return floorDao;
}
/**
* Close the database connections and clear any cached DAOs.
*/
#Override
public void close() {
super.close();
}
}
Floor:
...
public class Floor {
#DatabaseField(generatedId = true)
public int id;
#DatabaseField(foreign = true, canBeNull = true, foreignAutoRefresh = true, maxForeignAutoRefreshLevel=5)
public Branch branch;
public Floor(){}
}
Branch:
....
public class Branch {
#DatabaseField(generatedId = true)
public int id;
#DatabaseField(canBeNull = true)
public int field;
#ForeignCollectionField(eager = true, maxEagerLevel = 99)
private ForeignCollection<Floor> floors;
public Branch(){}
public ForeignCollection<Floor> getEmptyFloorCollection() {
try {
return MainActivity.getDatabaseHelper().getBranchDao().getEmptyForeignCollection("floors");
} catch (SQLException e) {
Log.e("DatabaseHelper", "SQL Exception: " + e.getLocalizedMessage());
return null;
}
}
public ForeignCollection<Floor> getFloors() {
if(floors == null) floors = getEmptyFloorCollection();
return floors;
}
public void setFloors(ForeignCollection<Floor> floors) {
this.floors = floors;
}
}
Related
In my application, I use the users password as the encryption key for encryption media. I am encrypting media using PBEWithMD5AndDES and this works fine with a password stored in shared preferences. Now to achieve a level of security I am removing the password from shared preferences and using a singleton that is only kept alive during the app session (as the app logs out automatically requiring entry of the password). Below is my singleton:
public class Credentials {
private static Credentials dataObject = null;
private Credentials() {
// left blank intentionally
}
public static Credentials getInstance() {
if (dataObject == null)
dataObject = new Credentials();
return dataObject;
}
private char[] user_password;
public char[] getUser_password() {
return user_password;
}
public void setUser_password(char[] user_password) {
this.user_password = user_password;
}
}
The password is zeroed out from memory if the app logs out, or is log out by the user or gets destroyed. However at times I am getting a null pointer when trying to retrieve the password.
char[] pswd = Credentials.getInstance().getUser_password();
What could be causing this? is there any other method I can use except a singleton?
Alternatively, you can store the password using built-in Sqlite db, though I'd still recommend you save it encrypted for max protection. You can do this in 4 steps:
2) Create an entity object to store the password:
public class Password {
int password_id; // will be auto-increamted
String password;
public Password(int password_id, String password) {
this.password_id = password_id;
this.password = password;
}
// getter/setters ...
}
2) Create an Sqlite utility object:
public class SQLiteDBAdapter {
protected static final String DATABASE_NAME = "mydb";
protected static final int DATABASE_VERSION = 1;
protected Context context;
protected static DatabaseHelper mDbHelper;
public static final String TABLE_PASSWORD = "tbl_password";
// columns
public static final String PASSWORD_ID = "_id";
public static final String PASSWORD = "password";
// create table string
private static final String CREATE_TABLE_PASSWORD =
"CREATE TABLE if not exists " + TABLE_PASSWORD + " ( " +
PASSWORD_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
PASSWORD + " TEXT NOT NULL);";
public SQLiteDBAdapter(Context context) {
context = context.getApplicationContext();
}
public SQLiteDatabase openDb() {
if (mDbHelper == null) {
mDbHelper = new DatabaseHelper(mContext);
}
return mDbHelper.getWritableDatabase();
}
protected static class DatabaseHelper extends SQLiteOpenHelper {
// -------------------------------------------------------------------------------------------
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// -------------------------------------------------------------------------------------------
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_PASSWORD);
}
// -------------------------------------------------------------------------------------------
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " +
newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS routes");
onCreate(db);
}
}
}
3) Extend an Sqlite object to manipulate the table (CRUD operations):
public class PasswordDbAdapter extends SQLiteDBAdapter {
private SQLiteDatabase db;
// these are column corresponding indices
public static final int INDEX_PASSWORD_ID = 0; // an auto-increment
public static final int INDEX_PASSWORD = 1;
public PasswordDbAdapter(Context context) {
super(context);
}
public void addPassword(String password) {
db = openDb();
ContentValues values = new ContentValues();
values.put(PASSWORD, password);
db.insert(TABLE_PASSWORD, null, values);
}
public void updatePassword(String password) {
db = openDb();
ContentValues values = new ContentValues();
values.put(PASSWORD, password);
db.update(TABLE_PASSWORD, values, null);
}
public void deletePassword() {
db = openDb();
db.delete(TABLE_PASSWORD, null, null);
}
public boolean isEmpty() {
db = openDb();
boolean empty = true;
Cursor cur = db.rawQuery("SELECT COUNT(*) FROM " + TABLE_PASSWORD, null);
if (cur != null && cur.moveToFirst()) {
empty = (cur.getInt (0) == 0);
}
cur.close();
return empty;
}
public Password fetchPassword() { // ok because there's only one password record
db = openDb();
Cursor cursor = db.query(TABLE_PASSWORD, new String[]{PASSWORD_ID, PASSWORD},
null, null, null, null, null, null);
if (cursor != null &&
cursor.moveToFirst()) {
return new Password(
cursor.getString(INDEX_PASSWORD_ID),
cursor.getInt(INDEX_PASSWORD));
}
return null;
}
}
4) Finally, save/update/retrieve the password as desired:
public class MainActivity extends AppCompatActivity {
private PasswordDbAdapter passwordDB;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
// initialize the password db
passwordDB = new PasswordDbAdapter(this);
// check if password record exists
if (passwordDB.isEmpty() {
// save a new copy
passwordDB.addPassword("the_password"); // more secure if it is saved encrypted
} else {
// update it
passwordDB.updatePassword("the_password");
}
}
...
public String fetchPassword() {
return passwordDB.fetchPassword(); // or first decrypt it, then return it
}
}
I'm working on a Swing app which is connected to MySQL database. It fetches some data from the DB and shows in the table. Besides, there are 3 buttons that suppose to changes table data ( Add row to table ), update the database (Update database ) and to discard the changes ( Discard changes ) as following,
After clicking the Add row to table button, the new entries from the form should be added in the table. Afterwards if the update button is clicked, the data will be updated in the db. Discard changes will delete the data from the last row of the table and will have nothing to do with db. The app has two classes CoffeesFrame.java and CoffeesTableModel.java I provided the sample code from both of the classes below,
public class CoffeesFrame extends JFrame implements RowSetListener {
private static final long serialVersionUID = 1L;
private static Connection myConn = null;
private static String url = "jdbc:mysql://localhost:3306/myDemo";
private static String user = "student";
private static String password = "student";
// initiate a table object
JTable table;
// labels of the table
// text data fields of the table
// buttons of the table
// initiate a table model object
CoffeesTableModel myCoffeesTableModel;
// table constructor
public CoffeesFrame(Connection myConn) throws SQLException {
table = new JTable();
createNewTableModel(myConn);
/*
label, text field and buttons
*/
Container contentPane = getContentPane();
contentPane.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
contentPane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
/*
Code for making the GUI
*/
button_ADD_ROW.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// showMessageDialog works
JOptionPane.showMessageDialog(CoffeesFrame.this, new String[] {
"Adding the following row:",
"Coffee name: [" + textField_COF_NAME.getText() + "]",
"Supplier ID: [" + textField_SUP_ID.getText() + "]",
"Price: [" + textField_PRICE.getText() + "]",
"Sales: [" + textField_SALES.getText() + "]",
"Total: [" + textField_TOTAL.getText() + "]" });
try {
// Insert row is not adding data to the table
myCoffeesTableModel.insertRow(
textField_COF_NAME.getText(),
Integer.parseInt(textField_SUP_ID.getText().trim()),
Float.parseFloat(textField_PRICE.getText().trim()),
Integer.parseInt(textField_SALES.getText().trim()),
Integer.parseInt(textField_TOTAL.getText().trim()));
// table.getModel();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
});
button_UPDATE_DATABASE.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
myCoffeesTableModel.coffeesRowSet.acceptChanges(myConn);
}
catch (Exception ex) {
ex.printStackTrace();
}
try {
createNewTableModel(myConn);
}
catch (Exception el) {
el.printStackTrace();
}
}
});
button_DISCARD_CHANGES.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("The changes are DISCARDED");
}
});
}
private void createNewTableModel(Connection myConn) throws SQLException {
myCoffeesTableModel = new CoffeesTableModel(getContentsOfCoffeesTable(myConn));
myCoffeesTableModel.addEventHandlersToRowSet(this);
table.setModel(myCoffeesTableModel);
}
#Override
public void rowSetChanged(RowSetEvent event) {
// TODO Auto-generated method stub
}
#Override
public void rowChanged(RowSetEvent event) {
// TODO Auto-generated method stub
}
#Override
public void cursorMoved(RowSetEvent event) {
// TODO Auto-generated method stub
}
public CachedRowSet getContentsOfCoffeesTable(Connection mycConn)
throws SQLException {
CachedRowSet crs = null;
ResultSet resultSet = null;
Statement stmt = null;
String sql = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";
try {
stmt = myConn.createStatement();
resultSet = stmt.executeQuery(sql);
crs = new CachedRowSetImpl();
crs.populate(resultSet);
}
catch (Exception e) {
e.printStackTrace();
}
return crs;
}
public static void main(String[] args) throws SQLException {
try {
myConn = DriverManager.getConnection(url, user, password);
if (myConn != null) {
System.out.println("Connected to the database myDemo");
}
}
catch (SQLException ex) {
System.out
.println("An error occurred. Maybe user/password is invalid");
ex.printStackTrace();
}
myConn.setAutoCommit(false);
CoffeesFrame coffeesFrame = new CoffeesFrame(myConn);
coffeesFrame.pack();
coffeesFrame.setVisible(true);
}
}
The CoffeesTableModel.java as follows, //
public class CoffeesTableModel implements TableModel {
CachedRowSet coffeesRowSet; // The ResultSet to interpret
ResultSetMetaData metadata; // Additional information about the results
int numcols, numrows; // How many rows and columns in the table
public CoffeesTableModel(CachedRowSet rowSetArg) throws SQLException {
this.coffeesRowSet = rowSetArg;
this.metadata = this.coffeesRowSet.getMetaData();
numcols = metadata.getColumnCount();
// Retrieve the number of rows.
this.coffeesRowSet.beforeFirst();
this.numrows = 0;
while (this.coffeesRowSet.next()) {
this.numrows++;
}
this.coffeesRowSet.beforeFirst();
}
public CachedRowSet getCoffeesRowSet() {
return coffeesRowSet;
}
public void addEventHandlersToRowSet(RowSetListener listener) {
this.coffeesRowSet.addRowSetListener(listener);
}
public void insertRow(String coffeeName, int supplierID, float price,
int sales, int total) throws SQLException {
try {
this.coffeesRowSet.moveToInsertRow();
this.coffeesRowSet.updateString("COF_NAME", coffeeName);
this.coffeesRowSet.updateInt("SUP_ID", supplierID);
this.coffeesRowSet.updateFloat("PRICE", price);
this.coffeesRowSet.updateInt("SALES", sales);
this.coffeesRowSet.updateInt("TOTAL", total);
this.coffeesRowSet.insertRow();
this.coffeesRowSet.moveToCurrentRow();
}
catch (SQLException e) {
e.printStackTrace();
// JDBCTutorialUtilities.printSQLException(e);
}
}
public void close() {
try {
coffeesRowSet.getStatement().close();
}
catch (SQLException e) {
e.printStackTrace();
// JDBCTutorialUtilities.printSQLException(e);
}
}
/** Automatically close when we're garbage collected */
protected void finalize() {
close();
}
/** Method from interface TableModel; returns the number of columns */
public int getColumnCount() {
return numcols;
}
/** Method from interface TableModel; returns the number of rows */
public int getRowCount() {
return numrows;
}
/**
* Method from interface TableModel; returns the column name at columnIndex
* based on information from ResultSetMetaData
*/
public String getColumnName(int column) {
try {
return this.metadata.getColumnLabel(column + 1);
}
catch (SQLException e) {
return e.toString();
}
}
/**
* Method from interface TableModel; returns the most specific superclass
* for all cell values in the specified column. To keep things simple, all
* data in the table are converted to String objects; hence, this method
* returns the String class.
*/
public Class getColumnClass(int column) {
return String.class;
}
/**
* Method from interface TableModel; returns the value for the cell
* specified by columnIndex and rowIndex. TableModel uses this method to
* populate itself with data from the row set. SQL starts numbering its rows
* and columns at 1, but TableModel starts at 0.
*/
public Object getValueAt(int rowIndex, int columnIndex) {
try {
this.coffeesRowSet.absolute(rowIndex + 1);
Object o = this.coffeesRowSet.getObject(columnIndex + 1);
if (o == null)
return null;
else
return o.toString();
}
catch (SQLException e) {
return e.toString();
}
}
/**
* Method from interface TableModel; returns true if the specified cell is
* editable. This sample does not allow users to edit any cells from the
* TableModel (rows are added by another window control). Thus, this method
* returns false.
*/
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
// Because the sample does not allow users to edit any cells from the
// TableModel, the following methods, setValueAt, addTableModelListener,
// and removeTableModelListener, do not need to be implemented.
public void setValueAt(Object value, int row, int column) {
System.out.println("Calling setValueAt row " + row + ", column "
+ column);
}
public void addTableModelListener(TableModelListener l) {
}
public void removeTableModelListener(TableModelListener l) {
}
}
The data shows in the table is grabbed from the db and presented as table. The "Add row to table" shows the pop-up from the form insertion but don't put the data as new row in the end of the table. However, I'm not getting any error. The "Update database" button is not working and provides java.sql.SQLException: Table not specified.. How can I improve the code ?
Instead of implementing the TableModel interface, you should be extending from the AbstractTableModel, this has default functionality of some of the "normal" functionality you don't want to replicated.
Remove the addTableModelListener and removeTableModelListener, your current implementation has essentially broken the model's contract by not providing the ability to make notifications to other observers (like the JTable)
Your insertRow method MUST call fireTableRowsInserted (from AbstractTableModel) in order to notify the JTable that the model has changed and that it needs to update itself with the new data
Java makes no guarantees that finalize will be called, don't rely on it
getRowCount will be effected by the size of the RowSet, therefore, you need to be able to either keep a running value (ie you need to increment it when you add new rows) or calculate the number of rows from the RowSet itself. I believe you can do this by moving the cursor to the end (or beyond the last position) and using getRow, don't forget though, ResultSet is 1 based, not 0 based which JTable expects
I haven't dealt with SQLite databases before last week. I last dealt with SQL many years ago, but I still have the gist of it.
The code below reads 140,000 words from an asset named dictionary.dic and inserts each into a SQLite database along with its status. My expectation was that it would take a good while, but it's been like 25 minutes on a 7" tablet and still not near finished (on P).
Should I say, "Hey, it's 1/7 of a million rows. It's gonna take awhile." But I can read all 140,000 words into an ArrayList<String> in 30 seconds. I realize there's overhead in creating the database, but many, many minutes?
Should I say, "Well, think how long it would take if not using AsyncTask" and accept it since it's a one-time task? But it's really obnoxious, taking so long. It's off-putting.
Should I say, "Why are you using a Scanner? No wonder it's taking so long?" and do some other asset access? Or is that not the real problem?
I also have never used AsyncTask. Am I misusing doInBackground? I've got a lot of code in there; not all MUST go there, but the loop is what it is and there's the hangup.
Is using database.Insert, which is called a "convenience method", what's causing the hangup? Should I be using a Cursor and query instead? I'm not entirely sure how I'd do that. Got my idea from Deitel's "Address Book" app in "Android for Programmers--App Driven...", but his database is empty at the outset.
I've given this plenty of thought. I just need someone with experience to look and say, "Well, HERE'S your problem." I can't justify starting redoing all the things I've thought of without some guidance about whether any of it is going to help.
public class DatabaseConnector //extends ArrayList<String>
{
public static Cursor cursor ;
Scanner scDict;
InputStream stream = null;
Context mContext;
AssetManager mAssets;
public static final String DATABASE_NAME = "Dictionary";
public static final String TABLE_NAME = "wordlist";
public static final String WORD_COLUMN_NAME = "word";
public static final String STATUS_COLUMN_NAME = "status";
public static final String [] columns = new String[]{WORD_COLUMN_NAME, STATUS_COLUMN_NAME};
private DatabaseOpenHelper ___databaseOpenHelper; // creates the database
private SQLiteDatabase ___database; // for interacting with the database
public DatabaseConnector(Context _context, AssetManager assets)
{
mContext = _context;
mAssets = assets;
___databaseOpenHelper = new DatabaseOpenHelper(_context, DATABASE_NAME, null, 1);
Log.w("DB connected", ___databaseOpenHelper.getDatabaseName());
createDbIfNecessary();
};
public void open() throws SQLException // opens/creates
{
___database = ___databaseOpenHelper.getWritableDatabase(); // create OR open
}
public void createDbIfNecessary(){
this.open();
if(getDbCount() < 140000){
try { stream = mAssets.open("dictionary.dic"); }
catch (IOException e) { System.out.println(Arrays.toString(e.getStackTrace())); }
MainActivity.setLblProgress("This one-time task takes awhile: loading letter... ");
LoadWords loadWords = new LoadWords();
loadWords.execute((Object[]) null);
this.close();
}
}
public void close(){
if(___database != null)
___database.close();
}
public int getDbCount(){
this.open();
return ___database.query(TABLE_NAME, columns, null, null, null, null, null).getCount();
}
public long insertWord(String _word)
{
ContentValues
__newWord;
__newWord = new ContentValues();
__newWord.put(WORD_COLUMN_NAME, _word);
__newWord.put(STATUS_COLUMN_NAME, true);
long __row = ___database.insert(TABLE_NAME, null, __newWord);
return __row; // -1 if can't insert
}
//////////////////////////////////////////////////////////////////////////////////////////////////
private class DatabaseOpenHelper extends SQLiteOpenHelper
{
public DatabaseOpenHelper(Context _context, String _name, CursorFactory _factory, int _version)
{ super(_context, _name, _factory, _version); }
#Override public void onCreate(SQLiteDatabase _db)
{
_db.execSQL( "CREATE TABLE " + TABLE_NAME +
"("
+ WORD_COLUMN_NAME + " TEXT primary key , " //not null, "
+ STATUS_COLUMN_NAME + " BOOLEAN" +
");"
); // execute query to create the ___database
}
} // end class DatabaseOpenHelper
//////////////////////////////////////////////////////////////////////////////////////////////////
private class LoadWords extends AsyncTask<Object, Integer, Void>
{
#Override
protected Void doInBackground(Object... params) {
long k = 0;
scDict = new Scanner(stream).useDelimiter("\r\n");
long count = getDbCount();
Log.w("Start load at " , "" + count);
String s = "";
while(k++ < count){
s = scDict.next();
}
Log.w("Add after " , s);
while (scDict.hasNext())
{
s = scDict.next();
publishProgress((Integer)(int)s.charAt(0));
insertWord(s) ;
return null;
}
protected void onProgressUpdate(Integer... progress) {
int c = (int)progress[0];
MainActivity.setLastLetterProcessed((char) c);
}
#Override
protected void onPostExecute(Void x)
{
MainActivity.popupMessage("Database has been created", mContext);
}
}
} // end class DatabaseConnector
You are attempting to do 140,000 individual database transactions. That might take weeks.
Instead, either wrap your entire thing in a single transaction, or batch the inserts into transactions (e.g., every 1000 words). You can create your own transaction bounds using this pseudo-Java:
db.beginTransaction();
try {
// do your SQL work here
db.setTransactionSuccesful();
}
catch (Exception e) {
// logging, event bus message to UI, whatever
}
finally {
db.endTransaction();
}
Thanks to #Commonsware, the 140,000 records now load in under a minute, as opposed to under an HOUR. All I did was use his "p-code" to surround my insert with a 1000-count loop:
protected Void doInBackground(Object... params) {
...
scDict = new Scanner(stream).useDelimiter("\r\n");
long count = getDbCount();
while (k++ < count)
s = scDict.next();
while (scDict.hasNext())
{
//////////////////////////////////////////////////////// start insert
int ki = 0;
try
{
___database.beginTransaction();
while (ki < MAX_TRANSACTIONS && scDict.hasNext())
{
//////////////////////////////////////////////////////// end
insertWord(scDict.next());
//////////////////////////////////////////////////////// start insert
++ki;
}
___database.setTransactionSuccessful();
}
catch(Exception e){ Log.w("Exception",e);}
finally
{
___database.endTransaction();
publishProgress((Integer) (int) s.charAt(0));
}
//////////////////////////////////////////////////////// end
}
return null;
}
...
}
This question already has answers here:
Ship an application with a database
(15 answers)
Closed 7 years ago.
I have a static sqlite db. How could I include it into the app? Where should i put it in my project folder? How should I access it from DatabaseHandler?
Everything I found on the web was using sqlite only for creating a new db and storing user or temp data in it, but not using existing db with predefined data.
Official Google docs does not tell how to do that.
Handling this case is basically just doing a file copy.
The tricky part is
To create the database when needed only (otherwise just open it)
To implement the upgrade logic
I wrote a sample Helper class that demonstrate how to load a database from your assets.
public abstract class SQLiteAssetHelper extends SQLiteOpenHelper {
// ----------------------------------
// CONSTANTS
// ----------------------------------
private static final String DATABASE_DIR_NAME = "databases";
// ----------------------------------
// ATTRIBUTES
// ----------------------------------
private final Context mContext;
private final CursorFactory mFactory;
private SQLiteDatabase mDatabase;
private String mDatabaseName;
private String mDatabaseAssetPath;
private String mDatabaseDiskPath;
private boolean mIsProcessingDatabaseCreation; // Database creation may take some time
// ----------------------------------
// CONSTRUCTORS
// ----------------------------------
public SQLiteAssetHelper(Context context, String name, CursorFactory factory, String destinationPath, int version) {
super(context, name, factory, version);
mContext = context;
mFactory = factory;
mDatabaseName = name;
mDatabaseAssetPath = DATABASE_DIR_NAME + "/" + name;
if (destinationPath == null) {
mDatabaseDiskPath = context.getApplicationInfo().dataDir + "/" + DATABASE_DIR_NAME;
} else {
mDatabaseDiskPath = destinationPath;
}
}
// ----------------------------------
// OVERRIDEN METHODS
// ----------------------------------
#Override
public synchronized SQLiteDatabase getWritableDatabase() {
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
// the database is already open and writable
return mDatabase;
}
if (mIsProcessingDatabaseCreation) {
throw new IllegalStateException("getWritableDatabase is still processing");
}
SQLiteDatabase db = null;
boolean isDatabaseLoaded = false;
try {
mIsProcessingDatabaseCreation = true;
db = createOrOpenDatabase();
// you should probably check for database new version and process upgrade if necessary
onOpen(db);
isDatabaseLoaded = true;
return db;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
mIsProcessingDatabaseCreation = false;
if (isDatabaseLoaded) {
if (mDatabase != null) {
try {
mDatabase.close();
} catch (Exception e) {
e.printStackTrace();
}
}
mDatabase = db;
} else {
if (db != null) db.close();
}
}
}
#Override
public final void onCreate(SQLiteDatabase db) {
// getWritableDatabase() actually handles database creation so nothing to code here
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO implement your upgrade logic here
}
// ----------------------------------
// PRIVATE METHODS
// ----------------------------------
private void copyDatabaseFromAssets() throws IOException {
String dest = mDatabaseDiskPath + "/" + mDatabaseName;
String path = mDatabaseAssetPath;
InputStream is = mContext.getAssets().open(path);
File databaseDestinationDir = new File(mDatabaseDiskPath + "/");
if (!databaseDestinationDir.exists()) {
databaseDestinationDir.mkdir();
}
IOUtils.copy(is, new FileOutputStream(dest));
}
private SQLiteDatabase createOrOpenDatabase() throws IOException {
SQLiteDatabase db = null;
File file = new File (mDatabaseDiskPath + "/" + mDatabaseName);
if (file.exists()) {
db = openDatabase();
}
if (db != null) {
return db;
} else {
copyDatabaseFromAssets();
db = openDatabase();
return db;
}
}
private SQLiteDatabase openDatabase() {
try {
SQLiteDatabase db = SQLiteDatabase.openDatabase(
mDatabaseDiskPath + "/" + mDatabaseName, mFactory, SQLiteDatabase.OPEN_READWRITE);
return db;
} catch (SQLiteException e) {
e.printStackTrace();
return null;
}
}
// ----------------------------------
// NESTED CLASSES
// ----------------------------------
private static class IOUtils {
private static final int BUFFER_SIZE = 1024;
public static void copy(InputStream in, OutputStream outs) throws IOException {
int length;
byte[] buffer = new byte[BUFFER_SIZE];
while ((length = in.read(buffer)) > 0) {
outs.write(buffer, 0, length);
}
outs.flush();
outs.close();
in.close();
}
}; // IOUtils
}
Then you only have to create a class that extends from the one above like this :
public class MyDbHelper extends SQLiteAssetHelper {
// ----------------------------------
// CONSTANTS
// ----------------------------------
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_FILE_NAME = "test.db";
// ----------------------------------
// CONSTRUCTORS
// ----------------------------------
public MyDbHelper(Context context) {
super(context, DATABASE_FILE_NAME, null, context.getFilesDir().getAbsolutePath(), DATABASE_VERSION);
}
}
Each time you will call getWritableDatabase() from your MyDbHelper instance, it will do all the copy/open stuff for you and return the writable database.
As I said before, I did not implement the upgrade() method in this sample, you'll have to.
I also didn't implement getReadableDatabase() since I usually only use getWritableDatabase(). You may need to do it.
If you want to test it, just do the following:
Copy the code above
Create a folder in your assets called "databases" and insert your sqlite database file in it
In MyDatabaseHelper, change the value of the DATABASE_FILE_NAME constants with the name of the database in the asset folder
Don't forget to instantiate the MyDatabaseHelper and call for getWritableDatabse()
Hope this helped.
I have this error when I run my Android application:
No fields have a DatabaseField
annotation in class [[Lmodel.Vak;
My class Vak has annotations, so I really don't understand why it still giving me this error.
package model;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
#DatabaseTable(tableName = "vak")
public class Vak {
#DatabaseField(generatedId = true, columnName = "vakID", id=true) Long id;
#DatabaseField int rij;
#DatabaseField
int kolom;
...
}
I have a file called Databasehelper.java in which extends OrmLiteSqLiteOpenHelper and the file looks like this:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
// name of the database file for your application -- change to something
// appropriate for your app
private static final String DATABASE_NAME = "project56.db";
// any time you make changes to your database objects, you may have to
// increase the database version
private static final int DATABASE_VERSION = 1;
private DatabaseType databaseType = new SqliteAndroidDatabaseType();
// the DAO object we use to access the tables
private Dao<Vleugel, Long> vleugelDao = null;
private Dao<Verdieping, Long> verdiepingDao = null;
private Dao<NavigatiePunt, Long> navigatiePuntDao = null;
private Dao<Lokaal, Long> lokaalDao = null;
private Dao<Raster, Long> rasterDao = null;
private Dao<Vak, Long> vakDao = null;
private Dao<Graaf, Long> graafDao = null;
private Dao<Vertex, Long> vertexDao = null;
private Dao<Edge, Long> edgeDao = null;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* This is called when the database is first created. Usually you should
* call createTable statements here to create the tables that will store
* your data.
*/
#Override
public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
try {
Log.i(DatabaseHelper.class.getName(), "onCreate");
TableUtils.createTable(connectionSource, Vleugel.class);
TableUtils.createTable(connectionSource, Verdieping.class);
TableUtils.createTable(connectionSource, NavigatiePunt.class);
TableUtils.createTable(connectionSource, Lokaal.class);
TableUtils.createTable(connectionSource, Raster.class);
TableUtils.createTable(connectionSource, Vak.class);
TableUtils.createTable(connectionSource, Graaf.class);
TableUtils.createTable(connectionSource, Vertex.class);
TableUtils.createTable(connectionSource, Edge.class);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't create database", e);
throw new RuntimeException(e);
}
}
/**
* This is called when your application is upgraded and it has a higher
* version number. This allows you to adjust the various data to match the
* new version number.
*/
#Override
public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource,
int oldVersion, int newVersion) {
try {
Log.i(DatabaseHelper.class.getName(), "onUpgrade");
TableUtils.dropTable(connectionSource, Vleugel.class, true);
TableUtils.dropTable(connectionSource, Verdieping.class, true);
TableUtils.dropTable(connectionSource, NavigatiePunt.class, true);
TableUtils.dropTable(connectionSource, Lokaal.class, true);
TableUtils.dropTable(connectionSource, Raster.class, true);
TableUtils.dropTable(connectionSource, Vak.class, true);
TableUtils.dropTable(connectionSource, Graaf.class, true);
TableUtils.dropTable(connectionSource, Vertex.class, true);
TableUtils.dropTable(connectionSource, Edge.class, true);
// after we drop the old databases, we create the new ones
onCreate(db, connectionSource);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e);
throw new RuntimeException(e);
}
}
/**
* Returns the Database Access Object (DAO) for the classes It will create
* it or just give the cached value.
*/
public Dao<Vleugel, Long> getVleugelDao() throws SQLException {
if (vleugelDao == null) {
vleugelDao = getDao(Vleugel.class);
}
return vleugelDao;
}
public Dao<Verdieping, Long> getVerdiepingDao() throws SQLException {
if (verdiepingDao == null) {
verdiepingDao = getDao(Verdieping.class);
}
return verdiepingDao;
}
public Dao<NavigatiePunt, Long> getNavigatiePuntDao() throws SQLException {
if (navigatiePuntDao == null) {
navigatiePuntDao = getDao(NavigatiePunt.class);
}
return navigatiePuntDao;
}
public Dao<Lokaal, Long> getLokaalDao() throws SQLException {
if (lokaalDao == null) {
lokaalDao = getDao(Lokaal.class);
}
return lokaalDao;
}
public Dao<Raster, Long> getRasterDao() throws SQLException {
if (rasterDao == null) {
rasterDao = getDao(Raster.class);
}
return rasterDao;
}
public Dao<Vak, Long> getVakDao() throws SQLException {
if (vakDao == null) {
vakDao = getDao(Vak.class);
}
return vakDao;
}
public Dao<Graaf, Long> getGraafDao() throws SQLException {
if (graafDao == null) {
graafDao = getDao(Graaf.class);
}
return graafDao;
}
public Dao<Vertex, Long> getVertexDao() throws SQLException {
if (vertexDao == null) {
vertexDao = getDao(Vertex.class);
}
return vertexDao;
}
public Dao<Edge, Long> getEdgeDao() throws SQLException {
if (edgeDao == null) {
edgeDao = getDao(Edge.class);
}
return edgeDao;
}
/**
* Close the database connections and clear any cached DAOs.
*/
#Override
public void close() {
super.close();
vleugelDao = null;
verdiepingDao = null;
navigatiePuntDao = null;
lokaalDao = null;
rasterDao = null;
vakDao = null;
graafDao = null;
vertexDao = null;
edgeDao = null;
}
}
I also have a file Controller which extends OrmLiteBaseActivity:
public class Controller extends OrmLiteBaseActivity<DatabaseHelper> {
Dao<Vleugel, Long> vleugelDao;
Dao<Verdieping, Long> verdiepingDao;
Dao<NavigatiePunt, Long> navigatiePuntDao;
Dao<Lokaal, Long> lokaalDao;
Dao<Raster, Long> rasterDao;
Dao<Graaf, Long> graafDao;
Dao<Vertex, Long> vertexDao;
Dao<Edge, Long> edgeDao;
Dao<Vak, Long> vakDao;
// Databasehelper is benodigd voor ORMLite
static {
OpenHelperManager.setOpenHelperFactory(new SqliteOpenHelperFactory() {
public OrmLiteSqliteOpenHelper getHelper(Context context) {
return new DatabaseHelper(context);
}
});
}
public Controller() throws SQLException {
/** initialiseren van dao */
vleugelDao = getHelper().getVleugelDao();
verdiepingDao = getHelper().getVerdiepingDao();
navigatiePuntDao = getHelper().getNavigatiePuntDao();
lokaalDao = getHelper().getLokaalDao();
rasterDao = getHelper().getRasterDao();
graafDao = getHelper().getGraafDao();
vertexDao = getHelper().getVertexDao();
edgeDao = getHelper().getEdgeDao();
vakDao = getHelper().getVakDao();
}
/**
* Haalt vleugel idNaam op uit dao object bijv. K1
*
* #return Vleugel
* #throws java.sql.SQLException
*/
public Vleugel getVleugel(String vleugelIDNaam)
throws java.sql.SQLException {
// select * from vleugel where idNaam='{vleugelIDNaam}'
QueryBuilder<Vleugel, Long> qb = vleugelDao.queryBuilder();
Where where = qb.where();
// the name field must be equal to "foo"
where.eq("idNaam", vleugelIDNaam);
PreparedQuery<Vleugel> preparedQuery = qb.prepare();
List<Vleugel> vleugelList = vleugelDao.query(preparedQuery);
Log.v("Getvleugel", vleugelList.size() + "");
if (vleugelList.size() == 1) {
return vleugelList.get(0);
}
return null;
}
public Verdieping getVerdieping(int nummer) throws java.sql.SQLException {
// TODO: Met querybuilder query naar db om verdieping te pakken
return null;
}
/**
* Haalt navigatiepunt op
*
* #param naam
* #return
* #throws java.sql.SQLException
*/
public NavigatiePunt getNavigatiePunt(String naam)
throws java.sql.SQLException {
// select * from navigatiepunt where naam='{naam}'
QueryBuilder<NavigatiePunt, Long> qb = navigatiePuntDao.queryBuilder();
Where where = qb.where();
where.eq("naam", naam);
PreparedQuery<NavigatiePunt> preparedQuery = qb.prepare();
List<NavigatiePunt> navigatieList = navigatiePuntDao
.query(preparedQuery);
Log.v("GetLokaal", navigatieList.size() + "");
if (navigatieList.size() == 1) {
return navigatieList.get(0);
}
return null;
}
/**
* Get lokaal object op basis van lokaalcode
*
* #param lokaalcode
* #return
* #throws java.sql.SQLException
*/
public Lokaal getLokaal(String lokaalcode) throws java.sql.SQLException {
// select * from lokaal where lokaalcode='{lokaalcode}'
QueryBuilder<Lokaal, Long> qb = lokaalDao.queryBuilder();
Where where = qb.where();
where.eq("lokaalcode", lokaalcode);
PreparedQuery<Lokaal> preparedQuery = qb.prepare();
List<Lokaal> lokaalList = lokaalDao.query(preparedQuery);
Log.v("GetLokaal", lokaalList.size() + "");
if (lokaalList.size() == 1) {
return lokaalList.get(0);
}
return null;
}
}
So do you have any advice on this, what should I check?
Could you check, are created table Vak in your DB? The absence of this table can be reason of this crash.
This turned out to be a bug in ORMLite around foreign object loops that was fixed in version 4.22. ORMLite was not properly handing the case where A has a foreign-field B which has a foreign-field to C which has a foreign-field back to A..
http://ormlite.com/releases/
Please send me some direct mail #Yanny if this does or doesn't work and I will tune this answer accordingly.
It's probably late for giving solution but this is my solution:
you see proguard try to obfuscating the code and if you read proguard in depth or intro
http://proguard.sourceforge.net/FAQ.html
what Shrinking in proguard -> Shrinking programs such as ProGuard can analyze bytecode and remove unused classes, fields, and methods.
so from this we can presume that it is removing your objects since it's not used by anywhere...
so wt you probably need?
you need to stop proguard from shirking that methods or objects from process
so this is the line for that..:
-keep class com.j256.**<br>
-keepclassmembers class com.j256.** { *; }<br>
-keep enum com.j256.**<br>
-keepclassmembers enum com.j256.** { *; }<br>
-keep interface com.j256.**<br>
-keepclassmembers interface com.j256.** { *; }
this line will keep proguard from removing my public methods and variables..
-keepclassmembers class classpath.** {
public *;
}
you need to write column name for atlest id... because it will search for it and will proguard change it's name... so you need to define column name id for primary key..