Auto delete nested objects in ORMLite - android

I have these to classes:
public class Station {
#DatabaseField(foreign = true, foreignAutoCreate = true)
private OpeningTimes openingTimes;
}
public class OpeningTimes {
#DatabaseField(generatedId = true)
int _id;
}
Now OpeningTimes row is auto created, when I call createOrUpdate method on StationDao. That's great!
I would be also thankful, if I could delete Station object and its nested object OpeningTimes automatically.
Now I have to do it this way in Station class and it seems quite confusing. Is there any more elegant way?
public void deleteFromDb(DatabaseHelper dbHelper) {
try {
openingTimes.deleteFromDb(dbHelper);
dbHelper.getStationDao().delete(this);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
EDIT:
I have been trying also this, but with SQL Statement errors
#DatabaseField(foreign = true, foreignAutoCreate = true, columnDefinition="INTEGER, FOREIGN KEY(`openingTimes_id`) REFERENCES openingtimes(`_id`)")

I would consider doing this at the DAO level instead of at the persisted object level. What I recommend is creating your own StationDao interface and your own StationDaoImpl implementation. The ORMLite docs an example of this.
public interface StationDao extends Dao<Station, Integer> {
// we will just be overriding some of the delete methods
}
Then create your implementation which would override the delete() method and delete any children objects. Something like the following:
public class StationDaoImpl extends BaseDaoImpl<Station, Integer>
implements StationDao {
private final Dao<OpeningTimes, Integer> openTimesDao;
public AccountDaoImpl(ConnectionSource connectionSource) throws SQLException {
super(connectionSource, Station.class);
openTimesDao = DaoManager.createDao(connectionSource, OpeningTimes.class);
}
#Override
public int delete(Station station) throws SQLException {
if (station.openTimes != null) {
openTimesDao.delete(station.openTimes);
}
return super.delete(station);
}
}
If you are using your own DAO then you would have to make sure it is configured using #DatabaseTable(daoClass = StationDaoImpl.class).

Related

Android DBFlow does not save objects

I have a database like this:
#Database(name = QuestionDatabase.NAME, version = QuestionDatabase.VERSION)
public class QuestionDatabase {
public static final String NAME = "QuestionDatabase"; // we will add the .db extension
public static final int VERSION = 1;
}
and a table like this:
#Table(database = QuestionDatabase.class)
public class Question extends BaseModel {
#PrimaryKey
public int localID;
#Column
public int Id;
#Column
public String Answer;
#Column
public String ImageURL;
#Column
public boolean IsFavorite;
#Column
public boolean IsSolved;
}
and an asynctask to retrive data from server:
public class QuestionRetriever extends AsyncTask<Integer, Void, Integer> {
private Activity callerActivity;
private QuestionAdapter questionsAdapter;
private List<Question> callerQuestions;
private Integer pageSize = 10;
public QuestionRetriever(Activity callerActivity, QuestionAdapter questionsAdapter, List<Question> questions){
this.callerActivity = callerActivity;
this.questionsAdapter = questionsAdapter;
this.callerQuestions = questions;
}
#Override
protected Integer doInBackground(Integer... pageNumbers) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.1.33:313/")
.addConverterFactory(GsonConverterFactory.create())
.build();
QuestionWebService service = retrofit.create(QuestionWebService.class);
Call<List<Question>> call = service.getQuestionsPaged(pageNumbers[0].toString(), pageSize.toString());
try {
Response<List<Question>> excecuted = call.execute();
List<Question> questions = excecuted.body();
FastStoreModelTransaction
.insertBuilder(FlowManager.getModelAdapter(Question.class))
.addAll(questions)
.build();
callerQuestions.addAll(questions);
callerActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
questionsAdapter.notifyDataSetChanged();
}
});
//Get TotalQuestionCount if not yet
if (((StatefulApplication) callerActivity.getApplication()).getQuestionCount() == -1){
Call<Integer> call2 = service.getQuestionsSize();
try {
((StatefulApplication) callerActivity.getApplication()).setQuestionCount(call2.execute().body());
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
catch (Exception e){
e.printStackTrace();
}
return 1;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
//TODO: show loader
}
#Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
//TODO: hide loader
}
}
as you see every thing seems ok and eve after running FastStoreModelTransaction nothing wrong happens. no errors.
the init job is done in splash screen activity like this:
private void initializeEveryRun() {
//Initializing DBFlow
//DBFlow needs an instance of Context in order to use it for a few features such as reading from assets, content observing, and generating ContentProvider.
//Initialize in your Application subclass. You can also initialize it from other Context but we always grab the Application Context (this is done only once).
FlowManager.init(new FlowConfig.Builder(getApplicationContext()).build());
}
any idea about what should cause this problem or any solution to try?
TG.
I found the answer!!!
As you see in the model, the Id is the identifier of the object retrieved from server and LocalId is the auto-increment identifier that is stored locally. This was the problem. I've used the Id field as Primary Key and added a field named OnlineId for server side identifer and everything is ok now.
Is this a bug or I was using that wrong?
TG.
This is not execute transaction, it's just transaction creation.
As you can see it this test DBFlow - FastModelTest.kt.
FastStoreModelTransaction
.insertBuilder(FlowManager.getModelAdapter(Question.class))
.addAll(questions)
.build();
You must execute your transaction like this :
FlowManager.getDatabase(QuestionDatabase.class).executeTransaction(<<YourTransaction>>);
Otherwise, if you already had a DatabaseWrapper instance you can do <<YourTransaction>>.excute(<<YourDatabaseWrapper>>);.

