Сохранение вложенных посторонних объектов с помощью ORMLite на Android - PullRequest
39 голосов
/ 20 января 2011

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

Например, у меня есть следующееРодительский класс.

@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. Это нормальное поведение?Есть ли способ сохранить вложенные объекты и распространить первичный ключ вверх?

Ответы [ 4 ]

47 голосов
/ 17 марта 2012

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

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

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

42 голосов
/ 20 января 2011

Начиная с версии 4.27 ORMlite поддерживает настройки foreignAutoCreate и foreignAutoRefresh для аннотации @DatabaseField в поле:

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

Это означает, что вы назначаетеВаше поле child, и если поле id на дочернем элементе не установлено при создании родительского элемента, оно будет создано.foreignAutoRefresh означает, что при извлечении родителя будет выполнен отдельный вызов SQL для заполнения поля child.

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

Вы также можете лучше контролировать, когда ORMLite выполняет вызовы дочернего объекта, создав дочерний объект. до вы создаете родителя.

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

Еще одна вещь, на которую следует обратить внимание, это то, что без foreignAutoRefresh = true, когда вы запрашиваете родительский объект, дочерний объект, который вы получаете обратно only извлекается поле id.Если идентификатор представляет собой автоматически сгенерированный 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);

Для получения дополнительной документации об этом см.онлайн страница о Поля посторонних объектов .

4 голосов
/ 13 октября 2013
@DatabaseField(foreign = true,foreignAutoCreate = true,foreignAutoRefresh = true)
public Child child;

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

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

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

4 голосов
/ 21 января 2011

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

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

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