Migration from SQLite to Room DB - java

I am facing a problem while migrating from SQLite to Room DB.
The problem is my old SQLite schema doesn't match with the new Room DB schema in my old SQLite DB I forgot to set primaryKey to NOT NULL and also I have a column URL and that column doesn't have any type like TEXT, INTEGER or BOOLEAN.
So now when I try to migrate my SQLite to Room I got Schema don't match error and my App is published on play store with SQLite DB.
So any help will be highly appreciated.
My old SQLite DB code:
private static final String DATABASE_NAME = "mylist.db";
private static final String TABLE_NAME = "mylist_data";
private static final String POST_TITLE = "ITEM1";
private static final String POST_URL = "URL";
private static final String KEY_ID = "ID";
public BookmarksDb(Context context) {
super(context, DATABASE_NAME, null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
String createTable = "CREATE TABLE " + TABLE_NAME + " (ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
" ITEM1 TEXT," +
" URL)";
db.execSQL(createTable);
}
My new Room DB code:
#Entity(tableName = "mylist_data")
public class Bookmark {
#PrimaryKey()
#ColumnInfo(name = "ID")
private int id;
#ColumnInfo(name = "ITEM1")
private String postTitle;
#ColumnInfo(name = "URL")
private String postUrl;
Problems are:
ID is NOT NULL = false in SQLite and in Room it is true by default(can't change it)
URL column in SQLite doesn't have any type and in Room it is TEXT by default.
I don't want to lose my old data which is stored in SQLite and my app is published, so now I want to migrate to Room without losing old user data.
Please help me to solve this problem.

You have two issue, the first the NOT NULL required is due to how Room handles primatives. So instead of using int use Integer (although really you should use Long). So change the Entity to be :-
#Entity(tableName = "mylist_data")
public class Bookmark {
#PrimaryKey()
#ColumnInfo(name = "ID")
private Integer id;
#ColumnInfo(name = "ITEM1")
private String postTitle;
#ColumnInfo(name = "URL")
private String postUrl;
The second issue is the column affinity, you need to ALTER your table to suit the Entity, As you have private String postUrl; then as you have found Room expects a column type of TEXT as opposed to nothing (UNDEFINED Affinity = 1).
To circumvent this, you could run the following SQL's to convert the table to suit Room:-
DROP TABLE IF EXISTS converted_mylist_data;
DROP TABLE IF EXISTS old_mylist_data;
CREATE TABLE IF NOT EXISTS converted_mylist_data (ID INTEGER PRIMARY KEY AUTOINCREMENT, ITEM1 TEXT, URL TEXT);
INSERT INTO converted_mylist_data SELECT * FROM mylist_data; /* copies existing data into new table */
ALTER TABLE mylist_data RENAME TO old_mylist_data;
ALTER TABLE converted_mylist_data RENAME TO mylist_data;
DROP TABLE IF EXISTS old_mylist_data;
Note you can actually retrieve the SQL to create the new table from the java(generated)
Example
Run 1 creates the database (version 1) not using Room using:-
db.execSQL("CREATE TABLE mylist_data (ID INTEGER PRIMARY KEY AUTOINCREMENT,ITEM1 TEXT, URL);");
db.execSQL("INSERT INTO mylist_data VALUES(null,'item1','my url');");
The following changes are then made :-
Added the Entity
:-
#Entity(tableName = "mylist_data")
public class Bookmark {
#PrimaryKey()
#ColumnInfo(name = "ID")
private Long id; /* <<<<<<<<<< CHANGED (could be Integer) from primative to object*/
#ColumnInfo(name = "ITEM1")
private String postTitle;
#ColumnInfo(name = "URL")
private String postUrl;
public Bookmark(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPostTitle() {
return postTitle;
}
public void setPostTitle(String postTitle) {
this.postTitle = postTitle;
}
public String getPostUrl() {
return postUrl;
}
public void setPostUrl(String postUrl) {
this.postUrl = postUrl;
}
}
A Dao
:-
#Dao
interface AllDao {
#Query("SELECT * FROM mylist_data")
List<Bookmark> getAll();
}
The Database with the version increased and a Migration for version 1 to 2
:-
#Database(entities = Bookmark.class,version = 2 /*<<<<<<<<<<*/,exportSchema = false)
abstract class TheDatabase extends RoomDatabase {
abstract AllDao getAllDao();
private static volatile TheDatabase instance;
public static TheDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase.class,"mylist.db")
.allowMainThreadQueries()
.addMigrations(MIGRATION_1_2)
.build();
}
return instance;
}
static final Migration MIGRATION_1_2 = new Migration(1,2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
database.beginTransaction();
database.execSQL("DROP TABLE IF EXISTS converted_mylist_data;");
database.execSQL("DROP TABLE IF EXISTS oldmylist_data;");
database.execSQL("CREATE TABLE IF NOT EXISTS converted_mylist_data (ID INTEGER PRIMARY KEY AUTOINCREMENT, ITEM1 TEXT, URL TEXT);");
database.execSQL("INSERT INTO converted_mylist_data SELECT * FROM mylist_data;");
database.execSQL("ALTER TABLE mylist_data RENAME TO oldmylist_data;");
database.execSQL("ALTER TABLE main.converted_mylist_data RENAME TO mylist_data;");
database.setTransactionSuccessful();
database.endTransaction();
}
};
}
The changed invoking/using activity (from SQLite to Room with old code commented out)
:-
public class MainActivity extends AppCompatActivity {
//DBHelper db; /* Run 1 */
TheDatabase db; /* Run 2 NEW */
AllDao dao; /* Run 2 NEW */
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* Run 1 create the SQLite based database */
/*
db = new DBHelper(this);
db.getWritableDatabase();
*/
/* Run 2 NEW */
db = TheDatabase.getInstance(this);
dao = db.getAllDao();
for (Bookmark b: dao.getAll()) {
Log.d("BOOKMARKINFO","ID = " + b.getId() + " PostTitle = " + b.getPostTitle() + " PostURL =" + b.getPostUrl());
}
}
}
Result :-
Successfully runs and outputs :-
D/BOOKMARKINFO: ID = 1 PostTitle = item1 PostURL =my url
i.e. data has been kept.

