在 Android 上使用 ORMLite 保存嵌套外来对象

发布于 2024-10-12 19:33:47 字数 838 浏览 1 评论 0原文

在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。这是正常行为吗?有没有办法让嵌套对象持久化并向上传播主键?

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 able to save both of them with one call to dao.create()

For exmaple, I have the following Parent Class.

@DatabaseTable
public class Parent {

  @DatabaseField(generatedId=true)
  public int id;

  @DatabaseField
  public String name;

  @DatabaseField
  public Child child;
}

and the following Child Class.

@DatabaseTable
public class Child {

  @DatabaseField(generatedId=true)
  public int id;

  @DatabaseField
  public String name;
}

I want to be able to do the following.

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

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? Is there a way to have nested objects persisted and propagate the primary key up?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

£烟消云散 2024-10-19 19:33:47

你尝试过这个吗?

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

我正在使用 ORMLite 4.35。

Did you try this?

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

I'm using ORMLite 4.35.

将军与妓 2024-10-19 19:33:47

从版本 4.27 开始,ORMlite 支持 foreignAutoCreateforeignAutoRefresh 设置在字段的 @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,您返回的子对象会检索到其 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);

有关此内容的更多文档,请参阅有关外部对象字段的在线页面。

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.

满意归宿 2024-10-19 19:33:47

如前所述,精简版似乎不支持此功能。我编写了一个简单的递归函数来保存所有引用的对象。我在让泛型发挥良好作用时遇到了问题,所以最后我把它们全部删除了。我还为我的数据库对象创建了一个基本实体类。

这就是我写的。如果任何人都可以获得相同的代码来使用适当的泛型,或者可以对其进行改进,请随意编辑。

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

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);
    }
轮廓§ 2024-10-19 19:33:47
@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:
      “将此设置为 true(默认 false),以便在未设置 ID 字段(空或 0)时使用其内部 DAO 自动创建外部字段。”
  2. 只有根据 ORMlite 文档,子表的 generatedId 也设置为 true 时,这才有效a>.

@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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文