在 Android 上使用 ORMLite 保存嵌套的外来对象
Posted
技术标签:
【中文标题】在 Android 上使用 ORMLite 保存嵌套的外来对象【英文标题】:Saving nested foreign objects with ORMLite on Android 【发布时间】:2011-01-20 09:55:05 【问题描述】:在 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。这是正常行为吗?有没有办法让嵌套对象持久化并向上传播主键?
【问题讨论】:
另见this related question。 虽然 Gray 总是参考他的页面,但是如果没有关于 *** 的问题和带有完整示例的直接答案(那些实际上符合他的文档),那么很难通过 ORMLite。没有冒犯灰色 【参考方案1】:你试过了吗?
@DatabaseField(foreign = true, foreignAutoCreate = true, foreignAutoRefresh = true)
public Child child;
我正在使用 ORMLite 4.35。
【讨论】:
我之前的评论说 ORMLite for Android 不支持这些功能,but it does! +1 - 因为在我看来这是正确的答案。这几乎对我有用。我的问题是我从服务器获取了带有 ID 的外来对象,它只会自动创建 id=0 或 null 的外来对象:(我能做些什么来避免手动创建外来对象? 请注意,这种方法可能会影响性能。例如,在 UI 中,您可能会在对象标题中显示少量信息,并且仅根据用户需求查询详细信息。 不幸的是,不适用于@ForeignCollectionField 它运行良好,当我尝试创建父对象时,我的子对象也会创建。但是当我更改子对象中的一个字段的值然后我调用更新父对象时,它不会更新子对象自动地。你有什么线索吗?【参考方案2】:从 4.27 版开始,ORMlite 支持字段上的 @DatabaseField
注释上的 foreignAutoCreate 和 foreignAutoRefresh 设置:
@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);
还有一点需要注意的是,当您查询 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);
有关这方面的更多文档,请参阅有关 Foreign Object Fields 的在线页面。
【讨论】:
感谢您的反馈。我想的也差不多。我的问题只给出了一个简单的例子,但我真正想做的是能够存储相当复杂的对象,而不必对对象本身了解太多。我写了一个快速解决方案,我将发布,但我认为它可以改进。 请将其@Chase 发布到 ORMLite 邮件列表。 groups.google.com/group/ormlite-user 当然。我注意到较新版本的 ORMLite 现在支持类似的功能。从那以后,我进行了许多其他改进,我也会发布邮件列表。 @gray 根据 ORMLite 文档,您还应该这样做parent.child = child;
和 child=parent;
。此外,我在 ChilderDao 之后使用 create ParentDao 所以 childrenDao.create(children); parentDao.create(parent);
它会导致孩子被创建两次。可能是 Collection如前所述,精简版似乎不支持此功能。我写了一个简单的递归函数来保存所有引用的对象。我在让泛型发挥得很好时遇到了问题,所以最后我把它们全部删除了。我还为我的数据库对象创建了一个基本实体类。
这就是我写的。如果有人可以获得相同的代码来使用适当的泛型,或者可以改进它,请随时编辑。
// 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);
【讨论】:
感谢这位老兄。是的,我一直在考虑添加自动创建/更新外国字段的能力。然而,它不会是默认值,它将在另一个数据库事务中,而不是像 hibernate 使用的魔术连接。 其实再想一想,一般孩子都会把父母当成外来物。 Account 存在于具有外部 Account 字段的 Order 对象之前。通常,对象的自动刷新是可能的,但自动创建会很奇怪。小时。 我的理解是,唯一自动生成的列是用于外部对象的列,这意味着子对象不会自动创建对其父对象的引用。考虑到这一点,上面的代码对我来说工作得很好,尽管我已经稍微改进了泛型。最后,我使用 GSON 将 JSON 映射到一个对象,并使用同一个对象使用 ORM Lite 在本地持久化。我必须编写一些自定义 DAO 对象来加载和创建列表实体(一对多关系)。它工作得很好,但有点慢。让它成为一个内置功能会很好:)【参考方案4】:@DatabaseField(foreign = true,foreignAutoCreate = true,foreignAutoRefresh = true)
public Child child;
关于此解决方案的一些注意事项
(foreignAutoCreate = true) 仅在根据 ORMlite 文档 http://ormlite.com/javadoc/ormlite-core/com/j256/ormlite/field/DatabaseField.html 未设置 ID 字段(null 或 0)时有效
foreignAutoCreate: “将此设置为 true(默认为 false),如果未设置 ID 字段(null 或 0),将使用其内部 DAO 自动创建外部字段。”这仅在根据ORMlite documentation 将子表的 generatedId 也设置为 true 时才有效。
【讨论】:
以上是关于在 Android 上使用 ORMLite 保存嵌套的外来对象的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 上使用 OrmLite 进行 Proguard
是否可以在带有 h2 或 sybase 数据库的 android 上使用 ormlite?
如何在 android 中使用 ProGuard 和 OrmLite
在 CursorAdapter 中使用带有 ORMLite 的 Android 游标