在j2ee中实现一般java对象数据库的方法。

大家都知道,Hibernate, ibatis等都是用来存储“数据对象”的,你必须显示地指明每个属性对应于哪个数据库字段。

似乎并没有支持数据库存储“一般”Java对象的一个框架。在j2se中,我们用Serilizable接口来实现java对象存储到一个流,j2ee对于stateful Bean的序列化就用了这个方法。

我现在需要在自己的一个应用框架中实现这样一个功能:
1。Java对象按binary方式存储到数据库(Serializable接口),每个对象一条记录。
2。允许选择个别对象属性进行索引查询。
3。支持修改,删除,查询对象操作。
4。不需要Cache。
5。给定ClassLoader作为重新载入对象的参数。

我觉得“Java对象按binary方式存储到数据库”似乎和“允许选择个别对象属性进行索引查询”是冲突的。

Java对象二进制流中不会有属性信息,只需要知道class就可以了。所以给从一个Java对象二进制流是得不到属性信息的,只有这个对象的类才知道怎么还原这些信息。外部程序也就无从查询了。

为什么你需要这样的功能,可以说说需求么?

需求是:

我要管理一个对象集合,每个对象的Class都是运行时候决定的,专门有一个数据库管理jar文件,ClassLoader必须运行时动态创建,然后用该ClassLoader创建对象。


我要将这些对象存入对象数据库,对个别属性进行索引。所以基本的想法是用Seriliazable接口将整个对象存成Binary字段,然后对要索引的属性也建立字段。这样当然会有部分数据冗余,不过问题不大。

well, this is supported by JDO.

In Libertor JDO, you could do the following:

1. Define a class, for example. Foo.java
2. In Foo.java, declares two fields:
String className // searchable
Object objectInstance

You could assign a serializableobject to objectInstance field. Then When you persist class Foo, JDO will persist objectInstance field as a binary blob. Also, the objectInstace field is updatable.

Of course, the instance you want to persist must implments serializable interface.

My 2 cents

Thanks, does JDO support to use a ClassLoader to deserialize the object? I wrote the following ObjectInputStream to use a "dynamic class loader".


class ObjectInputStreamWithLoader extends ObjectInputStream
{
private ClassLoader loader;

/**
* Loader must be non-null;
*/


public ObjectInputStreamWithLoader(InputStream in, ClassLoader loader)
throws IOException {

super(in);
if (loader == null) {
throw new IllegalArgumentException(
"Illegal null argument to ObjectInputStreamWithLoader");
}
this.loader = loader;
}

/**
* Make a primitive array class
*/


private Class primitiveType(char type) {
switch (type) {
case 'B': return byte.class;
case 'C': return char.class;
case 'D': return double.class;
case 'F': return float.class;
case 'I': return int.class;
case 'J': return long.class;
case 'S': return short.class;
case 'Z': return boolean.class;
default: return null;
}
}

/**
* Use the given ClassLoader rather than using the system class
*/

protected Class resolveClass(ObjectStreamClass classDesc)
throws IOException, ClassNotFoundException {

String cname = classDesc.getName();
if (cname.startsWith(
"[")) {
// An array
Class component;
// component class
int dcount;
// dimension
for (dcount=1; cname.charAt(dcount)=='['; dcount++) ;
if (cname.charAt(dcount) == 'L') {
component = loader.loadClass(cname.substring(dcount+1,
cname.length()-1));
} else {
if (cname.length() != dcount+1) {
throw new ClassNotFoundException(cname);
// malformed
}
component = primitiveType(cname.charAt(dcount));
}
int dim[] = new int[dcount];
for (int i=0; i<dcount; i++) {
dim[i]=0;
}
return Array.newInstance(component, dim).getClass();
} else {
return loader.loadClass(cname);
}
}
}

Does JDO fit my requirements?

1. load Foo.className
2. Use the className to look up the "jar file"
3. Create a classloader from the "jar file".
4. Create a ObjectInputStreamWithLoader
5. Use the ObjectInputStreamWithLoader to deseriliaze the Foo.objectInstance.

Does JDO fit my requirements?

1. load Foo.className YES
2. Use the className to look up the "jar file" Why need this?
3. Create a classloader from the "jar file". Why need this?
4. Create a ObjectInputStreamWithLoader Why need this?
5. Use the ObjectInputStreamWithLoader to deseriliaze the .objectInstance. Why need this?


