Saving nested foreign objects with ORMLite on Android

后端 未结 4 1440
-上瘾入骨i
-上瘾入骨i 2020-12-07 12:57

When working on Android, does ORMLite only save shallow level objects? I have a data structure with nested Objects, both of which are newly created, and I would like to be a

相关标签:
4条回答
  • 2020-12-07 13:12

    As of version 4.27 ORMlite supports the foreignAutoCreate and foreignAutoRefresh settings on the @DatabaseField annotation on a field:

    @DatabaseField(foreign = true, foreignAutoCreate = true, foreignAutoRefresh = true)
    public Child child;
    

    This means that you assign your child field and if the id field on the child is not set when the parent is created then it to will be created. The foreignAutoRefresh means that when a parent is retrieved a separate SQL call will be made to get the child field populated.

    When doing this, the parent object is persisted but not the child object and the auto-generated child_id column in the parent table is set to 0. Is this normal behavior?

    You can also have more control over when ORMLite makes the calls to the child object by creating the child before you create the parent.

    Parent parent = new Parent();
    parent.name = "ParentName";
    
    Child child = new Child();
    child.name = "ChildName";
    
    parent.child = child;
    
    // this will update the id in child
    childDao.create(child);
    
    // this saves the parent with the id of the child
    parentDao.create(parent);
    

    One more thing to note is that without the foreignAutoRefresh = true when you query for a Parent object, the child object that you get back only has its id field retrieved. If the id is an auto-generated int (for example), then the above name field will not be retrieved until you do an update on the child object.

    // assuming the id of the Parent is the name
    Parent parent = parentDao.queryForId("ParentName");
    System.out.println("Child id should be set: " + parent.child.id);
    System.out.println("Child name should be null: " + parent.child.name);
    
    // now we refresh the child object to load all of the fields
    childDao.refresh(parent.child);
    System.out.println("Child name should now be set: " + parent.child.name);
    

    For more documentation about this, see the online page about Foreign Object Fields.

    0 讨论(0)
  • 2020-12-07 13:15

    Did you try this?

    @DatabaseField(foreign = true, foreignAutoCreate = true, foreignAutoRefresh = true)
    public Child child;
    

    I'm using ORMLite 4.35.

    0 讨论(0)
  • 2020-12-07 13:24

    As mentioned, this does not seem to be supported in the lite version. I wrote a simple recursive function to save all referenced objects. I had problems getting the generics to play nice so in the end I just removed them all. I also made a base Entity class for my db objects.

    So here is what I wrote. If anyone can get the same code to work with proper generics, or can improve upon it, please feel free to edit.

        // Debugging identity tag
        public static final String TAG = DatabaseHelper.class.getName();
    
        // Static map of common DAO objects
        @SuppressWarnings("rawtypes")
        private static final Map<Class, Dao<?, Integer>> sDaoClassMap = new HashMap<Class, Dao<?,Integer>>();
    
        /**
         * Persist an entity to the underlying database.
         * 
         * @param context
         * @param entity
         * @return boolean flag indicating success
         */
        public static boolean create(Context context, Entity entity) {
            // Get our database manager
            DatabaseHelper databaseHelper = DatabaseHelper.getHelper(context);
    
            try {
                // Recursively save entity
                create(databaseHelper, entity);
    
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "Object is not an instance of the declaring class", e);
                return false;
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Field is not accessible from the current context", e);
                return false;
            } catch (SQLException e) {
                Log.e(TAG, "Unable to create object", e);
                return false;
            }
    
            // Release database helper
            DatabaseHelper.release();
    
            // Return true on success
            return true;
        }
    
        /**
         * Persist an entity to the underlying database.<br><br>
         * For each field that has a DatabaseField annotation with foreign set to true, 
         * and is an instance of Entity, recursive attempt to persist that entity as well. 
         * 
         * @param databaseHelper
         * @param entity
         * @throws IllegalArgumentException
         * @throws IllegalAccessException
         * @throws SQLException
         */
        @SuppressWarnings("unchecked")
        public static void create(DatabaseHelper databaseHelper, Entity entity) throws IllegalArgumentException, IllegalAccessException, SQLException {
            // Class type of entity used for reflection
            @SuppressWarnings("rawtypes")
            Class clazz = entity.getClass();
    
            // Search declared fields and save child entities before saving parent. 
            for(Field field : clazz.getDeclaredFields()) {
                // Inspect annotations
                for(Annotation annotation : field.getDeclaredAnnotations()) {
                    // Only consider fields with the DatabaseField annotation
                    if(annotation instanceof DatabaseField) {
                        // Check for foreign attribute
                        DatabaseField databaseField = (DatabaseField)annotation;
                        if(databaseField.foreign()) {
                            // Check for instance of Entity
                            Object object = field.get(entity);                      
                            if(object instanceof Entity) {
                                // Recursive persist referenced entity
                                create(databaseHelper, (Entity)object);
                            }
                        }
                    }
                }
            }
    
            // Retrieve the common DAO for the entity class
            Dao<Entity, Integer> dao = (Dao<Entity, Integer>) sDaoClassMap.get(clazz);
            // If the DAO does not exist, create it and add it to the static map
            if(dao == null) {
                dao = BaseDaoImpl.createDao(databaseHelper.getConnectionSource(), clazz);
                sDaoClassMap.put(clazz, dao);
            }
    
            // Persist the entity to the database
            dao.create(entity);
        }
    
    0 讨论(0)
  • 2020-12-07 13:28
    @DatabaseField(foreign = true,foreignAutoCreate = true,foreignAutoRefresh = true)
    public Child child;
    

    Some Notes on this solution

    1. (foreignAutoCreate = true) work only if the ID field is not set (null or 0) according to ORMlite documentation http://ormlite.com/javadoc/ormlite-core/com/j256/ormlite/field/DatabaseField.html

      • foreignAutoCreate : "Set this to be true (default false) to have the foreign field will be automagically created using its internal DAO if the ID field is not set (null or 0)."
    2. This only works if generatedId is also set to true for the child table according to ORMlite documentation.

    0 讨论(0)
提交回复
热议问题