论坛首页 Java版

关于bean-to-bean mapping

浏览 5410 次
该帖已经被评为精华帖
作者 正文
时间:2006-10-22
被投为新手贴了.这里重新发一下.主要是做一个BeanUtils不支持的复制整个java bean对象树的库.


其实说起来,我要的功能并不复杂。BeanUtils只能处理String, int之类的转换,否则property的类型必须一致才行。

而我遇到的需求,是从一个对象图转换到另一个对象图。比如:
class Person{
  private String name;
  private Date birthdate;
   //getters and setters
}
class JobCategory{
  private String category;
  //getter and setter
}
class Adult extends Person {
  private Adult spouse;
  private Person[] kids
  private JobCategory jobCategory;
  //getters and setters
}
class PersonBean{
  private String name;
  private Date birthdate;
   //getters and setters
}

class JobCategoryBean{
  private String categoryName;
  //getter and setter
}
class AdultBean extends PersonBean {
  private String name;
  private Date birthdate;
  private JobCategoryBean jobCategory;
  private AdultBean spouse;
  private List kids;
  //getters and setters
}



假设AdultBean, PersonBean是从XMLBeans自动代码生成出来的,现在要把这些东西转换成Adult, Person, JobCategory这种对象树。

我理想的情况是,
AdultBean bean = ...;
Adult adult = new Adult();
BeanUtils.copy(adult, bean);

然后所有的name, birthdate, jobcategory, spouse, kids都自动转换好了。不能手工转换么?当然能,问题是,当我们每个bean class有二十多个property,有七八种这种bean class,有从axis到xmlbeans,从xmlbeans到业务bean,从甲层到乙层等等等等的转换任务时,这种转换就是一种对程序员的摧残了。

我还以为这个需求不是特别特殊,应该有人遇到过的呢。

当然,我上面举的例子因为JobCategory.name和JobCategoryBean.categoryName这两个property明子不匹配,更一般地说,不是每个property都是那么一对一的,很可能有其它的匹配的不那么整齐的情况发生。

对这种情况,我的解决方法是写一个conversion class:
class AdultConversion{
  public static void convert(JobCategory cat, JobCategoryBean bean){
    cat.setName(bean.getCategoryName());
  }
}

然后把这个conversion class传递给Beans这个facade:
AdultBean bean = ...;
Adult adult = new Adult();
Beans.copy(adult, bean, new AdultConversion());

Beans会分析AdultConversion的meta data,发现存在一个客户自定义的从JobCategoryBean到JobCategory的转换方法,于是转换仍然可以成功。

目前编码和测试基本完成,即将进入production乐。

居然没人遇到过这个问题么?考虑是否把这个咚咚开源中.
   
时间:2006-10-27
没人关心么?

还是大家还是坚持认为这是一个初学贴,只是给俺留点面子没有再次投票?


今天google,发现还是有人做这方面的东西的。而且从回复来看,大家也都认为这是一个实实在在存在的问题呀。
http://www.theserverside.com/news/thread.tss?thread_id=38898

只不过目前相关的工具还比较少。jxpath不错,不过不能完全解决这个问题。Dozer和otom似乎是这个领域唯二的两个工具。
我不是很喜欢Dozer的xml配置,对otom的代码生成也有点保留。

我目前的解决方法是:
Beans.map(target, "personName<=name, hobby<=likes", src);


底层仍然用BeanUtils,不过增加了递归支持,增加了自定义转换,增加了简单的property name mapping。

觉得比写xml配置舒服。


如果我把这个东西开源,有人感兴趣一块儿作么?
   
0 请登录后投票
时间:2006-10-27
偶上次不是已经答复过你了么,用ognl可以很好的解决,现成的util有:com.opensymphony.xwork.util.OgnlUtil.copy,嫌它依赖太多的话,自己写一个也就是几十行代码的事情阿。
   
0 请登录后投票
时间:2006-10-27
Readonly说的没错,其实xwork已经提供了这样的一个转换框架,思路感觉跟ajoo描述的是一样的。不过有个xx_conversion.propertes看着讨厌,可以自己开发一个。
ajoo的方案还有很多事情要做?
ajoo的说的很清晰,学习,论坛有bug,晚点再投个精华帖
另外我在想,既然总有xxbean->yybean的这种数据的传递。JavaBean可以定义get,set方法叫做规范。干脆也定义个bean之间的命名规则好了。这样用户一行代码也不写。
   
0 请登录后投票
时间:2006-10-27
这方面需求的确碰得很少,最多就是自己重构时的内部一些转换。针对其他人开发的遗留系统的大批量转换我没有遇见过,但是肯定也会存在。

ajoo 写道
我目前的解决方法是:
Beans.map(target, "personName<=name, hobby<=likes", src);




就你的代码示例来看,如果两个对象树中,处于不同层次的属性拷贝,似乎不能处理了,比如

user.person.name <= user.firstName

直觉上,readonly说的ognl来处理更加合适一些。

ognl:user.person.name=user.firstName,user.person.name

意思是:
1. a, b 读取a, 写入b
2. b=a 把a赋值给b
   
0 请登录后投票
时间:2006-10-27
Readonly 写道
偶上次不是已经答复过你了么,用ognl可以很好的解决,现成的util有:com.opensymphony.xwork.util.OgnlUtil.copy,嫌它依赖太多的话,自己写一个也就是几十行代码的事情阿。