ORMLite and custom data persiter of Optional<Double>

Anyone know how to write custom data persister for Guava's Optional<Double>?
So it could be possible to directly use in entity the code:
#DatabaseField(columnName = "myField")
Optional<Double> myField;
After initial attemps I found a few tricky points. Eg: registering Optional<Double> in mapper - seems that types dictionary flattens it to just Optional.
Here is my implementation which ONLY persit to / read from DB.
And DOESN'T handle: arguments in statements, global type registering.
Before use it's worth to read why not to use Optional as object's fied.
Use case:
#DatabaseField(columnName = "myField", persisterClass = OptionalDoubleType.class)
Optional<Double> myField;
Persister:
public class OptionalDoubleType extends BaseDataType {
private static final OptionalDoubleType singleton = new OptionalDoubleType();
public static OptionalDoubleType getSingleton() {
return singleton;
}
private OptionalDoubleType() {
super(SqlType.DOUBLE, null);
}
protected OptionalDoubleType(SqlType sqlType, Class<?>[] classes) {
super(sqlType, classes);
}
#Override
public Object resultToJava(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
double aDouble = results.getDouble(columnPos);
if (results.wasNull(columnPos))
return Optional.absent();
else
return Optional.of(aDouble);
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) throws SQLException {
Optional<Double> optDbl = (Optional<Double>) javaObject;
if (optDbl.isPresent())
return optDbl.get();
else
return null;
}
#Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) throws SQLException {
throw new UnsupportedOperationException();
}
#Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
throw new UnsupportedOperationException();
}
// BUGFIX: there is a bug in ORMLite which causes that
// decoding 'sql null' to Optional.absent() is wrong when
// Entity with Optional<Double> is read as "child entity".
// It fixes the bug [ugly but works ;]
#Override
public boolean isStreamType() {
return true;
}
}

Do ORMLite persisters work in Android?