By default sqlite use Blob column type if type not defined in create table statement . Paragraph 3.1.3 of sqlite doc. That's why you can use #ColumnInfo(name = "URL", typeAffinity = ColumnInfo.BLOB) to solve your second problem. You declare id with type int which cant be null, try to use Integer instead int - i think it solve your first problem.
I think you have other option to migrate on room and not lose your data: use migration mechanism.

Related

How to insert values with Foreign KEY in SQLite?

I am new with Android Sqlite Database.
I have created a database with SQLite in Android and I have a table Student_details which use a foreign key of the table: Student, I have made the id as AUTOINCREMENT.
And i tried to return the value of the AUTOINCREMENT Id from Student table.
But I am stuck to use the return values to insert into COLUMN_FR_KEY_USER_ID = "student_id"; as Foreign Key values.
How can I add value with a foreign key from another table using ContentValues into insetDataIntoStudentDetails method?
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "finalStudent.db";
private static final int VERSION_NUMBER = 16;
private static final String TABLE_STUDENT = "Student";
private static final String TABLE_DETAILS_NAME = "Student_details";
private static final String COLUMN_STUDENT_ID = "id";
private static final String COLUMN_DETAILS_ID = "id";
private static final String COLUMN_FR_KEY_USER_ID = "student_id";
private static final String COLUMN_STUDENT_NAME = "name";
private static final String COLUMN_DETAILS_NAME = "name";
private static final String COLUMN_STUDENT_EMAIL = "email";
private static final String COLUMN_STUDENT_PASSWORD = "password";
private static final String CREATE_STUDENT_TABLE = "CREATE TABLE "+TABLE_STUDENT+"( "+COLUMN_STUDENT_ID+" INTEGER PRIMARY KEY AUTOINCREMENT, "+COLUMN_STUDENT_NAME+" TEXT, "+COLUMN_STUDENT_EMAIL+" TEXT, "+COLUMN_STUDENT_PASSWORD+" TEXT)";
private static final String CREATE_DETAILS_TABLE = "CREATE TABLE "+TABLE_DETAILS_NAME+"( "+COLUMN_DETAILS_ID+" INTEGER PRIMARY KEY AUTOINCREMENT, "+COLUMN_DETAILS_NAME+" TEXT, "+COLUMN_FR_KEY_USER_ID+" INTEGER, FOREIGN KEY("+COLUMN_FR_KEY_USER_ID+") REFERENCES "+TABLE_STUDENT+" ("+COLUMN_STUDENT_ID+") ON UPDATE CASCADE ON DELETE CASCADE)";
private Context context;
private static final String DROP_TABLE = "DROP TABLE IF EXISTS "+TABLE_STUDENT;
private static final String DROP_DETAILS_TABLE = "DROP TABLE IF EXISTS "+TABLE_DETAILS_NAME;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, VERSION_NUMBER);
this.context = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
try {
Toast.makeText(context,"Table created successfully and called onCreate method",Toast.LENGTH_SHORT).show();
db.execSQL(CREATE_STUDENT_TABLE);
db.execSQL(CREATE_DETAILS_TABLE);
}catch (Exception e){
Toast.makeText(context,"Exception : "+e,Toast.LENGTH_SHORT).show();
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
try {
Toast.makeText(context,"Table created successfully and called onCreate method",Toast.LENGTH_SHORT).show();
db.execSQL(DROP_TABLE);
db.execSQL(DROP_DETAILS_TABLE);
onCreate(db);
}catch (Exception e){
Toast.makeText(context,"Exception : "+e,Toast.LENGTH_SHORT).show();
}
}
public long insertDataIntoStudent( String name, String email, String password){
// to write or read data in database , we have to call getWritableDatabase
SQLiteDatabase sqLiteDatabase = this.getWritableDatabase();
// we have to call contentValues class to store data in the data
ContentValues contentValues = new ContentValues();
contentValues.put(COLUMN_STUDENT_NAME,name);
contentValues.put(COLUMN_STUDENT_EMAIL,email);
contentValues.put(COLUMN_STUDENT_PASSWORD,password);
// Insert the new row, returning the primary key value of the new row
long newRowId = sqLiteDatabase.insert(TABLE_STUDENT,null,contentValues);
return newRowId;
}
public void insetDataIntoStudentDetails(String name){
SQLiteDatabase sqLiteDatabase = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
// TODO: insert the foreign key's value
contentValues.put(name,name);
}
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
//enable foreign key constraints like ON UPDATE CASCADE, ON DELETE CASCADE
db.execSQL("PRAGMA foreign_keys=ON;");
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private final AppCompatActivity activity = MainActivity.this;
DatabaseHelper databaseHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
databaseHelper = new DatabaseHelper(activity);
databaseHelper.insertDataIntoStudent("Bappy","abcd#gmail.com","145456");
}
}
I'll answer about databases in general, not specifically about Sqlite or it's usage in Android.
So you have student's name (which is not a primary key), but you need to save some additional data for this student to another table. The steps are:
Execute SELECT id FROM students WHERE name = ? to find the relevant identifier.
Execute INSERT INTO student_details for your secondary table using retrieved id as a key.
Extra tips:
Name is not a good unique identifier. It's good to have one more criteria - such as some group name, or group number or even date of birth.
When your database is large enough you may benefit from index for your search criteria to optimize execution of the first query. But usually it's not the case for client-side (in-app) databases.
Here we assume that name is not the data which could be updated by someone (or something) else between searching for id and executing INSERT. Otherwise it's wise to use transaction and "lock" your student until you complete the updates.

