I use in my project "ActiveAndroid". I have a model which I retain. here is a model:
#Table(name="Params")
public class Params extends Model {
public Params() {
super();
}
#Expose
#Column(name="height")
#SerializedName("heigth")
public int heigth;
#Expose
#Column(name="weight")
#SerializedName("weight")
public int weight;
}
here all save:
public void success(User user, Response response) {
user.params.save();
user.save();
ActiveAndroid.clearCache();
}
Everything works fine! But if I want to add another field in the model:
#Expose
#Column(name="strong")
#SerializedName("strong")
public int strong;
I get an error:
android.database.sqlite.SQLiteException: table Params has no column named strong (code 1): , while compiling: INSERT INTO Params(Id,weight,height,strong) VALUES (?,?,?,?)
of course I know why this error. Because the table is no such column. That's the question, how to add this column ???
Now that I've tried:
remove programs completely, again to compile, run, and everything works fine! because it creates a new table from this column.
Tried to change the version in the manifest database, but this has not led to success.
I know that with normal use of the database in the method of upgrade you can change the version of the database and restructure table. but how to do it in ORM "ActiveAndroid" ?
There is a thing known as Migration, in Active Android you can do that like this
Add the field in your model (which you already did)
Change the database version the the AndroidManifest.xml's metadata
Write your migration script. Name your script [newDatabaseVersion].sql, and place it in the directory [YourApp’sName]/app/src/main/assets/migrations. In my specific example, I’ll create the file [MyAppName]/app/src/main/assets/migrations/2.sql.
E.G. ALTER TABLE Items ADD COLUMN Priority TEXT;
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) + "";
Well, i am trying to test my database migration. Unfortunatly something looks like wrong.
#RunWith(AndroidJUnit4ClassRunner.class)
public class MigrationTest {
private static final String TEST_DB = "migration-test";
#Rule
public MigrationTestHelper helper;
public MigrationTest() {
helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
AppDatabase.class.getCanonicalName(),
new FrameworkSQLiteOpenHelperFactory());
}
#Test
public void migrateAll() throws IOException {
// Create earliest version of the database.
SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
db.close();
// Open latest version of the database. Room will validate the schema
// once all migrations execute.
AppDatabase appDb = Room.databaseBuilder(
InstrumentationRegistry.getInstrumentation().getTargetContext(),
AppDatabase.class,
TEST_DB)
.addMigrations(ALL_MIGRATIONS).build();
appDb.getOpenHelper().getWritableDatabase();
appDb.close();
}
// Array of all migrations
private static final Migration[] ALL_MIGRATIONS = new Migration[]{MIGRATION_1_2};
}
Migration code.
public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE mytable ADD COLUMN reference_code TEXT");
}
};
All is working fine with real migration but in junit test case i have the following error.
E/SQLiteLog: (1) duplicate column name: reference_code
E/TestRunner: failed: migrateAll(com.apps.MigrationTest)
E/TestRunner: ----- begin exception -----
E/TestRunner: android.database.sqlite.SQLiteException: duplicate column name: reference_code (code 1 SQLITE_ERROR): , while compiling: ALTER TABLE mytable ADD COLUMN reference_code TEXT
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:986)
at a
As i understand, it looks like SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1); is creating the schema V2 of my database (and not the version 1).
As a result the new column is taggued as duplicate.
To fix it i have to rollback my version = 1 to #Database class and then start my junit test again.
Can anyone help me on it ?
I follow the google guide here : https://developer.android.com/training/data-storage/room/migrating-db-versions.html
Well, i finally found it. It looks like a wrong schema has been generated in my assets folder.
To fix the issue, here is what i did.
Delete 1.json and 2.json files from the assets folder (each file contains the structure of the database version)
Rollback to the version 1 database (in my code), build > make projet
You will see the 1.json in your assets folder
Made my changes, i mean adding my new column in my Table.java file
Build > make projet
You will see the 2.json in your assets folder
Run the junit test, working now
Here is the difference bewteen my java object and database version 1 et 2
#Entity(tableName = "Table")
public class Table implements Parcelable {
#ColumnInfo(name = COLUMN_REFERENCE_CODE)
private String referenceCode;
}
Hope this will help.
I am relatively new to Android Development and using its Room persistence library. The problem I am currently facing is the following error:
error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: s_abb)
However my table schema (that this column is being referenced by) does contain this column by this name. Here is how I defined my entity in Android
#Entity
public class stops {
#PrimaryKey
#NonNull
#ColumnInfo(name = "s_name")
private String s_name;
#Ignore
#ColumnInfo(name = "s_abb")
private String s_abb;
#Ignore
#ColumnInfo(name = "Comments")
private String Comments;
public String getS_abb() {
return s_abb;
}
public void setS_abb(String s_abb) {
this.s_abb = s_abb;
}
public String getS_name() {
return s_name;
}
public void setS_name(String s_name) {
this.s_name = s_name;
}
public String getComments() {
return Comments;
}
public void setComments(String comments) {
Comments = comments;
}
}
I have tested the query in SQLite Studio and it does return expected data. Here is a screen shot of query written within DAO Interface: Query. I personally think the main problem is that Room may not recognize the aliases I am using with my subqueries and the column names. Am I correct in thinking this? I hope my screenshot helps. I did make sure to add proper spacing between SQL statements, as many solutions here have pointed out. If any of you need me to provide more information, I am happy to oblige! Thank you
As Vladimir Gladun pointed out, the column s_abb that I was querying for was set with an #Ignore annotation over it. Which as Android's documentation on #Ignore annotations states that "Ignores the marked element from Room's processing logic":
https://developer.android.com/reference/android/arch/persistence/room/Ignore.
Which basically means Room disregards it completely.
However this was not the only problem, My method was expecting Entity type values
whereas the SELECT statement from my outermost query was returning String type values. Fixing those two errors solved my problem.
Let's say I have a Patient entity, storing the patient ID, a boolean and finally a Person object. So I annotate these fields with #ColumnInfo to store in the database.
Now a Person has 2 String fields: a first name and last name.
However, in my patients table, I want to have a column directly for the first name and last name fields (from Person), and so I want to be able to call e.g. firstName (and not having to call Person.firstName) from a query. How may I achieve this?
You can use #Embedded annotation of Room for it.
In your case it will be as follows
public class Person {
String firstName;
String lastName;
}
public class Patient {
int patientId;//just an assumption
#Embedded
Person person;
}
For more information check this
Note : I haven't provided other annotations like #ColumnInfo for brevity
I updated my AbstractFooEntity class by adding an integer field like below, and I bumped the DB version (the DB is initialized with new DatabaseSource(context, Models.DEFAULT, DB_VERSION).
#Entity
abstract class AbstractFooEntity {
// this was in DB schema v1
String someField;
// added in DB schema v2
int newField = 0;
}
When I deploy this code and the (automatic) DB migration is performed when the user runs the new version of the Android app, I get the following error at runtime: "Cannot add a NOT NULL column with default value NULL".
What's the proper way to annotate the entity so that the framework correctly handles the automatic DB migration in this scenario?
I found a solution of this.
database.execSQL("ALTER TABLE table_name ADD COLUMN colum_name INTEGER DEFAULT 1 not null");
add the command : "DEFAUT value" after data type will solve your problem.
There are two options, first one is probably to be preferred - in the second one, you need to handle possible nullpointers in the code:
option 1
#Entity
abstract class AbstractFooEntity {
...
#Column(value = "0")
int newField;
}
option 2
#Entity
abstract class AbstractFooEntity {
...
Integer newField;
}