Do custom persisters work on Android? I was trying to write one for an entity, and was having no luck in having it run when the entity gets written by the DAO. So, I tried to use the "MyDatePersister" from the examples and I am not able to get that working either.
The persister is nearly identical to the example one -> https://github.com/j256/ormlite-jdbc/blob/master/src/test/java/com/j256/ormlite/examples/datapersister/MyDatePersister.java
In my entity, I have
#DatabaseTable
public class ClickCount implements Serializable {
// other declarations
#DatabaseField(columnName = DATE_FIELD_NAME, persisterClass = MyDatePersister.class)
private Date lastClickDate;
// more code
}
Here is a link to the whole project in Bitbucket -> https://bitbucket.org/adstro/android-sandbox. It's basically one of the ORMLite Android examples with the custom persister example added.
Any feedback would be greatly appreciated.
Thanks
First off, what is the error result you're getting?
I got my custom persister to work just fine, though I didn't try to extend the DateType. Below is a JSONArrayPersister I found the need for. The confusing part is in the naming of the methods, but once they're setup properly, it should be ok.
package com.example.acme.persister;
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.field.SqlType;
import com.j256.ormlite.field.types.BaseDataType;
import com.j256.ormlite.support.DatabaseResults;
import org.json.JSONArray;
import org.json.JSONException;
import java.sql.SQLException;
public class JSONArrayPersister extends BaseDataType {
public static int DEFAULT_WIDTH = 1024;
private static final JSONArrayPersister singleTon = new JSONArrayPersister();
public static JSONArrayPersister getSingleton() {
return singleTon;
}
private JSONArrayPersister() {
super(SqlType.STRING, new Class<?>[] { String.class });
}
protected JSONArrayPersister(SqlType sqlType, Class<?>[] classes) {
super(sqlType, classes);
}
#Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) {
try {
return new JSONArray(defaultStr);
} catch (JSONException ex)
{
return new JSONArray();
}
}
#Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
try {
return new JSONArray( results.getString(columnPos) );
} catch (JSONException ex)
{
return new JSONArray();
}
}
#Override
public Object resultStringToJava(FieldType fieldType, String stringValue, int columnPos) throws SQLException {
return parseDefaultString(fieldType, stringValue);
}
#Override
public int getDefaultWidth() {
return DEFAULT_WIDTH;
}
}
Then in your entity:
#DatabaseField(persisterClass = JSONArrayPersister.class)
private JSONArray something;

ORMLite many to many relation Android

I have two classes :
UniteStratigraphique.java :
#DatabaseTable(tableName = "unitestratigraphique")
public class UniteStratigraphique {
public final static String ID_FIELD_NAME = "id";
#DatabaseField(generatedId = true, columnName = ID_FIELD_NAME)
private int id;
// CAMPAGNES
#DatabaseField(foreign = true, foreignAutoRefresh = true)
private Campagne campagne;
#ForeignCollectionField
private ForeignCollection<Campagne> listeCampagnes;
public UniteStratigraphique() {}
public Campagne getCampagne() {
return campagne;
}
public void setCampagne(Campagne campagne) {
this.campagne = campagne;
}
public ArrayList<Campagne> getListeCampagnes() {
ArrayList<Campagne> campagnesArray = new ArrayList<Campagne>();
for (Campagne campagne : listeCampagnes) {
campagnesArray.add(campagne);
}
return campagnesArray;
}
public ForeignCollection<Campagne> getListeCampagnesForeign() {
return listeCampagnes;
}
public void setListeCampagnes(ForeignCollection<Campagne> listeCampagnes) {
this.listeCampagnes = listeCampagnes;
}
}
Campagne.java :
#DatabaseTable(tableName = "campagne")
public class Campagne {
#DatabaseField(generatedId = true)
private int id;
// UNITE STRATIGRAPHIQUE
#ForeignCollectionField
private ForeignCollection<UniteStratigraphique> listeUniteStratigraphique;
#DatabaseField(foreign = true, foreignAutoRefresh = true)
private UniteStratigraphique uniteStratigraphique;
public Campagne() {}
public ArrayList<UniteStratigraphique> getListeUniteStratigraphique() {
ArrayList<UniteStratigraphique> usArray = new ArrayList<UniteStratigraphique>();
for (UniteStratigraphique us : listeUniteStratigraphique){
usArray.add(us);
}
return usArray;
}
public ForeignCollection<UniteStratigraphique> getListeUniteStratigraphiqueForeign() {
return listeUniteStratigraphique;
}
public void setListeUniteStratigraphique(
ForeignCollection<UniteStratigraphique> listeUniteStratigraphique) {
this.listeUniteStratigraphique = listeUniteStratigraphique;
}
public int getSizeListeUniteStratigraphique() {
return listeUniteStratigraphique.size();
}
public UniteStratigraphique getUniteStratigraphique() {
return uniteStratigraphique;
}
public void setUniteStratigraphique(UniteStratigraphique uniteStratigraphique) {
this.uniteStratigraphique = uniteStratigraphique;
}
}
As you can see, these are Many-To-Many linked (0...n---0...n, with ORMLite annotations).
Now, my workflow is :
I create multiple "UniteStratigraphique" classes and I store them into my database (this works fine).
=> So I have n * "UniteStratigraphique" stored.
After that what I want is to create a "Campagne" class wich will contain multiple "UniteStratigraphique" classes.
=> So I want to set this field from "Campagne.java" :
#ForeignCollectionField
private ForeignCollection<UniteStratigraphique> listeUniteStratigraphique;
with the n * "UniteStratigraphique" elements I just stored before.
I tried to do this with this DAO method but it only duplicate the "UniteStratigraphique" classes into my db and no link is made..
public void addUsToCampagne(Campagne campagne,
ArrayList<UniteStratigraphique> usArray) {
ForeignCollection<UniteStratigraphique> usForeign = campagne
.getListeUniteStratigraphiqueForeign();
if (usForeign == null) {
try {
usForeign = getHelper().getCampagneDao()
.getEmptyForeignCollection("listeUniteStratigraphique");
for (UniteStratigraphique us : usArray) {
usForeign.add(us);
}
} catch (SQLException e) {
e.printStackTrace();
}
}else{
for (UniteStratigraphique us : usArray) {
usForeign.add(us);
}
}
}
And in my Activity I'm doing this :
db.addCampagne(campagne);
if( myUniteStratigraphiqueArray.size() > 0){
db.addUsToCampagne(campagne, myUniteStratigraphiqueArray);
}
Many to Many relations are non automatic with ORMLite, the only way to achieve it is to make a 3rd Table only for link beetween these 2 classes..
This link refers to this problem : What is the best way to implement many-to-many relationships using ORMLite?
And the example here : https://github.com/j256/ormlite-jdbc/tree/master/src/test/java/com/j256/ormlite/examples/manytomany
Hope it helped.