Mapping #ManyToOne / Foreign Key Hibernate Java

First of all, sorry if the subject has already been answered, but i've been searching for almost 6hours in a row, and trying everything i could find on the web, but i'm still stuck..
I Have a problem when i'm executing my code.. I can't find the origin of it, nor a solution...
Here's my different classes: (btw it's my first post on SOF, let me know if you need more information).
I'm going to post my Pojo's, useful DAO's and MySQL table creation orders, and the error message i get.
POJOs:
-Celebrite:
#Entity
#Table(name="Celebrite")
public class Celebrite implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name="numCelebrite")
#GeneratedValue(strategy=GenerationType.IDENTITY)
#JoinColumn(name="numCelebrite")
private int numCelebrite;
#Column(name="nom")
private String nom;
#Column(name="prenom")
private String prenom;
#Column(name="nationalite")
private String nationalite;
#Column(name="epoque")
private String epoque;
public Celebrite() {
super();
}
public Celebrite(String nom, String prenom, String nationalite, String epoque) {
super();
this.nom = nom;
this.prenom = prenom;
this.nationalite = nationalite;
this.epoque = epoque;
}
public int getNumCelebrite() {
return numCelebrite;
}
public void setNumCelebrite(int numCelebrite) {
this.numCelebrite = numCelebrite;
}
//Other getters/setters
}
-Monument
#Entity
public class Monument implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private String codeM;
private String nomM;
private String proprietaire;
private String typeMonument;
private float latitude;
private float longitude;
public Monument() {
super();
}
public Monument( String codeM,String nomM, String propritaire, String typeMonument, float latitude, float longitude) {
super();
this.codeM=codeM;
this.nomM = nomM;
this.proprietaire = propritaire;
this.typeMonument = typeMonument;
this.latitude = latitude;
this.longitude = longitude;
}
public Monument( String nomM, String propritaire, String typeMonument, float latitude, float longitude) {
super();
this.nomM = nomM;
this.proprietaire = propritaire;
this.typeMonument = typeMonument;
this.latitude = latitude;
this.longitude = longitude;
}
public String getCodeM() {
return codeM;
}
public void setCodeM(String codeM) {
this.codeM = codeM;
}
//other getters/setters..
}
-AssocieA (translation : AssociatedTo)
#Entity
public class AssocieA implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#ManyToOne(fetch=FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="numCelebrite",referencedColumnName="numCelebrite")
private Celebrite celebrite;
//private int numCelebrite;
#Id
#ManyToOne(fetch=FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="codeM",referencedColumnName="codeM")
//private String codeM;
private Monument monument;
public AssocieA() {
}
public AssocieA(Celebrite celebrite, Monument monument) {
super();
this.celebrite = celebrite;
this.monument = monument;
}
//getters/setters
}
Ok now the DAO, i'm only posting AssocieA's DAO as the others are working perfectly
public class DAOAssocieA {
EntityManagerFactory emf;
EntityManager em ;
public DAOAssocieA() {
super();
this.emf = Persistence.createEntityManagerFactory("jpa-societe-pu");
this.em = emf.createEntityManager();
}
public List<AssocieA> getAssociatedMonuments(int numCelebrite){
Query req=em.createQuery("Select a from AssocieA a where a.numCelebrite =" + numCelebrite);
return req.getResultList();
}
public List<AssocieA> getAssociatedCelebrities(String codeM){
Query req=em.createQuery("Select a from AssocieA a where a.codeM = '"+codeM+"'");
return req.getResultList();
}
}
Finally, the 'main' class
public String execute() {
setDAOc(new DAOCelebrite());
setDAOm(new DAOMonument());
setDAOa(new DAOAssocieA());
if (getNom()==null)
setNom("");
if (getPrenom() == null)
setPrenom("");
if (getNationalite() == null)
setNationalite("");
if (getEpoque()==null)
setEpoque("");
setListeCelebrite(DAOc.getCelebritiesBy(getNom(),getPrenom(),getNationalite(), getEpoque()));
System.out.println(getAssociated());
if (getAssociated().equals("on")) {
for (Celebrite c:listeCelebrite) {
for (AssocieA a : DAOa.getAssociatedMonuments(c.getNumCelebrite())){
System.out.println(a.getCelebrite());
System.out.println(a.getMonument());
}
}
}
return ("success");
-> The return ("success") is because i use Struts2
Now, Table creation orders (on MySQL)
CREATE TABLE Celebrite (numCelebrite int auto_increment, nom varchar(16), prenom varchar(16), nationalite varchar(10), epoque varchar(6), PRIMARY KEY (numCelebrite)) ENGINE=InnoDB;
CREATE TABLE Monument (codeM varchar(5), nomM varchar(25), proprietaire varchar(10), typeMonument varchar(16), longitude float, latitude float, PRIMARY KEY (codeM)) ENGINE=InnoDB;
CREATE TABLE AssocieA (codeM varchar(5), numCelebrite int, PRIMARY KEY (codeM,numCelebrite), FOREIGN KEY (codeM) REFERENCES Monument(codeM), FOREIGN KEY (numCelebrite) REFERENCES Celebrite(numCelebrite)) ENGINE=InnoDB;
To finish, the error message i get:
org.hibernate.QueryException: could not resolve property: numCelebrite of: pojo.AssocieA [Select a from pojo.AssocieA a where a.numCelebrite =1]
I understand that the class 'AssocieA' doesn't have a "numCelebrite" property, but i thought that because of the #ManyToOne annotation, Celebrite Table should be loaded when Associe is loaded.
Else, could you give me some tips to explain how to do that?
The final goal is: having a Celebrite, i'd like, using the numCelebrite, to retrieve every Monuments related to it, using the AssocieA table.
Thank you in advance
EDIT: Solution found on another website by kulturman:
I was using native queries:
em.createQuery("from AssocieA a where a.numCelebrite  =" + numCelebrite);
Instead of JPQL (HQL queries):
em.createQuery("from AssocieA a where a.celebrite.numCelebrite  =" + numCelebrite);
For those who want to see directly the solution, it's in french on OpenClassRoom
Try removing the #JoinColumn(name="numCelebrite") and #GeneratedValue(strategy=GenerationType.IDENTITY) in celebrity pojo, change CascadeType.ALL to CascadeType.Persist and let us know what happen. I'm not an expert but I think that way you can do the trick.
Solution found on another website by kulturman:
I had to replace:
em.createQuery("from AssocieA a where a.numCelebrite =" + numCelebrite);
Instead of JPQL (HQL queries):
by:
em.createQuery("from AssocieA a where a.celebrite.numCelebrite =" + numCelebrite);
Here's the explanation:
I was building my query as if i was requesting my database :
The table AssocieA has the attribute "numCelebrite"
But in my case, I mapped the tables to my classes: Each row of the AssocieA table is now an instance of my AssocieA class.
Using a HQL query, i'm requesting on the instance of my class, not the table of my database.
So what i have to do is a.getCelebrite().getNumCelebrite(), hence the "a.celebrite.numCelebrite"
I hope i've made myself clear enough because my english is not that good.
Here's the link of the forum i had this answer on (it's in french):
https://openclassrooms.com/forum/sujet/org-hibernate-queryexception?page=1#message-92165885

