对双向相关bean的一些思考
最近一直在新项目中做建模工作,决定这次使用双向相关bean。这个名字听着挺唬人,然而却是做应用的程序员最常接触到的东西。对于不喜欢看长文章的读者,我直接上一组完整的双向相关bean来进行说明。
- 以下代码描述了一个文章类(Article)和一个作者类(Writer)以及它们共同继承的基类(PojoSupport),文章和作者关系为“一篇文章只对应一位作者,一位作者可对应多篇文章,我们不考虑多位作者合著一篇文章的情况。”
public class Article extends PojoSupport<Article> {
private int id;
private Writer writer;
private String title;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Writer getWriter() {
return writer;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void setWriter(Writer newWriter) {
if (this.writer == null || !this.writer.equals(newWriter)) {
if (this.writer != null) {
Writer oldWriter = this.writer;
this.writer = null;
oldWriter.removeArticle(this);
}
if (newWriter != null) {
this.writer = newWriter;
this.writer.addArticle(this);
}
}
}
}
public class Writer extends PojoSupport<Writer> {
private int id;
private String name;
private java.util.Collection<Article> article;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addArticle(Article newArticle) {
if (newArticle == null)
return;
if (this.article == null)
/*如果对article的顺序没有要求,这里以及下面所有出现LinkedHashSet的地方都可以改为HashSet*/
this.article = new java.util.LinkedHashSet<Article>();
if (!this.article.contains(newArticle)) {
this.article.add(newArticle);
newArticle.setWriter(this);
}
}
public java.util.Iterator<Article> getIteratorArticle() {
if (article == null)
article = new java.util.LinkedHashSet<Article>();
return article.iterator();
}
public java.util.Collection<Article> getArticle() {
if (article == null)
article = new java.util.LinkedHashSet<Article>();
return article;
}
public void removeAllArticle() {
if (article != null) {
Article oldArticle;
for (java.util.Iterator<Article> iter = getIteratorArticle(); iter
.hasNext();) {
oldArticle = iter.next();
iter.remove();
oldArticle.setWriter(null);
}
}
}
public void removeArticle(Article oldArticle) {
if (oldArticle == null)
return;
if (this.article != null)
if (this.article.contains(oldArticle)) {
this.article.remove(oldArticle);
oldArticle.setWriter((Writer) null);
}
}
public void setArticle(java.util.Collection<Article> newArticle) {
removeAllArticle();
for (java.util.Iterator iter = newArticle.iterator(); iter.hasNext();)
addArticle((Article) iter.next());
}
}
然后还有一个辅助类PojoSupport,它的用处是提供一些辅助方法,使得从数据库中加载的对象可以像java本地对象一样具有等价替换的功能。
public abstract class PojoSupport<T extends PojoSupport<T>> {
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PojoSupport<?> other = (PojoSupport<?>) obj;
if (getId() == null) {
if (other.getId() != null)
return false;
} else if (!getId().equals(other.getId()))
return false;
return true;
}
}
以上就是一个经典的一对多关系双向相关bean使用java的实现了。相对于非双向相关bean,它多出了不少代码,那它有哪些好处呢?请看下面这种情形:
Article a = new Article();
Writer w = new Writer();
a.setWriter(w);//或者 w.addArticle(a);
这样a和w就建立了对应关系,值得注意的是,上面最后一行的两种表达方式实现的效果是完全相同的。
同样在a和w脱离关系时,以下两种方式也是完全等效的:
a.setWriter(null);//方式1
w.removeArticle(a);//方式2
有些时候你得不到w的句柄,但你又需要改变这个writer的状态,这时你就会发现方式1是多么的可爱了。
我们假设a和w并没有脱离关系,当你将a或者w序列化时(比如序列化为json),本质上来说序列化这两者得到的内容是相同的。例如我们先给article和writer充实一下内容
a.setId(2);
a.setTitle("标题");
w.setId(3);
w.setName("姓名");
把w序列化后的json为
{"article":[{"id":2,"title":"标题","writer":{"$ref":"$"}}],"id":3,"name":"姓名"}
把a序列化后的json为:
{"id":2,"title":"标题","writer":{"article":[{"$ref":"$"}],"id":3,"name":"姓名"}}
这两个json虽然看起来不一样,但信息量是完全一样的。当然,在前端想真正发挥双向相关bean的长处,你还需要一个先进一点的json解析器,可以参考这篇文章 >http://www.oschina.net/question/109503_120507
也许这还说明不了什么问题,为了体现出“一对多”的价值,我们再加入另一篇文章otherA,这篇文章的作者同样是w:
Article otherA = new Article();
otherA.setId(5);
otherA.setTitle("另一篇");
otherA.setWriter(w);
//实现同样效果的另一种方式是w.addArticle(otherA);
然后我们把a序列化:
{"id":2,"title":"标题","writer":{"article":[{"$ref":"$"},{"id":5,"title":"另一篇","writer":{"$ref":"$.writer"}}],"id":3,"name":"姓名"}}
可以看出a中包含了otherA的信息,同样如果我们把otherA序列化:
{"id":5,"title":"另一篇","writer":{"article":[{"id":2,"title":"标题","writer":{"$ref":"$.writer"}},{"$ref":"$"}],"id":3,"name":"姓名"}}
可以看出otherA中也包含了a的信息。同时我们还可以注意到在w内部a和otherA的顺序是固定的,这是因为我们先建立w和a的关系再建立w和otherA的关系,并且我们在java代码中使用了LinkedHashSet的缘故。(如果使用HashSet那每一次的顺序就都是随机的了)
最后,我们当然还要看看把w序列化的样子:
{"article":[{"id":2,"title":"标题","writer":{"$ref":"$"}},{"id":5,"title":"另一篇","writer":{"$ref":"$"}}],"id":3,"name":"姓名"}
可以看出,w的序列化也包含全部信息,并且w中a和otherA的顺序仍然是固定的。
结论:以上情形说明双向相关特性就像“胶水”一样,可以把bean紧紧的粘在一起,这对于我们模拟复杂的真实世界对象和构造大型健壮的应用软件是极有好处的。然而真实世界中还有一种普遍的关系我们没有涉及,即多对多关系,这就留待下一篇博文《对双向相关bean的更多思考》来讲吧。
本文探讨了双向相关bean在应用建模中的作用,通过实例展示了其在一对多关系中的优势,如序列化时能保持一致性,以及在处理多对多关系时的潜力。文中还提到了双向相关特性在模拟真实世界对象和构造大型应用中的价值,并预告后续将讨论更广泛的关系类型。
8万+

被折叠的 条评论
为什么被折叠?



