由于想要addChildCategory()成为子类中唯一外部可见的存储器方法(mutator method)(可能还要加上removeChildCateogry()方法),你可以让setChildCategories()方法为私有,或者删除它,。
在Category和Item类之间存在着一种不同的关系:一个双向的多对多关联,如图3-4所示.
对于多对多的关联,两侧都用集合值属性实现。添加访问Item关系的新属性和方法到Category类:
public class Category {
private String name;// 类型名称
private Category parentCategory;// 父类型
private Set
childCategories = new HashSet
();// 子类型 private Set
-
items = new HashSet
-
();// 商品列表 }
Item类的代码(多对多关联的另一侧)类似于Category类的代码。添加集合属性,标准的访问方法,以及简化关系管理的一种方法
public class Item implements Serializable {
private static final long serialVersionUID = 2473606759362725264L;
private String name;
private String description;
private BigDecimal initialPrice;
private BigDecimal reservePrice;
private Date startDate;
private Date endDate;
private ItemState state;
private Date approvalDatetime;
private Set
categories = new HashSet
(); public void addCategory(Category category) { if (null == category) throw new IllegalArgumentException("Null category"); category.getItems().add(this); categories.add(category); } }
addCategory()方法类似于Category类的addChildCategory()便利方法。它被客户端用来操作Item和Category之间的链接。为了增加可读性,后面的代码样例中将不显示便利方法了,并假设你会根据自己的偏好来添加它们。
给关联处理使用便利方法并不是改善领域模型实现的唯一途径。也可以把逻辑添加到你的访问方法中。
把逻辑添加到访问方法
例如,如果 数据库把用户名称保存为单个NAME列,但是User类有firstname和lastname属性,你可以把下列持久化的name属性添加到这样类:
public class User implements Serializable {
private static final long serialVersionUID = -7811634244303955773L;
private String firstname;
private String lastname;
// ...
public String getName() {
return firstname + lastname;
}
public void setName(String name) {
// StringTokenizer是一个用来分隔String的应用类
StringTokenizer t = new StringTokenizer(name, " ");
this.firstname = t.nextToken();
this.lastname = t.nextToken();
}
// ...
}稍后,你会明白Hibernate定制类型是处理多个这种情况的一种更好的方法。然而,有多种选择的话也自有好处。
访问方法也可以执行验证。例如,在下列例子中,setFirstName()方法验证大写的名称:
public class User implements Serializable {
private static final long serialVersionUID = -7811634244303955773L;
private String firstname;
// ...
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) throws InvalidNameException {
if (!StringUtil.isCapitalizedName(firstname))
throw new InvalidNameException(firstname);
this.firstname = firstname;
}
} 另一个要考虑的问题是脏检查。Hibernate自动侦测对象状态的改变,以便使更新过的状态与数据库同步。从获取方法返回一个不同的对象,通常比由Hibernate传递到设置方法的对象来得安全。Hibernate按值比较对象――不是按对象同一性――来确定一个属性的持久化状态是否需要被更新。例如,下列获取方法就不会导致不必要的SQL UPDATE(更新):
public String getFirstname() {
return new String(firstname);
} 还有个重要的例外:集合是按同一性比较的!对于一个被映射为持久化集合的属性,你应该从获取方法中返回与Hibernate传递到设置方法中完全相同的集合实例。如果没有,Hibernate将更新数据库,即使不需要更新,保存在内存中的状态每次也都会与数据库同步。
最后,必须知道访问方法中的异常如何处理,如果你在加载和存储实例时配置Hibernate来使用这些方法的话。如果抛出RuntimeException,当前的事务就被