Realm Database insert rows during migration - android

I have a small migration class and I wan't to create the SportTypes table with some predefined rows when the app starts . I tried the commented solutions under the migrate method but no chance.
public class SportTypes extends RealmObject {
#PrimaryKey
private Integer id;
private String name;
private RealmList<SportTypes> sportTypes;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public RealmList<SportTypes> getSportTypes() {
return sportTypes;
}
public void setSportTypes(RealmList<SportTypes> sportTypes) {
this.sportTypes = sportTypes;
}
}
public class RealmMigrations implements RealmMigration {
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
schema.create("SportTypes")
.addField("id", Integer.class, FieldAttribute.PRIMARY_KEY)
.addField("name", String.class);
// schema.get("SportTypes")
// .transform(new RealmObjectSchema.Function() {
// #Override
// public void apply(DynamicRealmObject obj) {
// Realm realm = Realm.getDefaultInstance();
// realm.beginTransaction();
// SportTypes sport = realm.createObject(SportTypes.class);
// sport.setId(0);
// sport.setName("cycling");
// sport.getSportTypes().add(sport);
// realm.commitTransaction();
// realm.close();
// }
// });
// schema.get("SportTypes").transform(new RealmObjectSchema.Function() {
// #Override
// public void apply(DynamicRealmObject obj) {
// obj.setInt("id", 0);
// obj.setInt("id", 1);
// obj.setInt("id", 2);
// obj.setInt("id", 3);
// obj.setInt("id", 4);
// obj.setString("name", "Running");
// obj.setString("name", "Cycling");
// obj.setString("name", "Swimming");
// obj.setString("name", "IndoorRunning");
// obj.setString("name", "IndoorCycling");
// }
// });
// Deliberately not using old version to run upper lines
if (oldVersion == 0) {
oldVersion++;
}
}
}
I call the below part in the onCreate method of the activity. I also manually changing the version to trigger the migrate event of the migration class but can't get any rows.
Realm.init(this);
final RealmConfiguration configuration = new RealmConfiguration.Builder().name("sports1761.realm").schemaVersion(1).migration(new RealmMigrations()).build();
Realm.setDefaultConfiguration(configuration);
// try {
// Realm.migrateRealm(configuration);
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// }
Realm realm = Realm.getDefaultInstance();
RealmObjectSchema schema = realm.getSchema().get("SportTypes");
final RealmResults<SportTypes> sports = realm.where(SportTypes.class).findAll();
Integer size = sports.size();

I found the solution.You should get first the schema and create DynamicRealmObject and delete old records before adding new ones then just add the new records. If your object has a primary key you should create the object using field & primary key overload of the createObject method.
public class RealmMigrations implements RealmMigration {
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
if (oldVersion == 0) {
RealmSchema schema = realm.getSchema();
RealmObjectSchema sportTypesSchema = schema.get("SportTypes");
realm.delete("SportTypes");
DynamicRealmObject sportType = realm.createObject("SportTypes", 0);
sportType.setString("name", "Running");
DynamicRealmObject sportType1 = realm.createObject("SportTypes", 1);
sportType.setString("name", "Cycling");
DynamicRealmObject sportType2 = realm.createObject("SportTypes", 2);
sportType.setString("name", "Swimming");
DynamicRealmObject sportType3 = realm.createObject("SportTypes", 3);
sportType.setString("name", "Indoor Running");
DynamicRealmObject sportType4 = realm.createObject("SportTypes", 4);
sportType.setString("name", "Indoor Cycling");
RealmList<DynamicRealmObject> listItems = sportType.getList("sportTypes");
List<DynamicRealmObject> values = Arrays.asList(sportType, sportType1, sportType2, sportType3, sportType4);
listItems.addAll(values);
oldVersion++;
}
}
}

Related

How to update initial data in realm after update of apk?

