I did the search about "cascade delete" operation for the Realm. Sadly that feature has not been implemented yet. I made my own implementation of it and shared it here.
How to make generic code for the Realm "cascade delete" operation ?
1) Copy this code to your project
import android.util.Log;
import java.lang.reflect.Method;
import io.realm.RealmList;
import io.realm.RealmObject;
import com.company.project.models.IRealmCascade;
/**
*/
public class RealmUtils
{
public static void deleteCascade( RealmObject dataObject )
{
if (dataObject == null)
{
return;
}
if( IRealmCascade.class.isAssignableFrom( dataObject.getClass() ) )
{
for( Method method : dataObject.getClass().getSuperclass().getDeclaredMethods() )
{
try {
//Ignore generated methods
if( (method.getName().contains("realmGet$")) || (method.getName().contains("access$super")) )
{
continue;
}
Class<?> resultType = method.getReturnType();
//Ignore non object members
if (resultType.isPrimitive()) {
continue;
}
if (RealmObject.class.isAssignableFrom(resultType)) {
//Delete Realm object
try {
RealmObject childObject = (RealmObject) method.invoke(dataObject);
RealmUtils.deleteCascade(childObject);
} catch (Exception ex) {
Log.e("REALM", "CASCADE DELETE OBJECT: " + ex.toString());
}
} else if (RealmList.class.isAssignableFrom(resultType)) {
//Delete RealmList items
try {
RealmList childList = (RealmList) method.invoke(dataObject);
while( childList.iterator().hasNext() )
{
RealmObject listItem = (RealmObject)childList.iterator().next();
RealmUtils.deleteCascade(listItem);
}
} catch (Exception ex) {
Log.e("REALM", "CASCADE DELETE LIST: " + ex.toString());
}
}
}
catch (Exception ex)
{
Log.e("REALM", "CASCADE DELETE ITERATION: " + ex.toString());
}
}
}
dataObject.deleteFromRealm();
}
}
2) Add interface to your project. If your Realm object implement this interface all child objects will be deleted after call deleteCascade. If interface not implemented this function delete Realm object but don't delete child objects.
public interface IRealmCascade {
}
3) Declare your Realm object. Example below.
public class NodeModel extends RealmObject implements IRITSerializable, IRealmCascade {
#PrimaryKey
#SerializedName("id") private String objId;
#SerializedName("parentId") private String parentId;
#SerializedName("contentType") private String nodeType;
#Required
#SerializedName("name") private String name;
#SerializedName("settings") private RealmList<ValueTypeModel> columns;
public String getObjId() {
return objId;
}
public void setObjId(String objId) {
this.objId = objId;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getNodeType() {
return nodeType;
}
public void setNodeType(String nodeType) {
this.nodeType = nodeType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public RealmList<ValueTypeModel> getColumns() {
return columns;
}
public void setColumns(RealmList<ValueTypeModel> columns) {
this.columns = columns;
}
}
4) You need to call RealmUtils.deleteCascade(realmObject); instead realmObject.removeFromRealm();
Example below
Update data in local database
for( NodeModel nodeItem: incomingData.getNodesList() )
{
RealmResults<NodeModel> results = bgRealm.where(NodeModel.class).equalTo("objId", nodeItem.getObjId()).findAll();
if (results.size() > 0)
{
RealmUtils.deleteCascade(results.first());
}
bgRealm.copyToRealm(nodeItem);
}
Enjoy your clean DB! :)
I have a variation on this implementation that others might find useful.
In the original implementation: RealmObject sub-classes that are to be traversable "implement IRealmCascade". Any RealmObjects that do not implement the interface will be treated as leaf nodes (the object will be deleted, but its children will not).
In my implementation: Any RealmObject/RealmList is traversable (they don't need to implement any interface). If the class has a member that is NOT to be traversed, the getter for that member is annotated with "#SkipDelete".
/**
* Traverse the tree of RealmObjects, deleting the RealmObject/RealmList children
* and the root RealmObject.
* <br><br>
* This method uses reflection to get the rootObject's "getter" methods. The
* getter methods are called to get the RealmObject/RealmList children, and
* those objects are deleted from the Realm.
* <br><br>
* If any of the getter methods return a RealmObject/RealmList that should NOT be
* deleted, those getter methods should be annotated with {#link SkipDelete}.
*
* #param rootObject The root of the RealmObject tree
*/
public static void delete(RealmObject rootObject) {
if (rootObject == null) {
return;
}
for (Method method : rootObject.getClass().getSuperclass().getDeclaredMethods()) {
try {
// Ignore non-getter methods
boolean noParams = method.getParameterTypes().length == 0;
if (!(method.getName().startsWith("get")) || !noParams) {
continue;
}
// Ignore primitive members
Class<?> resultType = method.getReturnType();
if (resultType.isPrimitive()) {
continue;
}
// Ignore methods annotated with SkipDelete
if (method.isAnnotationPresent(SkipDelete.class)) {
continue;
}
if (RealmObject.class.isAssignableFrom(resultType)) {
// getter method returns a RealmObject, delete it
try {
RealmObject childObject = (RealmObject) method.invoke(rootObject);
delete(childObject, true);
} catch (Exception ex) {
Log.e("delete: RealmObject " + resultType.getSimpleName(), ex);
}
} else if (RealmList.class.isAssignableFrom(resultType)) {
// getter method returns a RealmList, delete the objects in the list
try {
RealmList childList = (RealmList) method.invoke(rootObject);
while (childList.iterator().hasNext()) {
RealmObject listItem = (RealmObject)childList.iterator().next();
delete(listItem, true);
}
} catch (Exception ex) {
Log.e("delete: RealmList " + resultType.getSimpleName(), ex);
}
}
}
catch (Exception ex) {
Log.e("delete: ", ex);
}
}
rootObject.deleteFromRealm();
}
/**
* This annotation is used to mark a "getter" method that should be skipped
* over on the cascading delete traversal of the RealmObject/RealmList tree.
*/
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface SkipDelete {
}
In your RealmObject
public class Design extends RealmObject {
private MyRealmObject1 obj1; // do CascadeDelete on this member
private MyRealmObject2 obj2; // don't do CascadeDelete on this member
....
public MyRealmObject1 getObj1() {
return obj1;
}
#CascadeDelete.SkipDelete // don't do CascadeDelete of obj2
public MyRealmObject2 getObj2() {
return obj2;
}
}
Related
I am using room database in my app.The app shows books under three categories All Books,Home Books and Library Books.The app shows books data based on user selection.Everything works fine,the app shows correct category books,but when user deletes a Book from any category then the app and list refreshes, then some times app shows wrong data means if a user is viewing books under Home Books category and he deletes a book then the app shows him random books instead of showing his selected Home Books.
// This is how I am loading data:
public void loadBooks(){
if(booksType==1){
mBookViewModel.getAllBooks().observe(this, new Observer<List<Books>>() {
#Override
public void onChanged(#Nullable final List<Books>books) {
adapter.setBooks(books);
}
});
}
if(booksType==2){
mBookViewModel.getHomeBooks().observe(this, new Observer<List<Books>>() {
#Override
public void onChanged(#Nullable final List<Books>books) {
// Update the cached copy of the words in the adapter.
adapter.setBooks(books);
}
});
}
if(booksType==3){
mBookViewModel.getLibrarayBooks().observe(this, new Observer<List<Books>>() {
#Override
public void onChanged(#Nullable final List<Books>books) {
// Update the cached copy of the words in the adapter.
adapter.setBooks(books);
}
});
}
calculateSum();// calculating sum of values of a column.
}
public void calculateSum(){
double total=mBookViewModel.getBookSum();
}
// This how I am deleting a book:
public void delete(Books book){
int rowsDeleted = mBookViewModel.deleteBook(book);
loadBooks(); //have to call this method as I am doing other stuff also.Like calculating the sum of other integer fields.
// suppose user is viewing Home Books(means bookType==2) after this call instead of showing user Home Books the app shows him any random list of books.It does not happen always but after user deletes five to six books continuously and fast.
}
// My View Model:
LiveData<List<Books>> getAllBooks() {return mRepository.getAllBooks(); }
LiveData<List<Books>> getHomeBooks() {return mRepository.getHomeBooks(); }
LiveData<List<Books>> getLibraryBooks() {return mRepository.getLibraryBooks(); }
double getBookSum() {return mRepository.getBookSum(); }
// Repository:
LiveData<List<Books>> getallBooks() {
try {
return new getAllBooksAsyncTask(mBooksDao).execute().get();
} catch (ExecutionException e) {
e.printStackTrace();
return null;
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
private static class getAllBooksAsyncTask extends AsyncTask<Void, Void, LiveData<List<Books>>> {
private BooksDao mAsyncTaskDao;
getAllBooksAscAsyncTask(BooksDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected LiveData<List<Books>> doInBackground(Void... voids) {
return mAsyncTaskDao.getAllBooks();
}
}
LiveData<List<Books>> getallBooks() {
try {
return new getAllBooksAsyncTask(mBooksDao).execute().get();
} catch (ExecutionException e) {
e.printStackTrace();
return null;
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
private static class getHomeBooksAsyncTask extends AsyncTask<Void, Void, LiveData<List<Books>>> {
private BooksDao mAsyncTaskDao;
getHomeBooksAscAsyncTask(BooksDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected LiveData<List<Books>> doInBackground(Void... voids) {
return mAsyncTaskDao.getHomeBooks();
}
}
LiveData<List<Books>> getallBooks() {
try {
return new getAllBooksAsyncTask(mBooksDao).execute().get();
} catch (ExecutionException e) {
e.printStackTrace();
return null;
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
private static class getLibraryBooksAsyncTask extends AsyncTask<Void, Void, LiveData<List<Books>>> {
private BooksDao mAsyncTaskDao;
getLibraryBooksAscAsyncTask(BooksDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected LiveData<List<Books>> doInBackground(Void... voids) {
return mAsyncTaskDao.getLibraryBooks();
}
}
public double getBookSum() {
try {
return new getBookSumAsyncTask(mBookDao).execute().get();
} catch (ExecutionException e) {
e.printStackTrace();
return 0;
} catch (InterruptedException e) {
e.printStackTrace();
return 0;
}
}
private static class getBookSumAsyncTask extends AsyncTask<Void, Void, Double> {
private BookDao mAsyncTaskDao;
getBookSumAsyncTask(BookDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Double doInBackground(Void... voids) {
return mAsyncTaskDao.getBookSum();
}
}
// Dao:
#Query("SELECT * FROM books WHERE type = 1 ")
LiveData<List<Books>> getAllBooks();
#Query("SELECT * FROM books WHERE type = 2 ")
LiveData<List<Books>> getHomeBooks();
#Query("SELECT * FROM books WHERE type = 3 ")
LiveData<List<Books>> getLibraryBooks();
#Query("SELECT SUM(pages) FROM books ")
double getBookSum();
// Recycler View:
#Override
public void onBindViewHolder(#NonNull booksRecyclerAdapter.BookViewHolder holder, int position) {
if (mBookss != null) {
Books current = mBooks.get(position);
String name = current.getName();
holder.nameTextView.setText(name);
}
}
void setBooks(List<Books> book){
mBooks = book;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
if (mBooks != null)
return mBooks.size();
else return 0;
}
public Books getBookAtPosition (int position) {
return mBooks.get(position);
}
I have tried adding adapter.notifyDataSetChanged() and adapter.notifyItemRemoved(position); but it does not work.Also,if there is something wrong in my codes then why it does not happen always,it happens sometimes usually after deleting five or six items continuously.
Any help is highly appreciated.
After much trying I figured out that the problem was because of the adapter.The adapter was loading previously selected books list e.g. if a user first selects homeBooks and then selects allBooks and perform a delete query on any one book in allBooks list then after the delete operation instead of keeping the user on allBooks list the adapter was showing him homeBooks list. As I was using same adapter for all three cases.I think this may be a problem with Room Database .So,when I used different adapters for different cases everything worked as expected.I don't know whether this is the correct way to do it but for now it is working.So the code now becomes
public void loadBooks(){
if(booksType==1){
bookRecyclerAdapter adapter1=new bookRecyclerAdapter(this);
recyclerView.setAdapter(adapter1);
mBookViewModel.getAllBooks().observe(this, new Observer<List<Books>>() {
#Override
public void onChanged(#Nullable final List<Books>books) {
adapter1.setBooks(books);
}
});
}
if(booksType==2){
bookRecyclerAdapter adapter2=new bookRecyclerAdapter(this);
recyclerView.setAdapter(adapter2);
mBookViewModel.getHomeBooks().observe(this, new Observer<List<Books>>() {
#Override
public void onChanged(#Nullable final List<Books>books) {
// Update the cached copy of the words in the adapter.
adapter2.setBooks(books);
}
});
}
if(booksType==3){
bookRecyclerAdapter adapter3=new bookRecyclerAdapter(this);
recyclerView.setAdapter(adapter3);
mBookViewModel.getLibrarayBooks().observe(this, new Observer<List<Books>>() {
#Override
public void onChanged(#Nullable final List<Books>books) {
// Update the cached copy of the words in the adapter.
adapter3.setBooks(books);
}
});
}
calculateSum();// calculating sum of values of a column.
}
I'm willing to try the new Room Library from Android and I met the below error:
Error:(19, 29) error: Cannot figure out how to save this field into
database. You can consider adding a type converter for it.
This error refers to the following class member:
private HashSet<String> fruits;
I have the following class:
#Entity(tableName = "SchoolLunches")
public class SchoolLunch {
#PrimaryKey(autoGenerate = true)
private int lunchId;
private boolean isFresh;
private boolean containsMeat;
private HashSet<String> fruits;
public int getLunchId() {
return lunchId;
}
public void setLunchId(int lunchId) {
this.lunchId = lunchId;
}
public boolean isFresh() {
return isFresh;
}
public void setFresh(boolean fresh) {
isFresh = fresh;
}
public boolean isContainsMeat() {
return containsMeat;
}
public void setContainsMeat(boolean containsMeat) {
this.containsMeat = containsMeat;
}
public HashSet<String> getFruits() {
return fruits;
}
public void setFruits(HashSet<String> fruits) {
this.fruits = fruits;
}
Also, there is a relative DAO class:
#Dao
public interface SchoolLunchDAO {
#Query("SELECT * FROM SchoolLunches")
List<SchoolLunch> getAll();
#Insert
void insertAll(SchoolLunch... schoolLunches);
#Query("DELETE FROM SchoolLunches")
void deleteAll();
}
Since I'm trying to be a very good developer, I wrote a unit test as follows:
#Test
public void singleEntityTest() {
HashSet<String> fruitSet = new HashSet<>();
fruitSet.add("Apple");
fruitSet.add("Orange");
SchoolLunch schoolLunch = new SchoolLunch();
schoolLunch.setContainsMeat(false);
schoolLunch.setFresh(true);
schoolLunch.setFruits(fruitSet);
schoolLunchDAO.insertAll(schoolLunch);
List<SchoolLunch> schoolLunches = schoolLunchDAO.getAll();
assertEquals(schoolLunches.size(), 1);
SchoolLunch extractedSchoolLunch = schoolLunches.get(0);
assertEquals(false, extractedSchoolLunch.isContainsMeat());
assertEquals(true, extractedSchoolLunch.isFresh());
assertEquals(2, extractedSchoolLunch.getFruits().size());
}
What should I do here?
What should I do here?
You could create a type converter, as suggested by the error message. Room does not know how to persist a HashSet<String>, or a Restaurant, or other arbitrary objects.
Step #1: Decide what basic type you want to convert your HashSet<String> into (e.g., a String)
Step #2: Write a class with public static type conversion methods, annotated with #TypeConverter, to do the conversion (e.g., HashSet<String> to String, String to HashSet<String>), in some safe fashion (e.g., use Gson, formatting your String as JSON)
Step #3: Add a #TypeConverters annotation to your RoomDatabase or other scope, to teach Room about your #TypeConverter methods
For example, here are a pair of type converter methods for converting a Set<String> to/from a regular String, using JSON as the format of the String.
#TypeConverter
public static String fromStringSet(Set<String> strings) {
if (strings==null) {
return(null);
}
StringWriter result=new StringWriter();
JsonWriter json=new JsonWriter(result);
try {
json.beginArray();
for (String s : strings) {
json.value(s);
}
json.endArray();
json.close();
}
catch (IOException e) {
Log.e(TAG, "Exception creating JSON", e);
}
return(result.toString());
}
#TypeConverter
public static Set<String> toStringSet(String strings) {
if (strings==null) {
return(null);
}
StringReader reader=new StringReader(strings);
JsonReader json=new JsonReader(reader);
HashSet<String> result=new HashSet<>();
try {
json.beginArray();
while (json.hasNext()) {
result.add(json.nextString());
}
json.endArray();
}
catch (IOException e) {
Log.e(TAG, "Exception parsing JSON", e);
}
return(result);
}
I created the following class and now it works. Thank you, CommonsWare!
public class Converters {
private static final String SEPARATOR = ",";
#TypeConverter
public static HashSet<String> fromString(String valueAsString) {
HashSet<String> hashSet = new HashSet<>();
if (valueAsString != null && !valueAsString.isEmpty()) {
String[] values = valueAsString.split(SEPARATOR);
hashSet.addAll(Arrays.asList(values));
}
return hashSet;
}
#TypeConverter
public static String hashSetToString(HashSet<String> hashSet) {
StringBuilder stringBuilder = new StringBuilder();
for (String currentElement : hashSet) {
stringBuilder.append(currentElement);
stringBuilder.append(SEPARATOR);
}
return stringBuilder.toString();
}
}
I am trying to learn about rxJava and reactive programming in context of android and I feel I am nearly there, I just can't quite grasp the complete picture to fully understand what I am doing.
I have the below code which gets a list of instances of a class called iApps from the database
myHelper m = new myHelper(getApplication());
m.getApps()
.observeOn(Schedulers.newThread())
.subscribe(currentApps::addAll,
throwable -> Log.e("Error Observable", throwable.toString() + " " + Arrays.toString(throwable.getStackTrace())),
() -> compareLists(availableApps, currentApps));
}
Which uses the following methods:
//From my database caller function
public Callable<ArrayList<iApp>> getApps()
{
return this::getCurrentInfo;
}
A custom helper function
public class myHelper {
Context ctx;
tQuery t;
public myHelper(Context _ctx)
{
this.ctx = _ctx;
t = new tQuery(_ctx);
}
Observable<ArrayList<iApp>> getApps()
{
return makeObservable(t.getApps())
.subscribeOn(Schedulers.computation());
}
private static <T> Observable<T> makeObservable(final Callable<T> func) {
return Observable.create(
new Observable.OnSubscribe<T>() {
#Override
public void call(Subscriber<? super T> subscriber) {
try {
subscriber.onNext(func.call());
} catch (Exception ex) {
subscriber.onError(ex);
}
}
});
}
}
However my on complete never runs. I have checked the onNext by looping through the results of iApp and outputting one of the fields so I can see that the data is being collected, however my compareLists function is never run.
Could someone explain my oversight?
Well that was embarassing!
private static <T> Observable<T> makeObservable(final Callable<T> func) {
return Observable.create(
new Observable.OnSubscribe<T>() {
#Override
public void call(Subscriber<? super T> subscriber) {
try {
subscriber.onNext(func.call());
subscriber.onCompleted();
} catch (Exception ex) {
subscriber.onError(ex);
}
}
});
}
I my connecting to a MySQL database that is on PC from my android application.
I am using java.sql.jdb for that. Now I want my result set to get in android.database.cursor??
How can I do that..??
Thats my code I am using in android application its getting the results for database but can't cast to Cursor:
Connection connect = null;
ResultSet resultSet = null;
Statement statement = null;
try {
connect = DriverManager.getConnection("jdbc:mysql://"+DbHelper.DB_Path+"/"+DbHelper.DB_Name+"?"
+ "user="+ DbHelper.DB_UserName+ "&password="+ DbHelper.DB_Pass);
statement = connect.createStatement();
// Result set get the result of the SQL query
resultSet = statement
.executeQuery("Select * from btag_store "+
"Where "+
"guid='"+filterArgs+"'");
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Cursor cc;
cc = (Cursor) resultSet; // error in type casr
I know that type casting will give me error, but is there any other way for that..??
Thanks
To put it simply, you cannot. Unless you are willing to do all the work to define an object that implements the Cursor interface and uses a ResultSet to fulfil Cursor's implementation details. That would be somewhat silly, though, as the ResultSet object is already designed to iterate over results returned from the database. The cleanest approach is to use the ResultSet object as it was intended.
What Dave said is correct. My database items were constructed upon Cursor (Sqlite) but I need the same entrypoint wiht MySQL. So I tried this:
I created a base class
AbstractCursorGen.java:
import android.database.Cursor;
import java.sql.ResultSet;
public abstract class AbstractCursorGen {
protected Cursor c;
protected ResultSet rs;
public abstract int getColumnIndex(String iName);
public abstract String getString(String iName);
public abstract int getInt(String iName);
public abstract long getLong(String iName);
public abstract boolean moveToNext();
public abstract void close();
}
Then the one using Cursor will hold the instance of cursor. There is an additional benefit by getting result giving directly the column string. My code uses this for SQLite.
CursonGen.Java:
import android.database.Cursor;
public class CursorGen extends AbstractCursorGen{
public CursorGen(Cursor c)
{
this.c = c;
}
public int getColumnIndex(String iName)
{
return c.getColumnIndex(iName);
}
public String getString(String iName){
return c.getString(getColumnIndex(iName));
}
public int getInt(String iName){
return c.getInt(getColumnIndex(iName));
}
public long getLong(String iName){
return c.getLong(getColumnIndex(iName));
}
public boolean moveToNext()
{
return c.moveToNext();
}
public void close()
{
c.close();
}
}
And one built upon the resultset. This is used for MySQL results
ResultSetGen.java
import android.util.Log;
import java.sql.ResultSet;
import java.sql.SQLException;
public class ResultSetGen extends AbstractCursorGen{
public ResultSetGen(ResultSet rs)
{
this.rs = rs;
}
public int getColumnIndex(String iName)
{
try {
return rs.findColumn(iName);
} catch (SQLException ex)
{
Log.e("PROX","Column not found");
return -1;
}
}
public String getString(String iName){
try {
return rs.getString(getColumnIndex(iName));
} catch (SQLException ex)
{
Log.e("PROX","Column not found");
return null;
}
}
public int getInt(String iName){
try {
return rs.getInt(getColumnIndex(iName));
} catch (SQLException ex)
{
Log.e("PROX","Column not found");
return -1;
}
}
public long getLong(String iName){
try {
return rs.getLong(getColumnIndex(iName));
} catch (SQLException ex)
{
Log.e("PROX","Column not found");
return -1;
}
}
public boolean moveToNext()
{
try {
return rs.next();
} catch (SQLException ex)
{
Log.e("PROX","Column not found");
return false;
}
}
public void close()
{
try {
rs.close();
} catch (SQLException ex)
{
Log.e("PROX","Column not found");
}
}
}
The trick is providing implementation only for the methods I'm actually using.
This is finally called by (one example)
public Person(AbstractCursorGen cursor)
{
setFromCursor(cursor);
}
protected void setFromCursor(AbstractCursorGen cursor)
{
PersonID = cursor.getLong ( COLUMN_PERSON_ID);
ClusterID = cursor.getInt ( COLUMN_CLUSTER_ID);
Name = cursor.getString ( COLUMN_NAME);
.....
}
Hope this helps.
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.