这里用最常见的例子:Customer和Order来举例
客户和订单。
一个客户可能有N个订单,Customer和Order两个类分别对应两个表,字段中有各自相应属性...如Customer表里的id、name..Order表里的id,number,pricec_id..这些必要的属性外。当出现一种情况是业务需要某一用户的订单总金额(totalprice)和平均金额(avgprice)的时,该去怎么处理。
假设在Customer里面再加入两个字段的话,问题可以解决,但并不合理:因为数据在经常变动的情况下,双表关联频繁update显然会影响软件的性能,如果放在表中,这两个字段可视为是冗余数据。
那么我们现在就不做一一对应的关系,只在类中有这两个属性,表中没有。利用已知的条件:每个order的金额、和用户的order数来在程序中求出总金额与平均金额;
有两种方法来处理这样的功能,一种通常的方法是在程序中写一处理方法:
import java.util. * ;
public class Customer {
private Long id;
private String firstname;
private String lastname;
private char sex;
private Set orders = new HashSet();
private double avgPrice;
private double totalPrice;
private String description;
public Customer() {
}
public Customer(String firstname, String lastname, char sex, Set orders,
String description) {
this .firstname = firstname;
this .lastname = lastname;
this .sex = sex;
this .orders = orders;
this .description = description;
}
public Long getId() {
return this .id;
}
public void setId(Long id) {
this .id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this .firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this .lastname = lastname;
}
public String getName() {
return firstname + " " + lastname;
}
public void setName(String name) {
StringTokenizer t = new StringTokenizer(name);
firstname = t.nextToken();
lastname = t.nextToken();
}
public double getAvgPrice() {
return this .avgPrice;
}
private void setAvgPrice( double avgPrice) {
this .avgPrice = avgPrice;
}
public double getTotalPrice() {
return this .totalPrice;
}
private void setTotalPrice( double totalPrice) {
this .totalPrice = totalPrice;
}
public void setOrders(Set orders) {
this .orders = orders;
calculatePrice(); // 或者通过 <property>的 formula 属性来实现
}
public Set getOrders() {
return orders;
}
private void calculatePrice() {
double avgPrice = 0.0 ;
double totalPrice = 0.0 ;
int count = 0 ;
if (getOrders() != null ) {
Iterator iter = getOrders().iterator();
while (iter.hasNext()) {
double orderPrice = ((Order) iter.next()).getPrice();
totalPrice += orderPrice;
count++ ;
}
// Set the price for the order from the calcualted value
avgPrice = totalPrice / count;
setAvgPrice(avgPrice);
}
}
public char getSex() {
return this .sex;
}
public void setSex( char sex) {
if (sex != ' F ' && sex != ' M ' ) {
throw new IllegalArgumentException( " Invalid Sex " );
}
this .sex = sex;
}
public String getDescription() {
return this .description;
}
public void setDescription(String description) {
this .description = description;
}
public boolean equals(Object lObj) {
if ( this == lObj) {
return true ; // 比较的是两个对象的引用(references)是否相等
}
if (lObj == null ) {
return false ; // 检查 lObj是否为空
}
if (getClass() != lObj.getClass()) {
return false ; // 检查是否属于同一个class
}
return true ;
}
}
可以看出,用calculatePrice()方法 来运算可以解决这样的问题的。
下面的一种方法是通过映射文件来解决此问题,如果从SQL语句上来讲,avgPrice和totalPrice分别都可以用SQL语句来查出。如下:
select sum(o.PRICE) from ORDERS o where o.CUSTOMER_ID=?
select avg(o.PRICE) from ORDERS o where o.CUSTOMER_ID=?
而hibernate的映射文件支持方程式formula,我们可以在Customer.hbm.xml里面这么写:
< property name ="totalPrice"
formula ="(select sum(o.PRICE) from ORDERS o where o.CUSTOMER_ID=ID)" />
< property name ="avgPrice"
formula ="(select avg(o.PRICE) from ORDERS o where o.CUSTOMER_ID=ID)" />
< property name ="description" type ="text" column ="DESCRIPTION" />
再通过类里面的get、set方法来取得avgPrice和totalPrice。
总结,咋一看来,好像第二种方法比第一种方法要简练。但仔细想,现在我用的是SQL Server,如果底层数据库变了的话,而映射文件里的方程式恰好又有相关联的数据库函数的话,那么就违背了"write once,run anywhere"的原则,而第一种逻辑运算则变成了真理。除非没有了Java.