web-dev-qa-db-ja.com

AndroidでORMLiteを使用してネストされた外部オブジェクトを保存する

Androidで作業する場合、ORMLiteは浅いレベルのオブジェクトのみを保存しますか?入れ子になったオブジェクトのデータ構造があり、両方とも新しく作成されましたが、dao.create()への1回の呼び出しで両方を保存できるようにしたいと思います

たとえば、次の親クラスがあります。

@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に設定されます。これは通常の動作ですか?ネストされたオブジェクトを永続化し、主キーを上に伝搬する方法はありますか?

40
Chase

バージョン4.27以降、ORMliteはフィールドの@DatabaseFieldアノテーションの foreignAutoCreate および foreignAutoRefresh 設定をサポートします。

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

つまり、childフィールドを割り当て、親の作成時に子のidフィールドが設定されていない場合は、そのフィールドが作成されます。 foreignAutoRefreshは、親が取得されると、別のSQL呼び出しが行われてchildフィールドに値が入力されることを意味します。

これを行うと、親オブジェクトは永続化されますが、子オブジェクトは永続化されず、親テーブルの自動生成されたchild_id列は0に設定されます。これは通常の動作ですか?

また、親を作成する前にchildを作成することで、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);

もう1つ注意すべき点は、foreignAutoRefresh = trueがないと、Parentオブジェクトを照会するときに、返される子オブジェクトonlyには、 IDフィールドが取得されました。 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);

この詳細については、オンラインページの Foreign Object Fields を参照してください。

やってみましたか?

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

ORMLite 4.35を使用しています。

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

このソリューションに関する注意事項

  1. (foreignAutoCreate = true)ORMliteのドキュメントに従ってIDフィールドが設定されていない(nullまたは0)場合にのみ機能 http://ormlite.com/javadoc/ormlite-core/com/j256/ormlite/field/DatabaseField .html

    • foreignAutoCreate: "IDフィールドが設定されていない場合(nullまたは0)、内部DAOを使用して外部フィールドが自動的に作成されるようにするには、これをtrue(デフォルトはfalse)に設定します。"
  2. ORMliteのドキュメント に従って、子テーブルのgeneratedIdもtrueに設定されている場合にのみ機能します。

4
Ayman Mahgoub

前述のように、これはliteバージョンではサポートされていないようです。参照されているすべてのオブジェクトを保存する簡単な再帰関数を作成しました。ジェネリックでニースをプレイするのに問題があったので、結局すべてを削除しました。また、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
Chase