简体   繁体   中英

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM