Подтвердить что ты не робот

Сохранение вложенных посторонних объектов с помощью ORMLite на Android

Когда вы работаете на Android, ORMLite сохраняет только объекты с неглубоким уровнем? У меня есть структура данных с вложенными объектами, оба из которых были недавно созданы, и я хотел бы иметь возможность сохранять оба из них одним вызовом dao.create()

Для exmaple у меня есть следующий родительский класс.

@DatabaseTable
public class Parent {

  @DatabaseField(generatedId=true)
  public int id;

  @DatabaseField
  public String name;

  @DatabaseField
  public Child child;
}

и следующий дочерний класс.

@DatabaseTable
public class Child {

  @DatabaseField(generatedId=true)
  public int id;

  @DatabaseField
  public String name;
}

Я хочу иметь возможность сделать следующее.

Parent parent = new Parent();
parent.name = "ParentName";

Child child = new Child();
child.name = "ChildName";

parent.child = child;

//  .. get helper and create dao object...
dao.create(parent);

При этом родительский объект сохраняется, но не дочерний объект, а автоматически сгенерированный столбец child_id в родительской таблице равен 0. Это обычное поведение? Есть ли способ сохранить вложенные объекты и распространять первичный ключ?

4b9b3361

Ответ 1

При этом родительский объект сохраняется, но не дочерний объект, а автогенерированный столбец child_id в родительской таблице равен 0. Это обычное поведение?

OrmLite не автоматически сохраняет вложенные объекты автоматически, как другие ORM. Он разработан с учетом серьезного названия "Lite" и с учетом девиза KISS.

Однако вы можете легко вложить вложенные объекты, создав дочерний элемент до создания родительского элемента.

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

Еще одна вещь, которую следует отметить, заключается в том, что когда вы запрашиваете объект родителя, дочерний объект, который вы возвращаете, имеет только поле своего идентификатора. Если идентификатор является автоматически сгенерированным int (например), то указанное выше поле имени не будет восстановлено до тех пор, пока вы не выполните обновление дочернего объекта.

// 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);

Подробнее об этом см. на веб-странице Поля внешних объектов.

Ответ 2

Вы пробовали это?

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

Я использую ORMLite 4.35.

Ответ 3

Как уже упоминалось, это не похоже на поддержку в облегченной версии. Я написал простую рекурсивную функцию для сохранения всех ссылочных объектов. У меня были проблемы с получением дженериков, чтобы играть хорошо, поэтому, в конце концов, я просто удалил их всех. Я также создал базовый класс Entity для своих объектов db.

Итак, вот что я написал. Если кто-то может получить тот же код для работы с надлежащими дженериками или может улучшить его, отредактируйте его.

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

Ответ 4

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

Некоторые примечания к этому решению

  • (foreignAutoCreate = true) работают только в том случае, если поле ID не установлено (null или 0) в соответствии с документацией ORMlite http://ormlite.com/javadoc/ormlite-core/com/j256/ormlite/field/DatabaseField.html

    • foreignAutoCreate: Msgstr "Установить это значение true (по умолчанию false), чтобы внешнее поле было автоматически создано с использованием внутреннего DAO, если поле ID не установлено (значение null или 0)."
  • Это работает только в том случае, если сгенерированныйId также установлен в true для дочерней таблицы в соответствии с документацией ORMlite.