Hiding DataBaseHelper & DAOs from my Activity

I want to call CRUD operations on Order objects in my Activity. I was wondered is the following implementation of a "Service" class a good way to do this? I don't want any reference to DatabaseHelper or DAO objects in my Activity code as I don't think this would be desireable.
Here is my Service class
public class OrderService
{
private static OrderService instance;
private static Dao<Order, Integer> orderDAO;
static public void init(Context ctx) {
if (null == instance) {
instance = new OrderService(ctx);
}
}
public static OrderService getInstance() {
return instance;
}
private OrderService(Context ctx) {
DatabaseHelper helper = DatabaseHelper.getInstance(ctx);
helper.getWritableDatabase();
orderDAO = helper.getOrderDao();
}
public Order getOrderWithId(int orderId) {
Order myOrder = null;
try {
myOrder = orderDAO.queryForId(orderId);
} catch (SQLException e) {
e.printStackTrace();
}
return myOrder;
}
public Order neworder(Order order) {
try {
orderDAO.create(order);
} catch (SQLException e) {
e.printStackTrace();
}
return order;
}
public void deleteorder(Order order) {
try {
orderDAO.delete(order);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void updateorder(Order order) {
try {
orderDAO.update(order);
} catch (SQLException e) {
e.printStackTrace();
}
}
public List <Order> getordersForCategory(int orderId) {
List <Order> orders = null;
try {
orders = orderDAO.queryForAll();
} catch (SQLException e) {
e.printStackTrace();
}
return orders;
}
}
and here is how I intend to use the service
public class OrderProcessingActivity extends Activity {
int orderID;
private Order order;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myview);
order = OrderService.getInstance().getOrderWithId(orderID);
......
Does this look like a good way to access the SQLlite DB ?
I have read about "Service" implementations that can be configured in Android so I was sondered is this something that I should be using instead?
Despite moving your database logic to a different class, you're doing all of your database operations in the UI thread, which is not ideal. Also note that even though your class is called "service" it doesn't inherit from any of the Service classes in Android.
One alternate approach would be to do your database operations from the doInBackground method of an AsyncTask, return your needed data from that method. Then, use the returned data to update your activity in the onPostExecute method.
This is more or less the approach I take. My application architecture typically looks like this:
Activity <--> Service <--> DAO <--> SQLite
This looks pretty close to what you have, so I'd say it looks good! I normally don't implement it as a singleton, however, as I don't like to keep the same Context around for the entire lifetime of the app. Instead, I pass in the Context to create a service from each Activity.

Categories

Resources