In the app there is a realm database that has both initial data and data added by the user. The data added by the user should stay on the device even after the updates. The initial data is added through the class
public class RealmInitialData implements Realm.Transaction {
#Override
public void execute(Realm realm){
//initial data
Items item = new Items();
item.setName("Fragrance");
item.setTimestamp(System.currentTimeMillis());
realm.insertOrUpdate(item);
}
#Override
public int hashCode() {
return RealmInitialData.class.hashCode();
}
#Override
public boolean equals(Object obj) {
return obj != null && obj instanceof RealmInitialData;
}
}
The configuration (situated in a class which extends Application class) looks like this:
RealmConfiguration realmConfig = new RealmConfiguration.Builder()
.name("tasky.realm")
.schemaVersion(2)
.migration(new Migration())
.initialData(new RealmInitialData())
.build();
If I change/add something in the RealmInitialData for update, the data on the user application doesn't change. How can I both change the initial data (add or rewrite sth) and make no changes to the user's data?
You have to implement RealmMigration and pass it to RealmConfiguration.Builder().migration().
Something like this:
public class RealmMigrationAgent implements RealmMigration
{
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
if (oldVersion == 0) {
schema.get("InventDisableObject").addField("Reason", int.class);
oldVersion++;
}
if (oldVersion == 1) {
if (!schema.get("ActionObject").hasField("Annual"))
schema.get("ActionObject").addField("Annual", boolean.class);
oldVersion++;
}
if (oldVersion == 2) {
if (!schema.get("ActionObject").hasField("Hash"))
schema.get("ActionObject").addField("Hash", String.class);
if (!schema.get("ActionObject").hasField("Manual"))
schema.get("ActionObject").addField("Manual", boolean.class);
oldVersion++;
}
if (oldVersion == 3) {
if (!schema.get("OutletObject").hasField("Code"))
schema.get("OutletObject").addField("Code", String.class);
oldVersion++;
}
}
}
Incrementally adding scheme updates for every new scheme version.
I can see you have some class Migration for migration reason - can you provide the code of it?
I have the same problem and i find the way.For example,if your new version is 7,try to use SharedRealm.getInstance(config).getSchemaVersion() to get the old version 6 before Realm.setDefaultConfiguration(config),then you can update old realm data after setDefaultConfiguration which old version < 7.May this can help you:
RealmConfiguration config = new RealmConfiguration.Builder()
.schemaVersion(7)
.migration(new RealmMigration() {
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
if(oldVersion == 6) {
schema.get("ImageRealm")
.addField("isSelected", Boolean.class, FieldAttribute.REQUIRED)
.addField("isLow", Boolean.class, FieldAttribute.REQUIRED);
oldVersion++;
}
}
}).build();
SharedRealm sharedRealm = SharedRealm.getInstance(config);
long oldVersion = sharedRealm.getSchemaVersion();
Realm.setDefaultConfiguration(config);
long newVersion = Realm.getDefaultInstance().getVersion();
// 如果有历史记录,更新状态
if (oldVersion < 7) {
Realm.getDefaultInstance().executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<DocRealmBean> allData = realm.where(DocRealmBean.class).findAll();
for (DocRealmBean bean : allData) {
bean.state = "0";
}
}
});
}

Not delete embedded objects from Realm

Android Studio 3.2.
Realm 5.8.0
public class Merchant extends RealmObject {
#PrimaryKey
private long id;
private Image preview;
}
public class Image extends RealmObject {
#PrimaryKey
private long id;
}
I need to delete Merchants object with specific ids AND all it embedded objects (Image in my example).
So here code:
public static void updateMerchantList(final List<Merchant> thatMerchantsList) {
Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmList<Merchant> localMerchantList = getMerchantsRealmList();
if (!EqualsUtil.areEqualContentLists(localMerchantList, thatMerchantsList)) {
List<Merchant> itemNotExistInThatMerchants = new ArrayList<>(localMerchantList);
itemNotExistInThatMerchants.removeAll(thatMerchantsList);
if (itemNotExistInThatMerchants.size() > 0) {
localMerchantList.removeAll(itemNotExistInThatMerchants);
Long[] idsToDeleteArray = new Long[itemNotExistInThatMerchants.size()];
for (int index = 0; index < itemNotExistInThatMerchants.size(); index++) {
Merchant merchant = itemNotExistInThatMerchants.get(index);
idsToDeleteArray[index] = merchant.getId();
}
RealmResults<Merchant> localMerchantsForDelete = realm.where(Merchant.class).in(Merchant.ID, idsToDeleteArray).findAll();
boolean isDelete = localMerchantsForDelete.deleteAllFromRealm();
}
}
}
});
} finally {
realm.close();
}
}
public static RealmList<Merchant> getMerchantsRealmList() {
Realm realm = Realm.getDefaultInstance();
try {
RealmResults<Merchant> realmResults = realm.where(Merchant.class).findAll();
RealmList<Merchant> realmList = new RealmList<>();
realmList.addAll(realmResults.subList(0, realmResults.size()));
return realmList;
} finally {
realm.close();
}
}
As result 2 Merchant success delete from Realm (by method deleteAllFromRealm) .
Nice.
But all embedded objects (like Image) NOT delete from Realm.
Questions:
This is because I need to write custom method that cascade delete Merchant and all it embedded objects?
Is Realm can cascade delete objects?

