Java海阔天空

编程是我的生活,但生活不仅仅是编程。

基于SSH2框架构建JavaEE应用程序(2)

四、数据传输、数据模型与Dozer

数据传输是程序员实现各种功能时刻需要考虑的问题,从数据模型的建立,到数据模型的转换,从数据的合法性验证,到数据类型的转化,我们要时刻小心,精心设计与组织。数据模型与数据传输可简单可复杂,完全取决于设计者的经验与意图,当然,项目的规模也是我们应该考虑的因素,一个小型项目实在没必要将问题复杂化。


我们首先考虑数据从视图(View)传输到数据库(DB)的数据模型变化过程。用户从界面输入数据,此时,数据毫无结构可言,是零散的、无组织的,数据提交到控制器后,一方面考虑到OOP的严谨性,另一方面考虑到数据的封装,会将零散的无组织的数据封装成Value Object(简称VO)对象,VO就是JavaBean,并传送到业务类中,在数据模型比较复杂的情况下,业务类的方法参数和返回值都应该是VOVO的集合,VO转换成POPersistence Object)后传送到DAODAO调用Hibernate API持久化数据。简单来说,业务类对外暴露的数据模型是VODAO对外暴露的数据模型是PO


数据从数据库传递到视图层的数据模型变化恰恰相反,Hibernate将数据库中的记录转换成POPO传递给业务类后转换成VOVO被传送到视图层进行显示处理。


以上转换过程如下图:



 

为什么同时需要POVO?前面说过,这不是必须的,如果项目比较小,直接使用PO就行了。但PO有如下缺点:


PO反应了数据库的物理模型,向外暴露PO存在一定的风险;

PO过于僵化,无法适应变化莫测的业务需求;

PO在持久化状态下与数据库同步,可能导致数据意外修改;

PO将导致程序缺乏健壮性。


VO没有PO的缺点,相对而言,VO更加灵活,能适应各种需求的变化,下面是POVO的区别:


VO是用new关键字创建,由GC回收的。PO则是向数据库中添加新数据时创建,删除数据库中数据时削除的。并且它只能存活在一个数据库连接中,断开连接即被销毁;

VO是值对象,精确点讲它是业务对象,是存活在业务层的,是业务逻辑使用的,它存活的目的就是为数据提供一个生存的地方。PO则是有状态的,每个属性代表其当前的状态。它是物理数据的对象表示。使用它,可以使我们的程序与物理数据解耦,并且可以简化对象数据与物理数据之间的转换;

VO的属性是根据当前业务的不同而不同的,也就是说,它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。PO的属性是跟数据库表的字段一一对应的;

PO一般只有一个,但对应的VO可能有多个。


我们举一个简单的例子来说明VOPO的区别:比如要实现用户注册与登陆的功能,在物理模型中创建一个用户表,字段分别为用户ID(标识列)、用户名、密码、注册日期等等,定义PO时应该有这四个字段的映射属性。现在,我们来实现用户注册这一功能,为了接收用户输入的注册信息,必须定义VO,注册用户需要输入的信息有:用户名、密码1、密码2,这正好是VO的属性。而实现用户登陆功能时,用户需要输入的信息只有用户名和密码,所以,该VO的属性只有两个:用户名、密码。可以看出,PO侧重于物理模型,而VO则更关注用户的实际需求。如下图所示:



 


因为数据传输是双向的,所以VOPO之间存在相互转换的问题,这会给编程带来麻烦,我们总是要通过getter方法取出数据,再通过setter方法给对方属性赋值,在属性很多的情况下,让人深感繁琐,而Dozer工具能简化这个问题。


Dozerhttp://dozer.sourceforge.net/)是一个JavaBean映射工具,能实现对象属性值之间的相互赋值,Dozer支持简单类型映射、复合类型映射、双向映射以及递归映射,默认情况下,Dozer能实现类型相同、名字相同的属性之间的赋值,如果属性名不同,则必须在xmldozerBeanMapping.xml)配置文件中指定,如果不必为JavaBean的每个属性赋值,也可以在xml中指定。Xml的定义可以参考http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd,典型的结构如下:


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mappings PUBLIC "-//DOZER//DTD MAPPINGS//EN"

"http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd">

<mappings>

<configuration>

<stop-on-errors>false</stop-on-errors>

<date-format>MM/dd/yyyy HH:mm</date-format>

<wildcard>true</wildcard>

</configuration>

<mapping>

<class-a>com.denny_blue.dozerdemo.Book</class-a>

<class-b>com.denny_blue.dozerdemo.CookBook</class-b>

<field>

<a>name</a>

<b>bookName</b>

</field>

<field>

<a>author</a>

<b>author</b>

</field>

</mapping>

</mappings>


<class-a>指定所要复制的源对象,<class-b>复制的目标对象,<a>源对象的属性名, <b>目标对象的属性名。wildcard默认为true,在此时默认对所有属性进行map,如果为false,则只对在xml文件中配置的属性进行map


