在Struts和Hibernate之间搭起桥梁

作者:Ted He;alilo(作者的blog:http://blog.matrix.org.cn/page/alilo)

摘要

Hibernate和struts是当前市面上几个最流行的开源的库之一。它们很有效率,是程序员在开发Java企业应用,挑选几个竞争的库的首选。虽然它们经常被一起应用,但是Hibernate的设计目标并不是和Struts一起使用,而Struts在Hibernate诞生好多年之前就发布了。为了让它们在一起工作,仍然有很多挑战。这篇文章点明了Struts和Hibernate之间的一些鸿沟,尤其关系到面向对象建模方面。文章也描述了如何在两者间搭起桥梁,给出了一个基于扩展Struts的解决方案。所有的基于Struts和Hibernate构建的Web应用都能从这个通用的扩展中获益。

在Hibernate in Action(Manning,2004十月)这本书里,作者Christian Bauer和Gavin King揭示了面向对象世界的模型和关系数据模型,两个世界的范例是不一致的。Hibernate非常成功地在存储层(persistence Layer)将两者粘合在一起。但是领域模型(domain model)(也就是Model-View-Controller的model layer)和HTML页面(MVC的View Layer)仍然存在不一致。在这篇文章中,我们将检查这种不一致,并且探索解决的方案。

范例不一致的再发现

让我们先看一个经典的parent-child关系例子(看下面的代码):product和category。Category类定义了一个类型为long的标示符id和一个类型为String的属性name。Product类也有一个类型为long的标示符id和一个类型为Category的属性category,表示了多对一的关系(也就是说很多product可以属于一个Category)

/**

* @hibernate.class table="CATEGORY"

*/

public class Category {

private Long id;

private String name;

/**

* @hibernate.id generator-class="native" column="CATEGORY_ID"

*/

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

/**

* @hibernate.property column="NAME"

*/

public String getName() {

return name;

}

public void setName(Long name) {

this.name = name;

}

}

/**

* @hibernate.class table="PRODUCT"

*/

public class Product {

private Long id;

private Category category;

/**

* @hibernate.id generator-class="native" column="PRODUCT_ID"

*/

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

/**

* @hibernate.many-to-one

* column="CATEGORY_ID"

* class="Category"

* cascade="none"

* not-null="false"

*/

public Category getCategory() {

return category;

}

public void setCategory(Category category) {

this.category = category;

}

}

我们希望一个product可以被更改category,所以我们的HTML提供了一个下拉框列出所有Category。

No Category

Category 1

Category 2

Category 3

这里我们看出了两者的不一致:在Product领域对象里,category属性是Category类型,但是ProductForm只有一个类型为long的categoryId。这种不匹配不但增加了不一致,而且导致了不必要的代码进行primitive type的标示符和对应的对象之间的转换。

这种不一致部分是由于HTML Form自己引起的:它只代表了一种关系模型,不能代表面向对象的模型。面向对象和关系模型的不一致在存储层由对象关系映射(O/RM)解决。但是类似的问题在表示层(view layer)仍然存在。解决的关键是让他们一起无缝地工作。

Struts的功能和局限

幸运的是,Struts能够生成和解释内嵌的对象属性。Category下拉框可以用Struts page-construction(html) tag library:

No Category

我们假设categories是Category对象的一个list。所以现在我们要修改ProductForm,让它变得更加“面向对象”,我们要修改ProductForm的categoryId,改成类型为Category的category。这种改变会导致在Product和ProductForm之间复制属性的工作更加繁琐,因为两者有相同的属性。

public class ProductForm extends ActionForm {

private Long id;

private Category category;

...

}

当我们完成剩余的Struts Action, configuration, validator, jsp, hibernate层后,开始测试,我们马上在访问ProductForm.category.id时遇到了NullPointerException。这是预料中的!因为ProductForm.category还没有被设置,同时,Hibernate也会将多对一所联系的对象引用设为空(如果database field为空指)(译者:这里指Hiberate将product.category为Null,如果该Product没有联系到任何category)。Struts要求所有的对象在显示(生成HTML Form)和传播(提交HTML FORM)之前被建立。

让我们看看如何用ActionForm.reset()来架起桥梁。

(并非如此)臭名昭著的Struts ActionForm

在我第一个星期接触Struts的时候,我最大的一个疑问就是:为什么我必须为Properties, getter方法, setter方法保持几乎完全相同的两份copy, 一份在ActionForm Bean, 一份在DomainObject。这个繁琐的步骤成了Struts社区最主要的抱怨之一。

以我的观点,ActionForm存在有原因的。首先,它们可以区别于Domain Object因为他们但当了不同的角色。在MVC模式下,Domain Object是Model层的一个部分,ActionForm是View层的。因为Webpage的Field和Database的Field可能不一样,某些特制的转换是常见的。第二,ActionForm.validate()方法可以定义非常好用的验证规则。第三,可能有其他的,特定的View行为,但是又不想在domain layer实现,特别当persistence framework来管理domain object的时候。

提交Form

让我们来用ActionForm内有的方法-reset()-来解决view和model之间的不一致。这个reset()方法是在ActionForm在被Struts Controller Servlet处理request时候复制ActionForm属性之前调用的。这个方法最常见的使用是:checkbox必须被显式地设为false,让没有被选中的checkbox被正确识别。Reset()也是一个初始化用于view rending对象的合适地方。代码看起来是这样的:

public class ProductForm extends ActionForm {

private Long id;

private Category category;

...

public void reset(ActionMapping mapping, HttpServletRequest request)

{

super.reset( mapping, request );

if ( category == null ) { category = new Category(); }

}

}

Struts在使用用户提交的值填写ProductForm之前,Struts会调用reset(),这样category属性将会被初始化。请注意,你必须检查category看它是不是null,后面我们会讨论这个。

编辑Form

到目前为止,我们已经解决了form提交时候的问题。但是当我们在生成form页面的时候呢?Html:select tag也希望有一个非空的引用,所以我们将在form生成页面之前调用reset()。我们在action类里加入了一行:

public class EditProductAction extends Action {

public final ActionForward execute( ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response ) throws Exception

{

...

Product product = createOrLoadProduct();

ProductForm productForm = (ProductForm)form;

PropertyUtils.copyProperties( productForm, product );

productForm.reset( mapping, request );

...

}

}

我假设读者已经对action类和Jakarta commons Beanutils包非常熟悉了。CreateOrLoadProduct()建立了一个新的Product实例或者从数据库里载入一个已有的实例,具体取决于这个action是建立或者修改Product的。ProductForm被赋值后(译者:也就是调用PropertyUtils.copyProperties后),productForm.category已经从Product.category复制过来了(译者:实际上只是复制了category对象引用,并没有开销),然后,ProductForm就能用来生成页面了。我们同时也必须保证:不覆盖已经被Hibernate载入的对象,所以我们必须检查(category)是不是为null。

因为reset()方法是在ActionForm中定义的,我们可以把上述代码放入一个superclass,比如CommonEditAction,来处理这些事情:

Product product = createOrLoadProduct();

PropertyUtils.copyProperties( form, product );

form.reset( mapping, request );

如果你需要一个只读的Form, 你有两个选择: 第一检查所联系的jsp对象是不是null, 第二复制domain对象到ActionForm之后调用Reset()

保存domain对象

我们解决了提交Form和生成Form页面的问题, 所以Struts可以满足了。但是Hibernate呢?当用户选择了一个null ID option – 在我们的例子中“no category”option- 并且提交form, productForm.category指向一个新建立的hibernate对象,id为null。当category属性从ProductForm复制到Hibernate控制的Product对象并且存储时,Hibernate会抱怨product.category是一个临时对象,需要在Product存储前先被存储。当然,我们知道它是Null,并且不需要被存储。所以我们需要将product.category置为Null,然后Hibernate就能存储Product了(译者:在这种情况下,数据库product.category被设成空值)。我们也不希望改变Hibernate的工作方式,所以我们选择在复制到Domain对象之前清理这些临时对象,我们在ProductForm中加了一个方法:

public class ProductForm extends ActionForm {

private Long id;

private Category category;

...

public void reset(ActionMapping mapping, HttpServletRequest request) {

super.reset( mapping, request );

if ( category == null ) { category = new Category(); }

}

public void cleanupEmptyObjects() {

if ( category.getId() == null ) { category = null; }

}

}

我们在copyProperties之前清理掉这些临时对象,所以如果ProductForm.category只是用来放Null的,则将ProductForm.category置为Null。然后Domain对象的category也会被设成null:

public class SaveProductAction extends Action {

public final ActionForward execute( ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response ) throws Exception

{

...

Product product = new Product();

((ProductForm)form).cleanupEmptyObjects();

PropertyUtils.copyProperties( product, form );

SaveProduct( product );

...

}

}

一对多关系

我还没有解决Category到Product的一对多关系。我们把它加入到Category的Metadata中:

public class Category {

...

private Set products;

...

/**

* @hibernate.set

* table="PRODUCT"

* lazy="true"

* outer-join="auto"

* inverse="true"

* cascade="all-delete-orphan"

*

* @hibernate.collection-key

* column="CATEGORY_ID"

*

* @hibernate.collection-one-to-many

* class="Product"

*/

public Set getProducts() {

return products;

}

public void setProducts(Set products) {

this.products = products;

}

}

注意:Hibernate的cascade属性为all-delete-orphan表明:Hibernate需要在存储包含的Category对象时候,自动存储Product对象。和parent对象一起存储child对象的情况并不常见,常见的是:分别控制child的存储和parent的存储。在我们的例子中,我们可以容易地做到这一点,如果我们允许用户在同一个html page编辑Category和ProductS。用set表示Products是非常直观的:

public class CategoryForm extends ActionForm {

private Set productForms;

...

public void reset(ActionMapping mapping, HttpServletRequest request) {

super.reset( mapping, request );

for ( int i = 0; i

ProductForm productForm = new ProductForm();

productForm.reset( mapping, request );

productForms.add( productForm );

}

}

public void cleanupEmptyObjects() {

for ( Iterator i = productForms.iterator(); i.hasNext(); ) {

ProductForm productForm = (ProductForm) i.next();

productForm.cleanupEmptyObjects();

}

}

}

更进一步

我们已经可以察看,编辑,提交forms,并且存储相关的objects,但是为所有的ActionForm类定义CleanupEmptyObjects()和reset()方法是个累赘。我们将用一个抽象的ActionForm来完成协助完成这些工作。

作为通用的实现,我们必须遍历所有的Hibernate管理的domain对象,发现他们的identifier,并且测试id值。幸运的是:org.hibernate.metadata包已经有两个Utility类能取出domain对象的元数据。我们用ClassMetadata类检查这个object是不是Hibernate管理的。如果是:我们把它们的id Value取出来。我们用了Jakarta Commons Beanutils包来协助JavaBean元数据的操作。

import java.beans.PropertyDescriptor;

import org.apache.commons.beanutils.PropertyUtils;

import org.hibernate.metadata.ClassMetadata;

public abstract class AbstractForm extends ActionForm {

public void reset(ActionMapping mapping, HttpServletRequest request) {

super.reset( mapping, request );

// Get PropertyDescriptor of all bean properties

PropertyDescriptor descriptors[] =

PropertyUtils.getPropertyDescriptors( this );

for ( int i = 0; i

Class propClass = descriptors[i].getPropertyType();

ClassMetadata classMetadata = HibernateUtil.getSessionFactory()

.getClassMetadata( propClass );

if ( classMetadata != null ) {   // This is a Hibernate object

String propName = descriptors[i].getName();

Object propValue = PropertyUtils.getProperty( this, propName );

// Evaluate property, create new instance if it is null

if ( propValue == null ) {

PropertyUtils.setProperty( this, propName, propClass.newInstance() );

}

}

}

}

public void cleanupEmptyObjects() {

// Get PropertyDescriptor of all bean properties

PropertyDescriptor descriptors[] =

PropertyUtils.getPropertyDescriptors( this );

for ( int i = 0; i

Class propClass = descriptors[i].getPropertyType();

ClassMetadata classMetadata = HibernateUtil.getSessionFactory()

.getClassMetadata( propClass );

if ( classMetadata != null ) {   // This is a Hibernate object

Serializable id = classMetadata.getIdentifier( this, EntityMode.POJO );

// If the object id has not been set, release the object.

// Define application specific rules of not-set id here,

// e.g. id == null, id == 0, etc.

if ( id == null ) {

String propName = descriptors[i].getName();

PropertyUtils.setProperty( this, propName, null );

}

}

}

}

}

为了让代码可读,我们省略了Exception的处理代码。

我们的新AbstractForm类从Struts的ActionForm类继承,并且提供了通用行为:reset和cleanup多对一关联对象。当这个关系是相反的话(也就是一对多关系),那么每个例子将会有所不同,类似在Abstract类里实现是比较好的办法。

总结

Struts和Hibernate是非常流行和强大的框架,他们可以有效地相互合作,并且弥补domain模型和MVC视图(view)之间的差别。这篇文章讨论一个解决Struts/Hibernate Project的通用的方案,并且不需要大量修改已经有的代码。

作者:Ted He;alilo(作者的blog:http://blog.matrix.org.cn/page/alilo)

摘要

Hibernate和struts是当前市面上几个最流行的开源的库之一。它们很有效率,是程序员在开发Java企业应用,挑选几个竞争的库的首选。虽然它们经常被一起应用,但是Hibernate的设计目标并不是和Struts一起使用,而Struts在Hibernate诞生好多年之前就发布了。为了让它们在一起工作,仍然有很多挑战。这篇文章点明了Struts和Hibernate之间的一些鸿沟,尤其关系到面向对象建模方面。文章也描述了如何在两者间搭起桥梁,给出了一个基于扩展Struts的解决方案。所有的基于Struts和Hibernate构建的Web应用都能从这个通用的扩展中获益。

在Hibernate in Action(Manning,2004十月)这本书里,作者Christian Bauer和Gavin King揭示了面向对象世界的模型和关系数据模型,两个世界的范例是不一致的。Hibernate非常成功地在存储层(persistence Layer)将两者粘合在一起。但是领域模型(domain model)(也就是Model-View-Controller的model layer)和HTML页面(MVC的View Layer)仍然存在不一致。在这篇文章中,我们将检查这种不一致,并且探索解决的方案。

范例不一致的再发现

让我们先看一个经典的parent-child关系例子(看下面的代码):product和category。Category类定义了一个类型为long的标示符id和一个类型为String的属性name。Product类也有一个类型为long的标示符id和一个类型为Category的属性category,表示了多对一的关系(也就是说很多product可以属于一个Category)

/**

* @hibernate.class table="CATEGORY"

*/

public class Category {

private Long id;

private String name;

/**

* @hibernate.id generator-class="native" column="CATEGORY_ID"

*/

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

/**

* @hibernate.property column="NAME"

*/

public String getName() {

return name;

}

public void setName(Long name) {

this.name = name;

}

}

/**

* @hibernate.class table="PRODUCT"

*/

public class Product {

private Long id;

private Category category;

/**

* @hibernate.id generator-class="native" column="PRODUCT_ID"

*/

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

/**

* @hibernate.many-to-one

* column="CATEGORY_ID"

* class="Category"

* cascade="none"

* not-null="false"

*/

public Category getCategory() {

return category;

}

public void setCategory(Category category) {

this.category = category;

}

}

我们希望一个product可以被更改category,所以我们的HTML提供了一个下拉框列出所有Category。

No Category

Category 1

Category 2

Category 3

这里我们看出了两者的不一致:在Product领域对象里,category属性是Category类型,但是ProductForm只有一个类型为long的categoryId。这种不匹配不但增加了不一致,而且导致了不必要的代码进行primitive type的标示符和对应的对象之间的转换。

这种不一致部分是由于HTML Form自己引起的:它只代表了一种关系模型,不能代表面向对象的模型。面向对象和关系模型的不一致在存储层由对象关系映射(O/RM)解决。但是类似的问题在表示层(view layer)仍然存在。解决的关键是让他们一起无缝地工作。

Struts的功能和局限

幸运的是,Struts能够生成和解释内嵌的对象属性。Category下拉框可以用Struts page-construction(html) tag library:

No Category

我们假设categories是Category对象的一个list。所以现在我们要修改ProductForm,让它变得更加“面向对象”,我们要修改ProductForm的categoryId,改成类型为Category的category。这种改变会导致在Product和ProductForm之间复制属性的工作更加繁琐,因为两者有相同的属性。

public class ProductForm extends ActionForm {

private Long id;

private Category category;

...

}

当我们完成剩余的Struts Action, configuration, validator, jsp, hibernate层后,开始测试,我们马上在访问ProductForm.category.id时遇到了NullPointerException。这是预料中的!因为ProductForm.category还没有被设置,同时,Hibernate也会将多对一所联系的对象引用设为空(如果database field为空指)(译者:这里指Hiberate将product.category为Null,如果该Product没有联系到任何category)。Struts要求所有的对象在显示(生成HTML Form)和传播(提交HTML FORM)之前被建立。

让我们看看如何用ActionForm.reset()来架起桥梁。

(并非如此)臭名昭著的Struts ActionForm

在我第一个星期接触Struts的时候,我最大的一个疑问就是:为什么我必须为Properties, getter方法, setter方法保持几乎完全相同的两份copy, 一份在ActionForm Bean, 一份在DomainObject。这个繁琐的步骤成了Struts社区最主要的抱怨之一。

以我的观点,ActionForm存在有原因的。首先,它们可以区别于Domain Object因为他们但当了不同的角色。在MVC模式下,Domain Object是Model层的一个部分,ActionForm是View层的。因为Webpage的Field和Database的Field可能不一样,某些特制的转换是常见的。第二,ActionForm.validate()方法可以定义非常好用的验证规则。第三,可能有其他的,特定的View行为,但是又不想在domain layer实现,特别当persistence framework来管理domain object的时候。

提交Form

让我们来用ActionForm内有的方法-reset()-来解决view和model之间的不一致。这个reset()方法是在ActionForm在被Struts Controller Servlet处理request时候复制ActionForm属性之前调用的。这个方法最常见的使用是:checkbox必须被显式地设为false,让没有被选中的checkbox被正确识别。Reset()也是一个初始化用于view rending对象的合适地方。代码看起来是这样的:

public class ProductForm extends ActionForm {

private Long id;

private Category category;

...

public void reset(ActionMapping mapping, HttpServletRequest request)

{

super.reset( mapping, request );

if ( category == null ) { category = new Category(); }

}

}

Struts在使用用户提交的值填写ProductForm之前,Struts会调用reset(),这样category属性将会被初始化。请注意,你必须检查category看它是不是null,后面我们会讨论这个。

编辑Form

到目前为止,我们已经解决了form提交时候的问题。但是当我们在生成form页面的时候呢?Html:select tag也希望有一个非空的引用,所以我们将在form生成页面之前调用reset()。我们在action类里加入了一行:

public class EditProductAction extends Action {

public final ActionForward execute( ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response ) throws Exception

{

...

Product product = createOrLoadProduct();

ProductForm productForm = (ProductForm)form;

PropertyUtils.copyProperties( productForm, product );

productForm.reset( mapping, request );

...

}

}

我假设读者已经对action类和Jakarta commons Beanutils包非常熟悉了。CreateOrLoadProduct()建立了一个新的Product实例或者从数据库里载入一个已有的实例,具体取决于这个action是建立或者修改Product的。ProductForm被赋值后(译者:也就是调用PropertyUtils.copyProperties后),productForm.category已经从Product.category复制过来了(译者:实际上只是复制了category对象引用,并没有开销),然后,ProductForm就能用来生成页面了。我们同时也必须保证:不覆盖已经被Hibernate载入的对象,所以我们必须检查(category)是不是为null。

因为reset()方法是在ActionForm中定义的,我们可以把上述代码放入一个superclass,比如CommonEditAction,来处理这些事情:

Product product = createOrLoadProduct();

PropertyUtils.copyProperties( form, product );

form.reset( mapping, request );

如果你需要一个只读的Form, 你有两个选择: 第一检查所联系的jsp对象是不是null, 第二复制domain对象到ActionForm之后调用Reset()

保存domain对象

我们解决了提交Form和生成Form页面的问题, 所以Struts可以满足了。但是Hibernate呢?当用户选择了一个null ID option – 在我们的例子中“no category”option- 并且提交form, productForm.category指向一个新建立的hibernate对象,id为null。当category属性从ProductForm复制到Hibernate控制的Product对象并且存储时,Hibernate会抱怨product.category是一个临时对象,需要在Product存储前先被存储。当然,我们知道它是Null,并且不需要被存储。所以我们需要将product.category置为Null,然后Hibernate就能存储Product了(译者:在这种情况下,数据库product.category被设成空值)。我们也不希望改变Hibernate的工作方式,所以我们选择在复制到Domain对象之前清理这些临时对象,我们在ProductForm中加了一个方法:

public class ProductForm extends ActionForm {

private Long id;

private Category category;

...

public void reset(ActionMapping mapping, HttpServletRequest request) {

super.reset( mapping, request );

if ( category == null ) { category = new Category(); }

}

public void cleanupEmptyObjects() {

if ( category.getId() == null ) { category = null; }

}

}

我们在copyProperties之前清理掉这些临时对象,所以如果ProductForm.category只是用来放Null的,则将ProductForm.category置为Null。然后Domain对象的category也会被设成null:

public class SaveProductAction extends Action {

public final ActionForward execute( ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response ) throws Exception

{

...

Product product = new Product();

((ProductForm)form).cleanupEmptyObjects();

PropertyUtils.copyProperties( product, form );

SaveProduct( product );

...

}

}

一对多关系

我还没有解决Category到Product的一对多关系。我们把它加入到Category的Metadata中:

public class Category {

...

private Set products;

...

/**

* @hibernate.set

* table="PRODUCT"

* lazy="true"

* outer-join="auto"

* inverse="true"

* cascade="all-delete-orphan"

*

* @hibernate.collection-key

* column="CATEGORY_ID"

*

* @hibernate.collection-one-to-many

* class="Product"

*/

public Set getProducts() {

return products;

}

public void setProducts(Set products) {

this.products = products;

}

}

注意:Hibernate的cascade属性为all-delete-orphan表明:Hibernate需要在存储包含的Category对象时候,自动存储Product对象。和parent对象一起存储child对象的情况并不常见,常见的是:分别控制child的存储和parent的存储。在我们的例子中,我们可以容易地做到这一点,如果我们允许用户在同一个html page编辑Category和ProductS。用set表示Products是非常直观的:

public class CategoryForm extends ActionForm {

private Set productForms;

...

public void reset(ActionMapping mapping, HttpServletRequest request) {

super.reset( mapping, request );

for ( int i = 0; i

ProductForm productForm = new ProductForm();

productForm.reset( mapping, request );

productForms.add( productForm );

}

}

public void cleanupEmptyObjects() {

for ( Iterator i = productForms.iterator(); i.hasNext(); ) {

ProductForm productForm = (ProductForm) i.next();

productForm.cleanupEmptyObjects();

}

}

}

更进一步

我们已经可以察看,编辑,提交forms,并且存储相关的objects,但是为所有的ActionForm类定义CleanupEmptyObjects()和reset()方法是个累赘。我们将用一个抽象的ActionForm来完成协助完成这些工作。

作为通用的实现,我们必须遍历所有的Hibernate管理的domain对象,发现他们的identifier,并且测试id值。幸运的是:org.hibernate.metadata包已经有两个Utility类能取出domain对象的元数据。我们用ClassMetadata类检查这个object是不是Hibernate管理的。如果是:我们把它们的id Value取出来。我们用了Jakarta Commons Beanutils包来协助JavaBean元数据的操作。

import java.beans.PropertyDescriptor;

import org.apache.commons.beanutils.PropertyUtils;

import org.hibernate.metadata.ClassMetadata;

public abstract class AbstractForm extends ActionForm {

public void reset(ActionMapping mapping, HttpServletRequest request) {

super.reset( mapping, request );

// Get PropertyDescriptor of all bean properties

PropertyDescriptor descriptors[] =

PropertyUtils.getPropertyDescriptors( this );

for ( int i = 0; i

Class propClass = descriptors[i].getPropertyType();

ClassMetadata classMetadata = HibernateUtil.getSessionFactory()

.getClassMetadata( propClass );

if ( classMetadata != null ) {   // This is a Hibernate object

String propName = descriptors[i].getName();

Object propValue = PropertyUtils.getProperty( this, propName );

// Evaluate property, create new instance if it is null

if ( propValue == null ) {

PropertyUtils.setProperty( this, propName, propClass.newInstance() );

}

}

}

}

public void cleanupEmptyObjects() {

// Get PropertyDescriptor of all bean properties

PropertyDescriptor descriptors[] =

PropertyUtils.getPropertyDescriptors( this );

for ( int i = 0; i

Class propClass = descriptors[i].getPropertyType();

ClassMetadata classMetadata = HibernateUtil.getSessionFactory()

.getClassMetadata( propClass );

if ( classMetadata != null ) {   // This is a Hibernate object

Serializable id = classMetadata.getIdentifier( this, EntityMode.POJO );

// If the object id has not been set, release the object.

// Define application specific rules of not-set id here,

// e.g. id == null, id == 0, etc.

if ( id == null ) {

String propName = descriptors[i].getName();

PropertyUtils.setProperty( this, propName, null );

}

}

}

}

}

为了让代码可读,我们省略了Exception的处理代码。

我们的新AbstractForm类从Struts的ActionForm类继承,并且提供了通用行为:reset和cleanup多对一关联对象。当这个关系是相反的话(也就是一对多关系),那么每个例子将会有所不同,类似在Abstract类里实现是比较好的办法。

总结

Struts和Hibernate是非常流行和强大的框架,他们可以有效地相互合作,并且弥补domain模型和MVC视图(view)之间的差别。这篇文章讨论一个解决Struts/Hibernate Project的通用的方案,并且不需要大量修改已经有的代码。


相关内容

  • 三大主流框架的优缺点
  • 三大主流框架Hibernate.Struts和Spring优缺点(转帖) 2010-07-27 11:24:54| 分类: 星宇沙龙 |字号 订阅 本文重要一一向大家介绍Hibernate.Struts和Spring优缺点,下面给大家简单加以说明,希望对大家的学习和工作有所帮助. 1.struts ...

  • java三大框架框架与新技术
  • java 三大框架框架与新技术 1.谈谈你对Struts 的理解. 答: 1. struts是一个按MVC 模式设计的Web 层框架,其实它就是一个大大的servlet ,这个Servlet 名为ActionServlet ,或是ActionServlet 的子类.我们可以在web.xml 文件中将 ...

  • SSH优缺点
  • Struts,Spring,Hibernate优缺点 Struts跟Tomcat.Turbine等诸多Apache项目一样,是开源软件,这是它的一大优点.使开发者能更深入的了解其内部实现机制. Struts开放源码框架的创建是为了使开发者在构建基于Java Servlet和JavaServer Pa ...

  • 三大框架的原理及优缺点
  • Struts 的原理和优点. Struts 工作原理 MVC 即Model-View-Controller 的缩写,是一种常用的设计模式.MVC 减 弱了业务逻辑接口和数据接口之间的耦合,以及让视图层更富于变化.MVC 的 工作原理, 如下图1所示: Struts 是MVC 的一种实现,它将Serv ...

  • SSH三大组件各自的优缺点简述
  • SSH三大组件各自的优缺点简述 Struts的原理和优点: Struts工作原理 MVC即Model-View-Controller的缩写,是一种常用的设计模式.MVC 减弱了业务逻辑接口和数据接口之间的耦合,以及让视图层更富于变化.MVC的工作原理,如下图1所示:Struts 是MVC的一种实现, ...

  • 三大框架的作用
  • 一.Spring Spring是一个解决了许多在J2EE开发中常见的问题的强大框架. Spring提供了管理业务对象的一致方法并且鼓励了注入对接口编程而不是对类编程的良好习惯.Spring的架构基础是基于使用JavaBean属性的Inversion of Control容器.然而,这仅仅是完整图景中 ...

  • 研究生管理系统论文
  • 摘要 大学的研究生教学管理是一项重要而又繁重的工作,而学院级研究生教学管理又是学校研究生教学管理的基础,是沟通学校管理部门与师生的桥梁,是各种数据信息处理的中心.因此如何提高研究生教学管理水平,如何开发符合教学实际应用的全面.综合.规范的研究生管理系统成了研究生教学管理工作的大势所趋. 本文针对研究 ...

  • 国信蓝点杯课程-参加蓝点必看
  • 课 程 课程内容 Java基础 深入理解计算机系统,了解软件开发行业和软件 开发工作各种常用编程语言的认识.Java语言 的历史.特点.和用途. Java开发环境的搭建,包括操作系统初步培训目标 通过本部分内容的学习,掌握计算机的深入使用和对软件开发的深入认识和理解.掌握Java 编程中的基本语法规 ...

  • 岗前实训总结报告
  • 计算机与信息工程系 岗前实训总结报告 实 训 方 向: 软件开发实训 所 属 班 级: 学 号: 姓 名: 主 训 教 师: 实 训 时 间: 2010-9-6-2011-1-8 二0 一一年一月八日 一.实训综述 1.实训地点 文理楼B518 2.实训性质.目的及意义 实训性质: 这次java方向 ...