Inserting into a many to many relation in room - android

I am currently building an android app, that uses a small database which consists of two entity-data-classes (Card and CardDeck) and a third one representing a many to many relationsship between the former two by mapping there long id primary keys together (CardInCardDeckRelation).
Now I want to insert a new Deck into my database, which works just fine, but if I want to insert some CardInCardDeckRelation-objects afterwards by using the id returned from the insertCardDeck()-method it fails because the insertion calls on the relationship-table occur before the insertion of the cardDeck object is finished so I am always getting the wrong cardDeck-id.
I think I am going into the right direction by using a Async-task to insert my CardDeck however I don't know to proceed from there since I can only pass one set of Arguments to my async-task object, so I can't pass my Relationshipobjects to be inserted in say for example a onPostExecute-method in the Async-task-class.
This my insert-method in my Repository-class:
public void insertCardDeckAsync(CardDeck cardDeck){
new insertAsyncTaskDao(mCardDeckDao).execute(cardDeck);
}
private static class insertAsyncTaskDao extends AsyncTask<CardDeck, Void, Void> {
private CardDeckDao mAsyncTaskDao;
insertAsyncTaskDao(CardDeckDao dao){
mAsyncTaskDao = dao;
}
#Override
protected Void doInBackground(final CardDeck... params){
mAsyncTaskDao.insertCardDeck(params[0]);
return null;
}
#Override
protected void onPostExecute(Void v){
//maybe insert Relationship object here?
}
}
I would be very thankful if someone could provide a way to properly insert an entity object and some many-to-many relationsship objects afterwards, using the id generated by the former insert.

So after some testing i figured out my error:I was initially using an Executor which I defined somewhere else in my App to handle database operations, so I don't have to create a private inner class extending AsyncTask for every database operation in my Repository class.For some reason though my usage of Executor does seem to block the particular thread when executing database-queries so:
mExecutors.diskIO().execute(new Runnable(){
//insert new Deck
//insert Many-to-Many relationsship-object
}
would execute both operations immediately after one another, thus causing a SQL-ForeignConstraint-related error, because it tries to insert the realtion objects before the actual deck object is inserted.
The solution to this is to just use a AsyncTask instead, handling all the database operation in the right order in the doInBackground-method:
#Override
protected Void doInBackground(final CardDeck... params){
// insert new deck object into database
insertionId = mAsyncTaskDao.insertCardDeck(params[0]);
// create relations-array
CardInCardDeckRelation[] relations = new CardInCardDeckRelation[STANDARD_CARDS.length];
// insert standard-card objects into array
for(int i = 0; i < STANDARD_CARDS.length; i++){
relations[i] = new CardInCardDeckRelation(insertionId,
mAsyncCardDao.getStandardCardByName(STANDARD_CARDS[i]),
i);
}
// insert created array into database
mRelationDao.insertMultiple(relations);
Log.d(LOG_TAG, "Deck inserted");
return null;
}
If anyone needs further explanation I can provide the whole AsyncTask class.

Related

Receiving notification once all read operations are successfully performed

I have a question about some functionalities of Firestore. I am trying to occupy an array of data obtained through multiple read operations from the Firestore database. Is there a way for me to be notified when all the data are successfully read and stored in my array? This is particularly an issue because read operations are not finished in the order that they are called. Here are some code that illustrates my problem:
/* My array to insert the data read from the Firestore database */
String[] my_array = new String[3];
/* A method that will be called to initialize our array */
private void initArray(String doc_one, String doc_two, String doc_three) {
initSingleIndex(0, doc_one);
initSingleIndex(1, doc_two);
initSingleIndex(2, doc_three);
}
private void initSingleIndex(final int index, String doc_id) {
/* We perform our read operation here */
question_ref.document(doc_id).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
my_array[index] = documentSnapshot.getString("some_field");
}
});
}
My current implementation is to keep a global counter, which will be used to keep track of how many read operations were successfully carried out. I am also wondering whether the onSuccess() callbacks can be fired concurrently, since this will then lead to data corruption (i.e. the classic problem of incrementing values concurrently).
Any help or suggestion will be appreciated.

Load data from Parse.com and save in Local Data