其中,BookCookBook类分别定义如下:


public class Book{

public Book(){

  

public void setAuthor(String author) {

this.author = author; 

}

public String getAuthor() {

return (this.author); 

}

public void setName(String name){

this.name=name;

public String getName(){

return this.name;

}

}

public class CookBook {

private String bookName;

private String author;

public CookBook(){}

public String getBookName() {

return (this.bookName); 

}

public void setBookName(String bookName) {

this.bookName = bookName; 

}

public String getAuthor() {

return (this.author); 

}

public void setAuthor(String author) {

this.author = author; 

}

}


以下是测试代码:


Book book1=new Book();

book1.setAuthor("dennis");

book1.setName("dozer demo");

DozerBeanMapper mapper=new DozerBeanMapper();

book2=(Book)mapper.map(book1,com.denny_blue.dozerdemo.Book.class);

CookBook cookBook=new CookBook();

List myMappingFiles = new ArrayList();

myMappingFiles.add("dozerBeanMapping.xml");

mapper.setMappingFiles(myMappingFiles);

cookBook=(CookBook)mapper.map(book1,CookBook.class);

System.out.println("cookBook's name:"+   cookBook.getBookName()+"     cookBook's author:"+

  cookBook.getAuthor());

}


通过mapper.setMappingFiles()设置映射文件,可以添加多个配置文件,也可以把所有的映射写在一个配置文件里面。这里介绍的只是最基本的使用方法,为了实现Dozer的模块化应用,我专门写了一个VoPoConverter类简化Dozer的调用。


package com.aptech.util;

import java.util.ArrayList;

import java.util.List;

import org.dozer.DozerBeanMapper;

import org.dozer.Mapper;

/**

 * VOPO相互转换的类

 */

public class VoPoConverter {

/**

 * VOPO之间相互转换,将源对象的同名属性复制目标对象中

 * 前提:源对象和目标对象都必须存在

 * @param src 源对象

 * @param desc 目标对象

 */

public static void copyProperties(Object src, Object desc){

if(src == null) return;

Mapper mapper = new DozerBeanMapper();

mapper.map(src, desc);

}

/**

 * VOPO之间相互转换,先创建对象,再将源对象的同名属性复制目标对象中

 * @param <T> 目标类型

 * @param src 源对象

 * @param descType 目标类型

 * @return

 */

public static <T> T copyProperties(Object src, Class<T> descType){

if(src == null) return null;

Mapper mapper = new DozerBeanMapper();

return mapper.map(src, descType);

}

/**

 * 将源集合转换为目标集合,注意:目标集合是新建的

 * @param <T>

 * @param srcList 源集合

 * @param descType 目标集合中元素的类型

 * @return

 */

public static <T> List<T> copyList(List srcList, Class<T> descType){

if(srcList == null) return null;

List<T> descList = new ArrayList<T>();

for(Object obj : srcList){

T t = VoPoConverter.copyProperties(obj, descType);

descList.add(t);

}

return descList;

}

}


类名的意思虽然叫VOPO转换器,实际上可以应用在任何场合。如果要配合xml配置文件,该类还需要做一些修改。


——作者:李赞红 (lifenote@21cn.com),转载请保留版权!

posted on 2011-02-21 11:43 李赞红 阅读(3040) 评论(3)  编辑  收藏

评论

# re: 基于SSH2框架构建JavaEE应用程序(2)[未登录] 2011-02-21 14:05 littleJava

基于SSH2框架构建JavaEE应用程序 1 2 两部分写的很精彩。关于po vo的区别我以前也有疑问,但是从来没有像楼主写的这么细致,谢谢!后面关于Dozer的介绍可以单独写一篇文章,写在这里就有些喧宾夺主了。  回复  更多评论   

# re: 基于SSH2框架构建JavaEE应用程序(2) 2011-02-21 16:04 窝窝影视

不错 就是看着有点乱!  回复  更多评论   

# re: 基于SSH2框架构建JavaEE应用程序(2) 2011-02-21 17:31 jackerxff

好像就是推广Dozer的,实际完全没有必要这样,使PO映射表,VO映射视图,PO同样可以new ,PO的映射同样也可以相当的灵活:查询使用视图VO,添加更新使用PO(添加的PO就是new的,更新使用id先get出来,更新后的对象(也是new的)利用反射将值注入get出来的对象中,再update),删除只需一个id即可,无论PO还是VO都可以比较灵活通过@Transient提供的离线字段。。。。就先简单说点
  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

导航

<2011年2月>
303112345
6789101112
13141516171819
20212223242526
272812345
6789101112

统计

常用链接

留言簿(12)

随笔档案(28)

相册

技术友情博客

搜索

最新评论

阅读排行榜

评论排行榜