Android Swipe To Dismiss on PagedList with Realm local Database loses list position

My current Android application is using <LiveData<PagedList<ModelUI>> with a Realm local database.
I'm basing my approach on this #EpicPanda article.
I'm employing the following Architecture components
api "android.arch.lifecycle:extensions:1.1.1"
api "android.arch.paging:runtime:1.0.1"
My datasource classes resemble this:-
public abstract class BaseDataSource<T> extends PositionalDataSource<ModelDO> {
#WorkerThread
public abstract int countItems();
#WorkerThread
public abstract List<ModelDO> loadRange(final int startPosition, final int count);
#Override
public final void loadInitial(#NonNull final LoadInitialParams params, #NonNull final LoadInitialCallback<ModelDO> callback) {
final int totalCount = countItems();
if (totalCount == 0) {
callback.onResult(Collections.emptyList(), 0, 0);
return;
}
final int firstLoadPosition = computeInitialLoadPosition(params, totalCount);
final int firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount);
final List<ModelDO> list = loadRange(firstLoadPosition, firstLoadSize);
if (list != null && list.size() == firstLoadSize) {
callback.onResult(list, firstLoadPosition, totalCount);
} else {
invalidate();
}
}
#Override
public final void loadRange(#NonNull final LoadRangeParams params, #NonNull final LoadRangeCallback<ModelDO> callback) {
final List<ModelDO> list = loadRange(params.startPosition, params.loadSize);
if (list == null) {
invalidate();
} else {
callback.onResult(list);
}
}
}
and this:-
public class ModelDataSource<T> extends BaseDataSource<ModelDO> {
private static final String TAG = "ModelDataSource";
private final Realm workerRealm;
private final RealmResults<ModelDO> liveResults;
private final RealmChangeListener<RealmResults<ModelDO>> realmChangeListener = results -> {
if (results.isLoaded()) {
Log.i(TAG, "REALM DATA CHANGE DETECTED");
invalidate();
}
};
// WORKER THREAD
ModelDataSource() {
Log.i(TAG, "CREATED" + this);
this.workerRealm = DatabaseController.getRealm();
this.liveResults = DatabaseController.fetchShortListedResults(this.workerRealm);
if (!liveResults.isLoaded()) {
liveResults.load();
}
this.liveResults.addChangeListener(realmChangeListener);
}
#WorkerThread
public int countItems() {
Log.i(TAG, "COUNTING ITEMS" + this);
if (workerRealm.isClosed() || !liveResults.isValid()) {
Log.i("REALM TILED DATA SOURCE", "RESULTS ARE NOT VALID, OR REALM IS CLOSED.");
return 0;
}
Log.i(TAG, "ITEM SIZE [" + liveResults.size() + "]");
return liveResults.size();
}
#Override
public boolean isInvalid() {
Log.i(TAG, "REFRESHING REALM." + this);
workerRealm.refresh();
return super.isInvalid();
}
#WorkerThread
#Override
public List<ModelDO> loadRange(int startPosition, int count) {
Log.i(TAG, "LOAD: " + startPosition + " , " + count + " " + this);
int countItems = countItems();
if (countItems == 0) {
return Collections.emptyList();
}
final List<ModelDO> list = new ArrayList<>(count);
for (int i = startPosition; i < startPosition + count && i < countItems; i++) {
list.add(workerRealm.copyFromRealm(liveResults.get(i)));
}
return Collections.unmodifiableList(list);
}
}
My data factory class resembles:-
public class ModelDataSourceFactory<T> extends DataSource.Factory<Long, ModelUI> {
private static final String TAG = "ModelDataSourceFactory";
#Override
public DataSource<Long, ModelUI> create() {
Log.d(TAG, "create() called");
return new ModelDataSourceFactory().mapByPage(new Function<List<ModelDO>, List<ModelUI>>() {
#Override
public List<ModelUI> apply(final List<ModelDO> input) {
Log.d(TAG, "apply() called with: input = [" + input.size() + "]");
final List<ModelUI> output = new ArrayList<>();
for (final ModelDO articleSourceDO : input) {
final ModelUI articleSourceUI = constructModelUI(articleSourceDO);
output.add(articleSourceUI);
}
return output;
}
});
}
}
My ViewModel repository is a singleton; I configure my pagedList as shown here
realmPaginationManager = new PagingManager();
realmPaginationManager.open();
pagedListConfig = (new PagedList.Config.Builder())
.setEnablePlaceholders(true)
.setPrefetchDistance(17)
.setPageSize(20)
.build();
MY_PAGED_MODELS = new LivePagedListBuilder(new ArticleSourceDataSourceFactory(), pagedListConfig)
.setFetchExecutor(realmPaginationManager.getFetchExecutor())
.setInitialLoadKey(0)
.build();
When the list is first displayed it loads 60 items starting from 0 by calling the DataSources public final void loadInitial(#NonNull final LoadInitialParams params, #NonNull final LoadInitialCallback<ModelDO> callback) {} method.
The issue I have is that my list must support swipe to dismiss.
When I swipe to dismiss I delete the "swiped" record from the Realm database and my RealmChangeListener invalidates my Datasource as shown e.g.
private final RealmChangeListener<RealmResults<ModelDO>> realmChangeListener = results -> {
if (results.isLoaded()) {
Log.i(TAG, "REALM DATA CHANGE DETECTED");
invalidate();
}
};
What I would expect to happen is that by invalidating the DataStore within my RealmChangeListener this would re trigger the loadInitial() method again and I would now see a new List displayed with the swiped item missing.
What actually happens is the following
a). loadInitial() is called and loads firstLoadPosition = 0, firstLoadSize = 60.
b). loadRange() is called and loads startPosition = 60, count = 20
c). loadRange() is called and loads startPosition = 80, count = 20
I now have to page backwards through the list to where I swiped to dismiss and the deleted item has been removed.
Why doesn't the datastore simply call loadInitial() and redisplay the top of the list?