I don't understand how is the Parse working?
I download data in parse to my arraylist , but when I show the Pets.size inside (//here) method "done" it will show 4, but when I show pets.size outside the done's method it will show 0?
public class Test extends AppCompatActivity {
ArrayList<Pet> pets;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
pets = new ArrayList<>();
ParseQuery<Pet> query = new ParseQuery<>("Pets");
query.findInBackground(new FindCallback<Pet>() {
#Override
public void done(List<Pet> list, ParseException e) {
if (e!=null){
Toast.makeText(Test.this,"Error",Toast.LENGTH_LONG).show();
}
for (Pet pet : list){
Pet newPet = new Pet();
newPet.setName(pet.getName());
newPet.setType(pet.getType());
pets.add(newPet);
}
// here
}
});
Toast.makeText(Test.this,"You have "+pets.size()+" pets",Toast.LENGTH_LONG).show();
}
Here's my Pet class:
#ParseClassName("Pets")
public class Pet extends ParseObject {
public String getName(){
return getString("name");
}
public void setName(String name) {
put("name", name);
}
public String getType(){
return getString("type");
}
public void setType(String type) {
put("type", type);
}
}
And an orther question , what should I do if I wanna save the data in local data?
Explanation:
findInbackground performs an operation to find all ParseObjects in a background thread (outside the main thread, or UI thread). So when it completes in the place where you have the comment
//here
That is when the background thread finishes it's call to find the objects. When you try to look at the size of the array outside that call where it shows size of 0, it is because it reached that point before the background thread finishes it's work (of adding to your array from objects it found).
What is happening is the operation for find() is happening in parallel with your main threads code.
And for your second question, make sure you enableLocalDatastore and then you can pin results from queries to your local cache. This data is stored on the device until the user deletes your app or clears cached data in settings.
Follow this guide to setup local cache Local Datastore with Parse
Note: A solution to your problem for when the background task of finding the pets is complete is to call a method from within the Callback for the findInBackground call that will handle the newly found Pet ParseObjects. Also remember to handle if the query fails either by finding no objects or some failure in connection / timeout.
just calling pet.pin() or pet.pinInBackground(); you can save a parseObject in local storage , to query objects in local storage you need set query.fromPin(true)
https://parse.com/docs/android/guide#objects-the-local-datastore
"done" method fires when the background task ends.

Android SQLite Query, Insert, Update, Delete, Always Need to be On Background Thread?

I currently use Loaders to grab data from my ContentProvider (to enable auto-updating of my Cursors). This approach is straight-forward for Querying the database, though, it seems ill suited for any other DB operation (such as Insert, Update, Delete).
My questions are:
Do all SQLite operations need to be on a background thread, or is it safe to do simple operations like Inserting, Updating, or Deleting a single row on the UI thread?
What is a nice design patter to ensure all queries go through a background thread? I would like to implement AsyncTask, should I create a SuperTask so to speak that extends AsyncTask and Executes each SQLite operation? (Bonus: Can you provide bare-bones example?)
I have done SQLite operations on my UI Thread. I guess the question really becomes whether your queries will ever take a long time or not. I've never had my application crash from taking too long to execute SQL calls on my SQLite database.
With that said, if you plan on writing complex queries that can take time to load you would want to run it as an AsyncTask or Thread and use callbacks to update your UI if need be.
This is a great tutorial on SQLite on Android (It also addresses some of the complex sql timing issues you were talking about):
http://www.vogella.com/tutorials/AndroidSQLite/article.html
All SQLite operations do not need to be on a background, but should be. Even simple row updates can impact the UI thread and therefore application responsiveness.
Android includes the AsyncQueryHandler abstract class:
A helper class to help make handling asynchronous ContentResolver queries easier.
Here are two example implementations from Using AsyncQueryHandler to Access Content Providers Asynchronously in Android. A member class:
class MyQueryHandler extends AsyncQueryHandler {
public MyQueryHandler(ContentResolver cr) {
super(cr);
}
#Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// query() completed
}
#Override
protected void onInsertComplete(int token, Object cookie, Uri uri) {
// insert() completed
}
#Override
protected void onUpdateComplete(int token, Object cookie, int result) {
// update() completed
}
#Override
protected void onDeleteComplete(int token, Object cookie, int result) {
// delete() completed
}
}
An anonymous class:
AsyncQueryHandler queryHandler = new AsyncQueryHandler(getContentResolver()) {
#Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
if (cursor == null) {
// Some providers return null if an error occurs whereas others throw an exception
}
else if (cursor.getCount() < 1) {
// No matches found
}
else {
while (cursor.moveToNext()) {
// Use cursor
}
}
}
};
Further details:
Implementing AsyncQueryHandler
http://www.trustydroid.com/blog/2014/10/07/using-asyncqueryhandler-with-content-provider/

Proper implementation of DbOperations in Async Task