Samples:

pakcage com.foo;

public class Student implments Serializable{
......
}


public class StudentObj{
private Sting classname;
private Object student;

public StudentObj( final String name,
final Object student ){
this.classname = name;
this.student = student;
}

public Object getStudent(){
return this.student;
}

}


In Liberator JDO, define StudentObj as a perisstencecapable class,persist the StudentObj like this;

persistenceManager.currentTrasaction().begin();
persistenceManager.makePersistent( studentObj );
persistenceManager.currentTrasaction().commit();

The above code will persist studentObj and all its fields, including student as a binary blob.

Whe you use JDO to load sutdentObj, just call getStudent() you will get a student instance( not a class ). You don't need to use classloader to load and create a student instance.

> Does JDO fit my requirements?
>
> 1. load Foo.className YES
> 2. Use the className to look up the "jar file" Why
> need this?
> 3. Create a classloader from the "jar file". Why need
> this?
> 4. Create a ObjectInputStreamWithLoader Why need
> this?
> 5. Use the ObjectInputStreamWithLoader to deseriliaze
> the .objectInstance. Why need this?
>
>
> Samples:
>
> pakcage com.foo;
>
> public class Student implments Serializable{
> ......
> }
>
>
> public class StudentObj{
> private Sting classname;
> private Object student;
>
> public StudentObj( final String name,
> final Object student ){
> this.classname = name;
> this.student = student;
> }
>
> public Object getStudent(){
> return this.student;
> }
>
> }
>
>
> In Liberator JDO, define StudentObj as a
> perisstencecapable class,persist the StudentObj like
> this;
>
> persistenceManager.currentTrasaction().begin();
> persistenceManager.makePersistent( studentObj );
> persistenceManager.currentTrasaction().commit();
>
> The above code will persist studentObj and all its
> fields, including student as a binary blob.
>
> Whe you use JDO to load sutdentObj, just call
> getStudent() you will get a student instance( not a
> class ). You don't need to use classloader to load
> and create a student instance.


我猜persistenceManager.makePersistent( studentObj );肯定是用当前系统ClassLoader,不适合我的要求,在我的系统中,objectInstance的Class和所在的Jar文件是有专门管理的,必须动态创建ClassLoader。

you could dynamically create a classloader and set it to the context class loader of current thread and use your own loader to load everything including JDO, then ...

But that's your own business logic. JDO does its work: Persist objects and their fields, even the fields are Object.class type or an interfact type that implements Serializable.

to 廉价劳力:现在看来你的需求中最麻烦的就是:5、给定ClassLoader作为重新载入对象的参数。

在Weblogic Server中有这样的功能,就是在redeploy web app的时候,可以做到不丢失session信息。它的做法是把HttpSession保存起来,redeploy后再重新恢复。我猜想这种情形和你的需求比较类似,就是在恢复对象事例的时候采用的类定义和保存时候的不同。

实现这种需求的难度取决于什么时候才能知道需要使用哪个类定义,如果程序运行之前就知道,那么只需要更改classpath就可以了;极端的情况是直到恢复对象实例之前才能够知道(例如相关信息和对象事例保存在一起),并且在同一个JVM里面对于同一个class name存在不同的定义。

你的ObjectInputStreamWithLoader代码试图解决用不同ClassLoader的问题,我觉得是可行的,只是对于是否有O/R mapping方案能够更改ObjectInputStream不乐观。也许mustang_to的例子需要更改为:


class DynamicObjcet {
private String className;
private String myindex; // searchable parameter
byte[] objectInstance;

void setObject(Objcet obj);
Objcet getObjcet();
}

byte[]->object的转换必须自己来控制。

但是还有另外一个问题――不同ClassLoader并不意味着就能够得到不同的类定义,因为在Java 1.2后,java class的加载采用所谓的委托模式(Delegation Modle),当调用一个ClassLoader.loadClass()加载一个类的时候,将遵循以下的步骤:
1)检查这个类是否已经被加载进来了?
2)如果还没有加载,调用父对象加载该类
3)如果父对象无法加载,调用本对象的findClass()取得这个类。

所有需要保存的对象都不能存放在classpath中,也不能出现在任何的引用中,这样才能避免该类没有被SystemClassLoader所加载。这个限制会对你的程序架构有较大的影响,需要事先考虑清楚。

我以前写过一篇关于classloader的入门文章,可以参考一下。