Realm: The size of filled RealmList in RealmObject is zero

Scenario:
I have a model(DBBasket) to persist locally, the number of added products and products itself.
When the user clicks on the + button under each product thumbnail
I'm increasing totalQuantity in Product,
Adding the product to the DBBasket,
Set totalProducts which I get from server, in DBBasket too.
Codes in + button:
holder.HomeProductBindGrid.thatPlusButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final Product productIns = mProductsList.get(holder.getAdapterPosition());
mRealm = mRealmManager.getLocalInstance();
mRealm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
mProductRealm = realm.where(Product.class).equalTo(ProductFields.UNIQUE_ID, productIns.getUniqueId()).findFirst();
....
realm = mRealmManager.getLocalInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
String totalNumberOfItemsInBasket = parseDataFromServer(ServerResponse, "numberOfProducts");
if (totalNumberOfItemsInBasket.matches("")) {
totalNumberOfItemsInBasket = "0";
}
Product product = realm.where(Product.class).equalTo(ProductFields.UNIQUE_ID, prod.getUniqueId()).findFirst();
if (product == null) {
product = realm.createObject(Product.class, mProductRealm.getUniqueId());
}
if (product != null) {
if (product.totalQuantity.get() == null) {
product.totalQuantity.set(0);
product.totalQuantity.increment(countOrder);
} else {
product.totalQuantity.increment(countOrder);
}
realm.insertOrUpdate(product);
DBBasket dbBasket = realm.where(DBBasket.class).findFirst();
if (dbBasket == null) {
dbBasket = realm.createObject(DBBasket.class);
}
dbBasket.getProducts().add(product);
dbBasket.setTotalProducts(totalNumberOfItemsInBasket);
realm.insertOrUpdate(dbBasket);
Log.wtf("productRealm", dbBasket.getProducts().get(0).getUniqueId() + "");// It shows the UID correctly.
}
}
});
Cause of some reason, I clean the Product on Activity Destroy, but not DBBasket:
Realm realm = mRealmManager.getLocalInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
realm.delete(Product.class);
}
});
Each time the user comes to the main page, I receive the list of products from the server, and insert them to the local DB:
for (int j = 0; j < receivedProductsFromServer.getLength(); j++) {
final Product product = new Product(...);
product.setUniqueId(Utils.UniqueIdMaker());
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
Product productRealm = realm.where(Product.class).equalTo(ProductFields.UNIQUE_ID, product.getUniqueId()).findFirst();
if (productRealm == null) {
realm.insert(product);
}
}
});
Problem:
Now, for those products which added previously to the DBBasket, I want to show their totalQuantity in front of that +.
So I've changed above snippet code to:
for (int j = 0; j < receivedProductsFromServer.getLength(); j++) {
final Product product = new Product(...);
product.setUniqueId(Utils.UniqueIdMaker());
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
Product productRealm = realm.where(Product.class).equalTo(ProductFields.UNIQUE_ID, product.getUniqueId()).findFirst();
if (productRealm == null) {
DBBasket dbBasketRealm = realm.where(DBBasket.class).findFirst();
if(dbBasketRealm != null) {
RealmList<Product> productInBasket = dbBasketRealm.getProducts(); //Size() is zero!!!
RealmResults<Product> productFiltered = productInBasket.where().contains(ProductFields.UNIQUE_ID, product.getUniqueId()).findAll();
Product p = productFiltered.get(0);
if(p != null) {
product.totalQuantity.set(0);
product.totalQuantity.increment(Integer.valueOf(p.getQuantity()));
} else Log.wtf("productRealm", "productFiltered is Null.");
}
realm.insert(product);
}
}
});
But didn't work! and dbBasketRealm.getProducts() size is zero.
Edit:
+ Button on Debug mode:
dbBasketRealm.getProducts() on Debug mode:
DBBasket model:
public class DBBasket extends RealmObject{
public String totalProducts;
public RealmList<Product> products;
public DBBasket() {}
}
Product model:
public class Product extends RealmObject implements Observable {
#PrimaryKey
#Required
private String uniqueId;
public final MutableRealmInteger totalQuantity = MutableRealmInteger.valueOf(0);
public Product() {}
}
Get the previously added products in the basket, from the server. Then add them again to the local database. Something like below snippet code in OnCreate for example:
for (int i = 0; i < receivedFromServer.getLength(); i++) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
Product productRealm = realm.where(Product.class).equalTo(ProductFields.UNIQUE_ID, receivedFromServer[i].getUniqueId()).findFirst();
if (productRealm != null) {
productRealm.setTotalQuantity(quantity));
DBBasket dbBasket = realm.where(DBBasket.class).findFirst();
if (dbBasket == null) {
dbBasket = realm.createObject(DBBasket.class);
}
RealmList<Product> productRealmList = dbBasket.getProducts();
productRealmList.add(productRealm);
dbBasket.products = productRealmList;
dbBasket.setProducts(productRealmList);
dbBasket.totalProductsSetZero();
dbBasket.setTotalProducts(quantity));
realm.insertOrUpdate(dbBasket);
}
}
});

Realm "cascade delete" on Android

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;
}
}

Categories

Resources