How to setup MySQL database tables for an Android Studio Application?

currently I am developing an app for my final class in college. The concept of the app is a Contact keeping app, essentially a user will be greeted by a login screen where they can either login if they already have an account or register if they don't. When you register the user will be prompt to enter their name, username, password and email. They then submit it and they will again be greeted by the login screen where they can now login in with their recently created credentials. Once they login successfully they be be show an Activity that welcomes them with their name that is retrieved from the table in the database by matching the username and password on the login, it retrieves all the information in that row. I pretty much have that down but what I am having trouble with is where am I going to hold the contacts that the users wants to save, I know that is has to be in another table but how am I going to set it up so I can match the a certain user to all of their contacts in the other table.
Once I get that information I need to put it on a ListView so the user can see their contacts and scroll through them, they can then add, delete or edit them.
As of right now I am able to register, login and display the welcome screen with no problem now I just need to be able to add, delete and edit contacts.
Java is used for the Android Studio part and PHP is used for the file that make the connection in the host online.
I am using Volley and JSON request.
The database and PHP files that make the request to the database are hosted in 000webhost.
This seems like a simple CRUD application.
If you use a RESTful communication model it's quite simple to achieve.
As for storing the contacts, you can have a file specific to each user and have that file's URI on the user's table in the DB. The file can be CSV, or JSON which is easily convertable into POJOs with the right API.
Good luck.
First, create a MySQLite Helper class
public class MySQLiteHelper extends SQLiteOpenHelper {
public static final String TABLE_CONTACTS = "contacts";
public static final String COLUMN_CONTACTS_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_USERNAME ="username";
public static final String COLUMN_PASSWORD = "password"
private static final String DATABASE_NAME = "Contacts.db";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_CONTACTS_CREATE = "create table "+ TABLE_CONTACTS+
"("+COLUMN_CONTACTS_ID+" integer primary key autoincrement, "
+COLUMN_NAME+" text not null, "
+COLUMN_USERNAME+" text not null,"
+COLUMN_PASSWORD+" text not null);";
public MySQLiteHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase database){
database.execSQL(DATABASE_CONTACTS_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
Log.w(MySQLiteHelper.class.getName(),"Upgrading database from version "
+oldVersion
+ " to"
+newVersion
+", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS "+TABLE_CONTACTS);
onCreate(db);
}}
Then a model class
public class ContactModel {
private long id;
private String name;
private String username;
private String password;
///////////
public long getId(){
return id;
}
public void setId(long id){
this.id = id;
}
//////////
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public void setUsername(String username){
this.username = username;
}
////////////
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
} }
Then a datasource
public class ContactsDataSource {
private SQLiteDatabase database;
private MySQLiteHelper dbHelper;
private String[] allContactsColumns = {MySQLiteHelper.COLUMN_CONTACTS_ID,MySQLiteHelper.COLUMN_NAME,MySQLiteHelper.COLUMN_USERNAME,
MySQLiteHelper.COLUMN_PASSWORD};
public ContactsDataSource(Context context){
dbHelper = new MySQLiteHelper(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close(){
dbHelper.close();
}
public ContactModel createContact(String name, String username, String password){
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_NAME, name);
values.put(MySQLiteHelper.COLUMN_EMAIL,username);
values.put(MySQLiteHelper.COLUMN_PHONE,password);
long insertContactsId = database.insert(MySQLiteHelper.TABLE_CONTACTS, null, values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_CONTACTS,allContactsColumns,MySQLiteHelper.COLUMN_CONTACTS_ID+" = "
+insertContactsId, null, null, null, null);
cursor.moveToFirst();
ContactModel newContact = cursorToContact(cursor);
cursor.close();
Log.d("written", "Info was written");
return newContact;
}
public void deleteContact(ContactModel contact){
long id = contact.getId();
System.out.println("Comment deleted with id: "+id);
database.delete(MySQLiteHelper.TABLE_CONTACTS,MySQLiteHelper.COLUMN_CONTACTS_ID
+" = "+id,null);
}
public ArrayList<ContactModel> getAllContacts(){
ArrayList<ContactModel> contacts = new ArrayList<>();
Cursor cursor = database.query(MySQLiteHelper.TABLE_CONTACTS, allContactsColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()){
ContactModel contact = cursorToContact(cursor);
contacts.add(contact);
cursor.moveToNext();
}
cursor.close();
return contacts;
}
private ContactModel cursorToContact(Cursor cursor){
ContactModel contact = new ContactModel();
contact.setId(cursor.getLong(0));
contact.setName(cursor.getString(1));
contact.setUsername(cursor.getString(2));
contact.setPassword(cursor.getString(3));
return contact;
}}
You access the datasource as follows:
//Open the database
dataSource = new ContactsDataSource(this);
dataSource.open();
//use datasource functions like create and delete
datasource.close();
You can display the info in a listview by calling ContactsDataSource.getAllContacts(), and displaying the results using a custom listAdapter. The idea is that you will use the Data Source's functions to grab the data you want, and feed it into the model class. From there you can extract what you need for the listview. Let me know if you need more help with that.
You also mentioned wanting to make multiple tables in the database, and that is as easy as using the above code but just adding another variable, such as TABLE_CONTACTS2 = "contacts2", and duplicating the functions for that table.

Android - using variable from another activity

I'm quite new to Android and I guess it's a stupid question but i'll be glad to recieve help. I've got a code in one activity which set a database to SQLite. In another activity I want to refer to this SQLite code in order to enter it into a json and send it to a remote server.
The problem is that it's not recognizing the variable from the other activity. here is the code which creates the data from the db into a string.
In this example I want to create an ArrayList from the db, but it couldnt find the set functions I developed or the table name. Am I missed something ? Here is the code of the ArrayList :
GpsPage.java
public class PersonsDatabaseHandler extends SQLiteOpenHelper {
//Database Name
private static final String DATABASE_NAME = "RecordsDB";
//Table names
private static final TABLE_RECORD = "record";
//Get all Persons
public ArrayList<Record> getAllPersons() {
ArrayList<Record> localList = new ArrayList<Record>();
String selectQuery = "SELECT * FROM " + TABLE_RECORD;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
//Loops through all rows and adds them to the local list
if(cursor.moveToFirst())
{
do {
//Get person information
Record record = new Record();
Record.setpLong(cursor.getString(0));
Record.setpLat(cursor.getString(1));
Record.setpAcc(cursor.getString(2));
Record.setpTime(cursor.getString(3));
//Add person to list
localList.add(record);
} while (cursor.moveToNext());
}
return localList;
}
}
And here are the codes from the two other java pages (activities), the first one define the get and set of the records :
Record.Java
package com.program.android.taskir;
public class Record {
//private variables
private int id;
private double pLong;
private double pLat;
private float pAcc;
private long pTime;
public Record(){}
// Empty constructor
// constructor
public Record( double pLong, double pLat, float pAcc, long pTime){
super();
this.pLong = pLong;
this.pLat= pLat;
this.pAcc= pAcc;
this.pTime= pTime;
}
#Override
public String toString() {
return "Record [id=" + id + ", Longtitude=" + pLong + ", Latitude=" + pLat + ", Accuracy" + pAcc + ", Time" +pTime
+ "]";
}
// getting ID
public int getID(){
return this.id;
}
// setting id
public void setID(int id){
this.id = id;
}
// getting pLong
public double getpLong(){
return this.pLong;
}
// setting pLong
public void setpLong(double pLong){
this.pLong = pLong;
}
// getting pLat
public double getpLat(){
return this.pLat;
}
// setting pLat
public void setpLat(double pLat){
this.pLat = pLat;
}
// getting pAcc
public float getpAcc(){
return this.pAcc;
}
// setting pAcc
public void setpAcc(float pAcc){
this.pAcc = pAcc;
}
// getting pTime
public long getpTime(){
return this.pTime;
}
// setting pTime
public void setpTime(long pTime){
this.pTime = pTime;
}
}
and the activity which creates the db :
MySQLiteHelper.java
package com.program.android.taskir;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.util.LinkedList;
import java.util.List;
public class MySQLiteHelper extends SQLiteOpenHelper {
// Database Version
private static final int DATABASE_VERSION = 1;
// Database Name
private static final String DATABASE_NAME = "RecordsDB";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// SQL statement to create record table
String CREATE_RECORD_TABLE = "CREATE TABLE RECORD ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"latitude TEXT NOT NULL, " +
"longtitude TEXT NOT NULL, " +
"accuracy TEXT NOT NULL, " +
"time TEXT NOT NULL )";
// create books table
db.execSQL(CREATE_RECORD_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Drop older books table if existed
db.execSQL("DROP TABLE IF EXISTS Records");
// create fresh record table
this.onCreate(db);
}
// Books table name
private static final String TABLE_RECORD = "record";
// Books Table Columns names
private static final String KEY_ID = "id";
private static final String KEY_LONG = "longtitude";
private static final String KEY_LAT = "latitude";
private static final String KEY_ACC = "accuracy";
private static final String KEY_TIME = "time";
private static final String[] COLUMNS = {KEY_ID, KEY_LONG, KEY_LAT, KEY_ACC, KEY_TIME};
public void addRecord(Record record) {
//for logging
Log.d("addBook", record.toString());
// 1. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// 2. create ContentValues to add key "column"/value
ContentValues values = new ContentValues();
values.put(KEY_LONG, record.getpLong());
values.put(KEY_LAT, record.getpLat());
values.put(KEY_ACC, record.getpAcc());
values.put(KEY_TIME, record.getpTime());
// 3. insert
db.insert(TABLE_RECORD, // table
null, //nullColumnHack
values); // key/value -> keys = column names/ values = column values
// 4. close
db.close();
}
public Record getRecord(int id) {
// 1. get reference to readable DB
SQLiteDatabase db = this.getReadableDatabase();
// 2. build query
Cursor cursor =
db.query(TABLE_RECORD, // a. table
COLUMNS, // b. column names
" id = ?", // c. selections
new String[]{String.valueOf(id)}, // d. selections args
null, // e. group by
null, // f. having
null, // g. order by
null); // h. limit
// 3. if we got results get the first one
if (cursor != null)
cursor.moveToFirst();
// 4. build book object
Record record = new Record();
record.setID(Integer.parseInt(cursor.getString(0)));
record.setpLat(cursor.getDouble(1));
record.setpLong(cursor.getDouble(2));
record.setpAcc(cursor.getFloat(2));
record.setpTime(cursor.getLong(2));
//log
Log.d("getBook(" + id + ")", record.toString());
// 5. return book
return record;
}
public List<Record> getAllRecords() {
List<Record> records = new LinkedList<Record>();
// 1. build the query
String query = "SELECT * FROM " + TABLE_RECORD;
// 2. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
// 3. go over each row, build book and add it to list
Record record = null;
if (cursor.moveToFirst()) {
do {
record = new Record();
record.setID(Integer.parseInt(cursor.getString(0)));
record.setpLat(cursor.getDouble(1));
record.setpLong(cursor.getDouble(2));
record.setpAcc(cursor.getFloat(2));
record.setpTime(cursor.getLong(2));
// Add book to books
records.add(record);
} while (cursor.moveToNext());
}
Log.d("getAllRecords()", record.toString());
// return books
return records;
}
public int UpdateRecords(Record record) {
// 1. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// 2. create ContentValues to add key "column"/value
ContentValues values = new ContentValues();
values.put("Latitude", record.getpLat()); //
values.put("Longtitude", record.getpLong());
values.put("Accuracy", record.getpAcc());
values.put("Time", record.getpTime());
// 3. updating row
int i = db.update(TABLE_RECORD, //table
values, // column/value
KEY_ID + " = ?", // selections
new String[]{String.valueOf(record.getID())}); //selection args
// 4. close
db.close();
return i;
}
public void deleteRecords(Record record) {
// 1. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// 2. delete
db.delete(TABLE_RECORD, //table name
KEY_ID + " = ?", // selections
new String[]{String.valueOf(record.getID())}); //selections args
// 3. close
db.close();
//log
Log.d("deleteBook", record.toString());
}
}
Thank you!
What you are looking for is a Bundle. It's used to pass data between activities. Take a look at What is a "bundle" in an Android application and you can understand how it's done.

Joining classes in ORMLite for Android throws SQL exception: could not find a foreign class or vice versa

I'm trying to create a join query using QueryBuilder for two different classes, a Product class and a Coupon class, that references a Product attribute, the storeId.
public class Coupon {
#DatabaseField(columnName = TableColumns.PRODUCT, foreign = true, foreignColumnName = Product.TableColumns.STOREID)
private Product product;
}
public class Product {
#DatabaseField(columnName = TableColumns.ID, generatedId = true)
private Integer id;
#DatabaseField(columnName = TableColumns.STOREID, index = true, unique = true)
private Integer storeId;
}
I need to get a Coupon list based on the Product storeId.
public static List<Coupon> getByProduct(Product product) throws SQLException {
QueryBuilder<Coupon, String> couponBuilder = dao.queryBuilder();
QueryBuilder<Product, Integer> productBuilder = Product.dao.queryBuilder();
productBuilder.where().eq(Product.TableColumns.STOREID, product.getStoreId());
return couponBuilder.join(productBuilder).query();
}
This query is throwing a SQL Exception:
04-22 11:26:08.058: W/System.err(19479): java.sql.SQLException: Could not find a foreign class com.cde.express.mccopa.model.Coupon field in class com.cde.express.mccopa.model.Product or vice versa
04-22 11:26:08.059: W/System.err(19479): at com.j256.ormlite.stmt.QueryBuilder.matchJoinedFields(QueryBuilder.java:554)
04-22 11:26:08.059: W/System.err(19479): at com.j256.ormlite.stmt.QueryBuilder.addJoinInfo(QueryBuilder.java:525)
04-22 11:26:08.059: W/System.err(19479): at com.j256.ormlite.stmt.QueryBuilder.join(QueryBuilder.java:316)
The exception says my classes are not related by a foreign field, but the Coupon class has a foreign Product attribute, annotated properly. I already checked the database, the values in the tables are correct.
How can I fix it?
Looking at the code inside QueryBuilder.java
for (FieldType fieldType : tableInfo.getFieldTypes()) {
// if this is a foreign field and its foreign-id field is the same as the other's id
FieldType foreignIdField = fieldType.getForeignIdField();
if (fieldType.isForeign() && foreignIdField.equals(joinedQueryBuilder.tableInfo.getIdField())) {
joinInfo.localField = fieldType;
joinInfo.remoteField = foreignIdField;
return;
}
}
// if this other field is a foreign field and its foreign-id field is our id
for (FieldType fieldType : joinedQueryBuilder.tableInfo.getFieldTypes()) {
if (fieldType.isForeign() && fieldType.getForeignIdField().equals(idField)) {
joinInfo.localField = idField;
joinInfo.remoteField = fieldType;
return;
}
}
Above states that if you want to make a join using QueryBuilder, you need to make sure you put a join on id field so this seems to me like a limitation of QueryBuilder.java in orm-lite.If you have put up a foreign key as id in Coupon.java It would have worked.
A quick workaround should be to use a raw query string to achieve what you want.
For example -
final GenericRawResults<Coupon> results = couponDao.queryRaw("SELECT "
+ "product_table.product_id AS id, "
+ "product_table.store_id AS storeId "
+ " FROM coupon_table "
+ "JOIN product_table ON coupon_table.store_id = product_table.store_id "
+ "WHERE product_table.store_id = 1", new RawRowMapper<Coupon>() {
#Override
public Coupon mapRow(String[] columnNames, String[] resultColumns) throws SQLException {
final Integer productId = Integer.parseInt(resultColumns[0]);
final Integer storeId = Integer.parseInt(resultColumns[1]);
final Product product = new Product();
product.setId(productId);
product.setStoreId(storeId);
final Coupon coupon = new Coupon();
coupon.setProduct(product);
return coupon;
}
});
final Coupon coupon = results.getResults().get(0);
final Product product = coupon.getProduct();
System.out.println("Product id is " + product.getId() + " , Store id is " + product.getStoreId());

Categories