Is there an elegant way to add a batch of new objects from JSON, taking into consideration that the new bunch might contain values that already in DB and that DB must contain only unique values?
Why not using the same id in the JSON object?, check that a unique id is being sent from the server and prepare a method that checks out for the id if it exists.
//Check if item exists already with id
public boolean checkIfExists(String id){
RealmQuery<Data> query = realm.where(Data.class)
.equalTo("id", id);
return query.count() != 0;
}
Related
I have this OColumn partner_name = new OColumn("Partner", OVarchar.class).setLocalColumn(); in my sale order model class with odoo functional method that depends on partner_id column. I would like to search the partner_name in my list using that column partner_name, but I'm a little confused on how to achieve this. Please needed some help.
This is what I've tried:
BaseFragment
#Override
public void onViewBind(View view, Cursor cursor, ODataRow row) {
getPartnerIds(row);
OControls.setText(view, R.id.partner_name, row.getString("partner_name")); // displays false
....
}
}
private void getPartnerIds(ODataRow row){
OValues oValues = new OValues();
oValues.put("partner_id", row.get("partner_id"));
saleOrder.storeManyToOne(oValues);
}
updated:
I noticed that even though I created
#Odoo.Functional(method = "storeManyToOne", store = true, depends = {"partner_id"})
OColumn partner_name = new OColumn("Partner", OVarchar.class).setLocalColumn();
no column was created.
Updated:
partner_name column with odoo functional
Edit: Just place the 'if (type.isAssignableFrom(Odoo.Functional.class)'
before the 'if (type.getDeclaringClass().isAssignableFrom(Odoo.api.class))' to have the correct values.
Define the partner_name field like below:
#Odoo.Functional(method="storePartnerName", store=true, depends={"partner_id"})
OColumn partner_name = new OColumn("Partner name", OVarchar.class)
.setLocalColumn();
public String storePartnerName(OValues values) {
try {
if (!values.getString("partner_id").equals("false")) {
JSONArray partner_id = new JSONArray(values.getString("partner_id"));
return partner_id.getString(1);
}
} catch (Exception e) {
e.printStackTrace();
}
return "false";
}
You can simply get the partner_name using:
row.getString("partner_name")
EDIT:
Note that database is created when you first time run your application, or when you clean your data from app
setting. You need to clean application data everytime when you update your database column.
If the column was added after the database creation, it will not be added to the corresponding table. This is because the database is not upgraded. To fix this issue you can:
Clean application data to update your database column
Remove user account (This will delete database) or reinstall the application to recreate the database.
Or you can change DATABASE_VERSION in odoo/datas/OConstants then override onModelUpgrade method in sale order model and upgrade the table manually (alter sale order table and add the partner name column using SQL query: ALTER TABLE sale_order ADD partner_name VARCHAR(100)).
When a new sale order is created and synchronized, the partner name should be computed and stored automaticaly.
I noticed that the partner name was not set for existing records after synchrinization so I added another SQL query to compute and set the value of partner name for old records.
Example:
#Override
public void onModelUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("ALTER TABLE sale_order ADD partner_name VARCHAR(100)");
db.execSQL("UPDATE sale_order SET partner_name = (SELECT name from res_partner WHERE _id=partner_id) WHERE partner_name IS NULL AND partner_id IS NOT NULL");
}
Edit (config):
using the new configuration you will get the following error (which will prevent creating fields using annotations):
W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Class.isAssignableFrom(java.lang.Class)' on a null object reference
W/System.err: at com.odoo.core.orm.OModel.compatibleField(OModel.java:349)
CODE:
if (type.getDeclaringClass().isAssignableFrom(Odoo.api.class)) {
Try to remove .getDeclaringClass()
Edit: not all partner names are shown
There is a org.json.JSONException error that happens when it try to convert partner_id string to a JSON array.
W/System.err: org.json.JSONException: Unterminated array at character 12 of [114.0, UH PARTNER]
The error happens when it try to convert names containing spaces. To avoid that you can cast partner_id string to a list of objects.
In partnerName method, replace the following code:
JSONArray partner_id = new JSONArray(values.getString("partner_id"));
return partner_id.getString(1);
With:
List<Object> partner_id = (ArrayList<Object>) values.get("partner_id");
return partner_id.get(1) + "";
I'm developing an android app that uses SQLite as the local database. The app syncs data obtained from a web api and stores it in the local database. All the model classes have their ID property set as Primary key and Auto incremented so I can manually enter data without having to specify the ID. The issue is when I insert the data from the API into the SQlite, the ID of the object is ignored and Sqlite gives the object a new ID. I want the data stored with the same ID as the object being stored.
The web api returns the object lists that have their ID type long however the SQLite objects have their primary keys as int. Is this the reason why the ID values is not getting stored because their data types don't match? I can't change the datatype in my SQL database where the data comes from as there are hundreds of tables in it. Is there a way around it?
This is the Code to inserts or updates data in my local DB:
}
public async Task<string> insertUpdateVideoData(Video_Struct data)
{
try
{
var db = new SQLiteAsyncConnection(dbPath);
var m = GetVideos();
if (await db.FindAsync<Video_Struct>(f => f.VideoID == data.VideoID) != null)
{
await db.UpdateAsync(data);
}
else
{
if (await db.InsertAsync(data) != 0)
{
await db.UpdateAsync(data);
}
}
return "Single data file inserted or updated";
}
catch (SQLiteException ex)
{
return ex.Message;
}
}
This is the code to get data objects from the API:
public async Task<List<Video_Struct>> GetVideoData()
{
List<Video_Struct> vids = new List<Video_Struct>();
WebClient mClient = new WebClient();
var output = await mClient.DownloadDataTaskAsync(new Uri(GlobalVariables.host + "/api/media/getmedia"));
var json = Encoding.UTF8.GetString(output);
vids = JsonConvert.DeserializeObject<List<Video_Struct>>(json);
return vids;
}
If your local DB is a cache for web data and external DB gives you unique IDs, don't use auto increment in scheme, just re-use external IDs.
Actually, you can have a complex (compound) primary key, it depends on data unique properties.
If you do not work with your data as structured set you can try gson+SharedPreferences. Just don't forget to override equals and hashcode for your data models.
Datatype in not an issue, because sqlite uses INTEGER type.
I am using Ormlite and I have an object that has a foreign field.
The said object also has getters and setters for it's fields.
public class Object {
#DatabaseField(foreign = true)
private Object2 foreignField;
Object() {
}
public Object2 getForeignField(){ return foreignField; }
public void setForeignField(Object2 foreignField){
this.foreignField = foreignField;
}
}
So I assumed that when I call :
Object testObject;
Object2 testObject2;
testObject.setForeignField(testObject2);
getDao.update(testObject);
It will automatically update the testObject in the database with the new foreignfield's id, but my table is not updating.
What am I doing wrong?
EDIT :
By reading the actual documentation on Ormlite, the update(Object) method will not update any foreign objects or foreign collections.
http://ormlite.com/javadoc/ormlite-core/com/j256/ormlite/dao/Dao.html#update(T)
NOTE: This will not save changes made to foreign objects or to foreign collections.
And now because of that, how do I update a foreign object on a table?
It will automatically update the testObject in the database with the new foreignfield's id, but my table is not updating.
It certainly should be.
The update(...) call doesn't compare the object with the database so a SQL UPDATE statement will be made. Have you tried turning on ORMLite logging?
If it is updating the Object with the same id from the Object2 foreignField then maybe that id is the same? When you call testObject.setForeignField(testObject2), the testObject2 should already have been created in the Object2 table. It needs to have an appropriate id field which is was gets stored in the Object table.
If you provide more details about the id field values and what the database has before and after the update, I may be able to help more.
I'm looking for a way to access a newly created local ParseObject which hasn't yet synced to the Parse cloud server. Since there is no objectId value there's no way to query for the objectId through the local datastore and it appears the localId (which looks like it creates a unique identifier locally) is locked down (otherwise this would be a non-issue as I could use my Content Provider to take care of the details). Since the ParseObject class isn't Serializable of Parcelable I can't pass it through an Intent. To note the complexity of my task I have I have 3 levels of ParseObjects (ParseObject > Array[ParseObjects] > Array[ParseObjects]). Essentially I'm looking to see if Parse has full offline capabilities.
TL:DR
Basically I want to be able to access a single ParseObject in a different Activity as soon as it's created. Does this problem have a practical application with Parse and ParseObjects or am I going to have to implement some serious work arounds?
I believe ParseObjects are serializable, so put them into a Bundle and then put that Bundle into an Intent
in the current activity
Intent mIntent = new Intent(currentActivityReference, DestinationActivity.class);
Bundle mBundle = new Bundle();
mBundle.putSerializable("object", mParseObject);
mIntent.putExtras(mBundle);
startActivity(mIntent);
in the destination activity
retrieve the intent with getIntent().getExtras(), which is a Bundle object, so there is a getter for the serializable .getSerializable("object") but you will have to cast it to (ParseObject)
So I was able to keep everything within the confines of the structures I already have in place to take care of this problem (a sync adapter and the Parse API). Basically all I had to do was leverage Parse's existing "setObjectId" function.
NOTE: This only works with an existing Content Provider / SQLiteDatabase
I created a temporary unique ID for the new ParseObject to be stored locally. This unique value is based off of the max index number in the Content Provider I'm storing my objects (for my Sync Adapter).
//query to get the max ID from the Content Provider (used with the sync adapter)
Cursor cursor = context.getContentResolver().query(
WorkoutContract.Entry.CONTENT_URI,
new String[]{"MAX(" + WorkoutContract.Entry._ID + ")"},
null, null, null
);
long idx = 1; //default max index if there are no records
if (cursor.moveToFirst())
idx = cursor.getInt(0) + 1;
final long maxIndex = idx;
cursor.close();
//this is the temporary ID used for storing, a String constant prepended to the max index
String localID = WorkoutContract.LOCAL_WORKOUT_ID + maxIndex;
I then used the pin() method to store this ParseObject locally and then made an insert into the Content Provider to not only keep the ID in the table to iterate the max index in the table.
//need to insert a dummy value into the Content Provider so the max _ID iterates
ContentValues workoutValues = new ContentValues();
//the COLUMN_WORKOUT_ID constant refers to the column which holds the ParseObjects ID
workoutValues.put(WorkoutContract.Entry.COLUMN_WORKOUT_ID, localID);
context.getContentResolver().insert(
WorkoutContract.Entry.CONTENT_URI,
workoutValues);
Then I created another dummy ParseObject with all the same attributes as the one with the local ID (without the local ID). This ParseObject was then saved to the server via the saveEventually() function. (Note: This will create 2 local copies or your ParseObject. To leave the blank copy out of queries simply leave out ParseObjects with null object IDs).
query.whereNotEqualTo("objectId", null);
In the saveEventually() function there needs to be a callback which replaces the old (local) ParseObject as well as the localID value in the Content provider. In the SaveCallback object replace the server returned ParseObject's attributes with the local ones (to account for any changes made during the server query). Below is the full code for the SaveCallback where the tempObject is the one sent to the Parse server:
tempObject.saveEventually(new SaveCallback() {
//changes the local ParseObject's ID to the newly generated one
#Override
public void done(ParseException e) {
if (e == null) {
try {
//replaces the old ParseObject
tempObject.put(Workout.PARSE_FIELD_NAME, newWorkout.get(Workout.PARSE_FIELD_NAME));
tempObject.put(Workout.PARSE_FIELD_OWNER, ParseUser.getCurrentUser());
tempObject.put(Workout.PARSE_FIELD_DESCRIPTION, newWorkout.get(Workout.PARSE_FIELD_DESCRIPTION));
tempObject.pin();
newWorkout.unpinInBackground(new DeleteCallback() {
#Override
public void done(ParseException e) {
Log.i(TAG, "Object unpinned");
}
});
} catch (ParseException e1) {
e1.printStackTrace();
}
//update to content provider with the new ID
ContentValues mUpdateValues = new ContentValues();
String mSelectionClause = WorkoutContract.Entry._ID + "= ?";
String[] mSelectionArgs = {Long.toString(maxIndex)};
mUpdateValues.put(WorkoutContract.Entry.COLUMN_WORKOUT_ID, tempObject.getObjectId());
mUpdateValues.put(WorkoutContract.Entry.COLUMN_UPDATED, tempObject.getUpdatedAt().getTime());
context.getContentResolver().update(
WorkoutContract.Entry.CONTENT_URI,
mUpdateValues,
mSelectionClause,
mSelectionArgs
);
}
}
});
To get the local ParseObject in another Activity just pass the local objectId in an Intent and load it. However, the index of the ParseObject on the Content Provider needs to be passed as well (or it can be retrieved from the unique local ID) so if the ParseObject is ever retrieved again you can check the Content Provider for the updated Object ID and query the correct ParseObject.
This could use a bit of refinement but for now it works.
in my app i have two edit boxes for email and username. Whatever the user types in it i am trying to move it over an url as follows
http//xxxxxxx.com/id?mail=*email&user=*usernane
By this i am getting a return data from the url, this is what i am doing if network is available. But if network is not available i am storing those two values in Sqlite database and in another activity if network is available i will be fetching the above said data and i will move them to the server.
My problem is, at the time of network not available if the user tries to send two set of username and email to the server it gets stored in database. How can i store those values in an array and how can i fetch them one by one. Please help me friends
Following is the part of my code for database
off = openOrCreateDatabase("Offline.db", SQLiteDatabase.CREATE_IF_NECESSARY, null);
off.setVersion(1);
off.setLocale(Locale.getDefault());
off.setLockingEnabled(true);
final String CREATE_TABLE_OFFLINEDATA ="CREATE TABLE IF NOT EXISTS offlinedata(spotid INTEGER, username TEXT, email TEXT);";
off.execSQL(CREATE_TABLE_OFFLINEDATA);
ContentValues values = new ContentValues();
values.put("id", millis);
values.put("name", username);
values.put("mail", email);
off.insert("offlinedata", null, values);
Cursor con = off.rawQuery("select * from offlinedata" , null);
if (con != null )
{
if (con.moveToFirst())
{
do
{
int spotid = con.getInt(con.getColumnIndex("id"));
String first = con.getString(con.getColumnIndex("username"));
String middle = con.getString(con.getColumnIndex("email"));
}
while (con.moveToNext());
}
}
off.close();
Please help me friends....
From looking at your sample code, it seems like you're storing them properly(ish), and you've managed an exhaustive job fetching them in a really narrow scope, so make first and middle more globalish and since you have two strings available, put them in an array.
Though I must say if this is your actual code it probably won't work the way you want this whole offline thing to work.