代码改变世界

DB4O如何设置自增ID?

2011-08-18 22:18  唐郎  阅读(1785)  评论(3编辑  收藏  举报

DB4O带给了我们前所未有的ORM体验:

1. 直接存储对象

2. 直接查询, 更新, 删除对象

3. 直接支持LINQ

4. 无需创建表结构, 更无需维护数据库

5. 超小的数据库引擎, 不到800KB

6. 原生支持C#和JAVA

7. 性能高效

8. ...

看起来, 这一切都让人惊喜. 然而, 在RDBMS中最常用的一些字段设置, 却有点让人无从下手, 比如:


如何为对象增加自增ID?


Book book = new Book();
book.Id = ?;
Db4oFactory.OpenFile("Data.dat").Store(book);

DB4O手册上说, 不推荐对象使用Id属性, 但这明显是对这个问题的回避:

对某些数据应用而言, 如订单, 流水线等, 自增Id是必不可少的, 此时,如果采用DB4O作存储的话:
1. 不得不手动查询得到当前最大Id然后持久化;
2. 对于复杂对象而言, 则需要递归检查,确保每一级子对象自增Id都设置正确。。。这个是让人难以接受的。


另一种解决方案:


1. 在DB4O中存储一组 (类型 =>NextId) 对象, 用来保存每种持久化对象的Type和该类型的下一个自增Id值.

/// <summary>
/// 内部存储使用, 保存每一种类型对象的最后一个自增Id
/// </summary>
class SerialIdEntity
{
     public Type ObjectType   { get; set; }
     public int  NextId       { get; set; }
}

2. 用Attribute来标记对象的Id自增属性.

class SerialIdAttribute : Attribute
{
}

class Book
{
	[SerialId()]
	public int Id
	{      
		get; set;
	}

}

  

3. 持久化时反射检查属性是否有SerialIdAttribute, 若存在则先取出此类型的下一个自增Id, 并自增其类型对应的NextId, 然后存储.

/// <summary>
/// 保存对象: 检查自增字段
/// </summary>
/// <param name="?"></param>
public static void Add(object obj)
{
	Type t = obj.GetType();
	foreach (var p in t.GetProperties())
	{
		#region 自增属性
		SerialIdAttribute[] ids = (SerialIdAttribute[])p.GetCustomAttributes(typeof(SerialIdAttribute), false);
		if (ids.Length > 0)
		{
			SerialIdAttribute id = ids[ids.Length - 1];
			p.SetValue(obj, GenerateNexId(t), null);
		} 
		#endregion
        #region 关联子对象(含自增属性)
		//递归遍历子对象
		object subObj = p.GetValue(obj, null);                
		//...                
		#endregion	
	}

	Db4oFactory.OpenFile("Data.dat").Store(obj);
}

private static int GenerateNexId(Type t)
{
	SerialIdEntity sid;

	var res = from so in Db4oFactory.OpenFile("Data.dat").Query<SerialIdEntity>()
			  where so.ObjectType == t
			  select so;
	
	List<SerialIdEntity> data = res.ToList();
	if (data.Count > 0)
	{
		sid = data[0];
	}
	else
	{
		sid = new SerialIdEntity(){ ObjectType = t };
	}

	sid.NextId++;
	Db4oFactory.OpenFile("Data.dat").Store(sid);

	return sid.NextId;
}

同样的问题


对复杂对象而言, 对象中关联子对象, 存储时需要递归遍历检查自己的属性及子对象的属性...这个过程的开销和复杂度也几乎是无法让人接受的.  以前看到一篇文章说: 值得注意的是...我们手工提供了对象的新id, 而db4o是可以将其配置为自增字段的. 而据我说知, DB4O中的ID SYSTEM有两种. 一个是物理ID(即指向对象镜像存储位置的指针), 一个是UUID(需要在创建数据库时指定配置项), 都与自增无关。


 

关于DB4O的具体用法可以参考这里: [转] Android与db4o的完美结合:Password Manager

以下引用一段DB4O手册中关于ID System的描述:

15. IDs


The db4o team recommends, not to use object IDs where this is not necessary. db4o keeps track of object identities in a transparent way, by identifying "known" objects on updates. The reference system also makes sure that every persistent object is instantiated only once, when a graph of objects is retrieved from the database, no matter which access path is chosen. If an object is accessed by multiple queries or by multiple navigation access paths, db4o will always return the one single object, helping you to put your object graph together exactly the same way as it was when it was stored, without having to use IDs.

The use of IDs does make sense when object and database are disconnected, for instance in stateless applications.

db4o provides two types of ID systems.

15.1. Internal IDs

The internal db4o ID is a physical pointer into the database with only one indirection in the file to the actual object so it is the fastest external access to an object db4o provides. The internal ID of an object is available with

objectContainer.ext().getID(object);


To get an object for an internal ID use

objectContainer.ext().getByID(id);


Note that #getByID() does not activate objects. If you want to work with objects that you get with #getByID(), your code would have to make sure the object is activated by calling

objectContainer.activate(object, depth);


db4o assigns internal IDs to any stored first class object. These internal IDs are guaranteed to be unique within one ObjectContainer/ObjectServer and they will stay the same for every object when an ObjectContainer/ObjectServer is closed and reopened. Internal IDs will change when an object is moved from one ObjectContainer to another, as it happens during Defragment .

15.2. Unique Universal IDs (UUIDs)

For long term external references and to identify an object even after it has been copied or moved to another ObjectContainer, db4o supplies UUIDs. These UUIDs are not generated by default, since they occupy some space and consume some performance for maintaining their index. UUIDs can be turned on globally or for individual classes:

Db4o.configure().generateUUIDs(Integer.MAX_VALUE);
Db4o.configure().objectClass(typeof(Foo)).generateUUIDs(true);


The respective methods for working with UUIDs are:

ExtObjectContainer#getObjectInfo(Object)
ObjectInfo#getUUID();
ExtObjectContainer#getByUUID(Db4oUUID);