In my android application, I have various "entities" such as user defined. I'm using a single DbOperations class that has the default Select, Insert, Update and Delete functionality.
An async task is used as an intermediary. It sits in between my entities and DbOperations class and performs everything asynchronously. Here's an example.
ASYNC CLASS - with Insert Method code
private DbResponse InsertUser() {
ContentValues cntValues = GetCrmUserContentVal();
long result = _dbConn.InsertRecord(cntValues, TABLE_NAME);
DbResponse dbResponse = new DbResponse();
if(result == -1)
{
dbResponse.setStatus(false);
}
else {
dbResponse.setStatus(true);
dbResponse.setID(result);
}
return dbResponse;
}
CRM USER Entity Class - Insert Method
public void InsertintoDb()
{
new CRMUserDbOperations(this,this,DbOperationType.Insert,getCurrentContext()).execute();
}
DbResponse - Return type class is a seperate class -
private Boolean Status;
private String ErrorMessage;
private Cursor Data;
private long ID;
private DbOperationType dbOpType;
In the doBackground process of the async task, I have this switch code -
switch (_DbOpType) { // Enum type.
case Insert:
dbResponse = InsertUser();
break;
case Select:
dbResponse = SelectUser();
break;
case Update:
dbResponse = UpdateUser();
break;
default:
throw new InvalidParameterException(
_Context.getString(R.string.invalid_io));
}
As you can notice this asynchronous task has code for all the various operations I might have to perform on the entity. For other entities, I'll have the same structure as well...
Now my question is, could I be doing this in a better manner?
Yes it can be done in a better way. Let me give you an example of how we are handling it in our current app. You just need 4 AsyncTasks in total for insert, update, delete and select operations. Let me give you an example.
You have an interface and every entity will implement it:
public interface DbOps {
public int delete(String where);
public void insert();
public void update();
public Cursor select();
}
NOTE: Arguments and return types will be of your choice that fits your need but must also fit for every entity class. I am going to use delete() method as an example.
Now you need only one DeleteTask for all entitites:
private static class DeleteTask extends AsyncTask<String, Void, Integer> {
private final DbOps mOps;
public RowDeleteTask(DbOps ops) {
mOps = ops;
}
#Override
protected Integer doInBackground(String... wheres) {
String where = wheres[0];
int rowsDeleted = mOps.delete(where);
return rowsDeleted;
}
}
Fire it like this:
new DeleteTask(mUserEntity).execute("id = 4");
new DeleteTask(mMoviesEntity).execute("name = x-man");
and obviously you will have something similar to this if we take UserEntitiy for example:
public UserEntity implements DbOps{
#Override
public int delete(String where){
return _dbConn.delete(mTable, where, null);
}
.
.
.
}
This isn't product placement or anything, it is open source, I have been working on Async databases for a while now, and have recently created a library for it.
It is hosted on Github at http://fabiancook.github.io/AndroidDbHelper/
It covers a more general need for async database usage, you can either do one thing async if you want, or the whole lot.
It will have an implementation for a entity framework in the coming months as I am working on a Ubuntu touch version at this moment.
Any info needed just ask.
For small amounts of objects entities are great, but when you want to report on them they get really slow, which is even apparent in microsofts entity framework. For the most it is usually a heck of a lot faster (performance wise) to use straight SQL in an async way as it takes out the need for that middle object.
Note, between android 1.6 and 3.0 the AsyncTask class would execute sometimes in parallel, which would cause some problems in any database. So when using those versions you there would have to some differences, this is being worked into my DbHelper :)
http://developer.android.com/reference/android/os/AsyncTask.html#execute(Params...)

AsyncTask that accesses Sqlite database causes crash

I have a ListView which I need to populate using a background thread. The list needs to update as each item is retrieved. Below is a very simplified example of how I implement this.
public class DownloadTask extends AsyncTask <MyUserObject, Integer, String>
{
#Override
protected MyUserObject doInBackground(MyUserObject... myUserObj)
{
MyUserObject muo = null;
int nCount = myUserObj.length;
if( nCount > 0 )
muo = myUserObj[0];
muo.DownloadStuff();
return muo.getUserName();
}
protected void onPostExecute(String userName)
{
adapter.names.add(userName);
adapter.notifyDataSetChanged();
}
}
public class MyAdapterClass extends BaseAdapter
{
private ArrayList<String>names;
public MyAdapterClass(Context context)
{
names = new ArrayList<String>();
}
public fillList()
{
for( int i=0; i<users.length; i++ )
{
DownloadTask task = new DownloadTask();
task.execute(users[i]);
}
}
In the above example, 'adapter' is an object of type MyAdapterClass, and its fillList() method is what launches the threads. Calling notifyDataSetChanged() in onPostExecute() is what updates my ListView as data arrives.
The problem is, that I am accessing my sqlite database in "DownloadStuff()' which is called in 'doInBackground', and having multiple threads accessing the DB causes it to crash. (If I comment out all DB activities in here, then it runs fine). Below is how I try to workaround this problem, however it still crashes. Any advice on how I can have my ListView update as data is retrieved from a background thread?
Semaphore semaphore = new Semaphore(1, true);
public synchronized void DownloadStuff()
{
semaphore.acquire(1);
// ... DB operations ... //
semaphore.release(1);
}
I think your approach is wrong from it's beginning. Why do you want to start separate AsyncTask for each item you have to add to your adapter. Use onProgressUpdate to notify the gui for newly added items in the adapter. In this case you want have concurrent access to your db.
I'm not sure (because I'm really tired) but I think your ot using you synchronysed correctly.
you create a different instance of MyUserObject each time you do a async task, this means you never actually call Downloadstuff on the same instance hence no conflict, but on the other hand your database is unique being called by multiple MyUserObject hence conflict.
what you want to do is have the same instance of muo in all your async task, this way they all call downloadstuff on the same instance and then synchronized will work preventing multiple access.
you also don't need the semaphoe here.
edit:
Mojo Risin answer is also very good, if you can save yourself the trouble by centralizing all you async tasks into one you should(less concurrent threads running around you have the better)

Categories

Resources