ognl和beanutil没有本质区别亚。

我要的是能够任意定制对象树上的任意节点的转换逻辑的(比如一边是name,另一边是通过某个算法计算someAlgorithm(firstname, lastname)这种)。并不是简简单单的做一个property名字映射就能全部搞定的。

你要是能几十行代码搞定我倒是一定要瞧瞧的。
   
0 请登录后投票
时间:2006-10-27
sorphi 写道
这方面需求的确碰得很少,最多就是自己重构时的内部一些转换。针对其他人开发的遗留系统的大批量转换我没有遇见过,但是肯定也会存在。

ajoo 写道
我目前的解决方法是:
Beans.map(target, "personName<=name, hobby<=likes", src);




就你的代码示例来看,如果两个对象树中,处于不同层次的属性拷贝,似乎不能处理了,比如

user.person.name <= user.firstName

直觉上,readonly说的ognl来处理更加合适一些。

ognl:user.person.name=user.firstName,user.person.name

意思是:
1. a, b 读取a, 写入b
2. b=a 把a赋值给b


对这个我的方法是:
Beans.copy(target, src, new Object(){
  public void convert(TargetUser target, SourceUser user){
    targetUser.getPerson().setName(user.getFirstName());
    ...
  }
});


或者,还可以用jxpath来更方便地写。

这个和直接写setXXX(getYYY())有什么区别?区别在于,只需要少量定制一些特殊的mapping在这个converer对象里面,而名字相同的property,或者仅仅是名字不同的property都是直接自动搞定。




我这个方法的好处是灵活,可以放任意的业务逻辑在转换器中,而不是仅仅局限在property name mapping上。

当然,能支持"user.person.name <= user.firstName"的语法的话也是不错的特性。毕竟非常方便。

也许应该考虑支持这个功能。
   
0 请登录后投票
时间:2006-10-27
改一下标题
让标题说明一些东西....
如果还是这样
被当成新手贴也是早晚的事
   
0 请登录后投票
时间:2006-10-27
ajoo 写道
Readonly 写道
偶上次不是已经答复过你了么,用ognl可以很好的解决,现成的util有:com.opensymphony.xwork.util.OgnlUtil.copy,嫌它依赖太多的话,自己写一个也就是几十行代码的事情阿。

ognl和beanutil没有本质区别亚。

我要的是能够任意定制对象树上的任意节点的转换逻辑的(比如一边是name,另一边是通过某个算法计算someAlgorithm(firstname, lastname)这种)。并不是简简单单的做一个property名字映射就能全部搞定的。

你要是能几十行代码搞定我倒是一定要瞧瞧的。


ajoo 写道

我这个方法的好处是灵活,可以放任意的业务逻辑在转换器中,而不是仅仅局限在property name mapping上。


ognl的表达式当然不仅仅是property这么简单丫

ognl: new.name = someAlgorithm(old.firstname, old.lastname), new.name

不清楚jxpath,关于ognl实在是太强大了,operators就不说了,表达式支持见附件(屏考了ognl的文档目录)。这些表达式应该能够达到你描述的转换器的功能了。


一个ognl版本的beans transformation 的API应用场景:

BeansTransform.prepare(target, "target", src, "src")  //产生一个ognl 的 root context, 表达式"target"指向target实例,"src"指向src实例。或许可以准备更多的参与者。
     .map(TransformPolicy.PlainPropertiesCopy) //缺省,拷贝所有同名同级同类型属性
      .map("target.newName=src.oldName")    //属性复制
      .map("target.age=@Converter@Aconvert(src.birthdate)") //特殊逻辑转换复制
         //... 更多复杂的转换映射
      .excute(); //执行所有映射的ognl表达式、策略等

  • 6d3e36d6-5a4e-4794-aa01-c14456f0db34-thumb
  • 描述:
  • 大小: 50.8 KB
  • 7d3e2b7b-0428-48c1-ae13-2de2afab94e9-thumb
  • 描述: ognl 2.6.7 toc
  • 大小: 50.8 KB
   
0 请登录后投票
时间:2006-10-28
恩。低估ognl了。

问几个问题撒:
1。这个BeanTransform类我怎么没找到?和readonly说的OgnlUtil.copy有什么联系?我怎么没看到OgnlUtil.copy能够做这个BeanTransform例子里面做的事情?
2。ognl怎么处理对象创建?obj1.a.b.c = obj2.c.d.e的时候,obj1.a如果是null,ognl是否先创建实例给.a, .a.b?怎么创建?能否定制?
3。给的这个例子是纯粹的所谓context based。就是说,给定一个对象,从根部指定property或者property的property的映射规则。有没有type based?就是说只定义类型之间的转换规则,这个规则通用(除非在context based里面明确制定)

我暂时还是对把大量的转换逻辑用ognl来表达有点犹豫。这样refactor, type checking, auto complete的好处就都没了。
而我的方法就相对来说没这么革命性。复杂的转换逻辑还是在一个java类里面写java代码,除非这个逻辑简单到纯粹名字映射。这样损失的静态类型检查和重构能力就不是很多。

暂时就想到这么多。不过ognl看来明显也是做bean-to-bean mapping的一个有力工具。尤其是项目中不敢用groovy, jruby而又眼馋scripting的时候,倒是一个简洁实现业务逻辑的好的替代品。
   
0 请登录后投票
论坛首页 Java版

跳转论坛: