关于一个领域模型的问题,请各位帮忙.

最近在做一个项目,有如下一个需求:
用户可以加对方为好友。
我采用hibernate映射,模型如下:
public class Account{

private String id ;
private String userName;
private String password ;
......

@ManyToMany(fetch=FetchType.lazy)//这个地方级联关系应该如何设置呢?
private Collection<Account> friends;

setter/getter.....

}

public class UserCommonServiceImple implements UserCommonService{

public void addToMyFriend(Account myselfArg,Account friendArg){
//myself,以及friend都是从表现层传过来的,不完整,这样就需要查询数据。
myself = this.getAccountRepository().query(myselfArg.getId());
friend = this.getAccountRepository().query(friendArg.getId());
myself.getFriends().add(friend);
this.getAccountRepository().update(myself);
//问题就出在这里.每次加好友的时候首先要查询两次数据库,把从表现层传来的两个账号填充完整,然后还要把已经的好友全部加载出来,那么如果好友很多的话,每次加载好友很耗费性能。并且这样一个加好友的功能要查询多次数据库,太耗费性能了,请各位帮忙。多谢了。

}


}

请问各位这个问题怎么解决,有没有更好的方法。请各位能在百忙之中抽点时间帮个忙,多谢。
[该贴被javaEE于2008-09-08 15:35修改过]

请各位帮忙,多谢了。

用merge方法试验看看,比update效率要高,而且你事先不要再查询。

哦,多谢banq老师了。现在学生还有一个问题请教,请banq老师帮忙。每个用户都有个人主页。个人主页显示用户的好友,最近访客等信息。我是把个人主页独立出去做为了一个值对象,模型如下:
public class Account{
private PersonalPage personalPage;

}

public class PersonalPage{


}

但是这样有个问题,personpage的内容都是通过查询数据库填充的,并且个人主页信息很多,查询很费性能,如果用户刷新一下就要去查询数据,或者当其他人访问某人的个人主页时,也是去数据库查询,这样太费性能,所以我想是把personalPage的内容缓存,因为personalPage是只读的,banq老师能不能指点个缓存的方案。其他的一个方案就是把Account缓存,而account是聚合的根,account控制了对personalPage的缓存,能不能缓存account,account的写要比personalpage的写操作频繁。

>>用merge方法试验看看,比update效率要高,而且你事先不要再查询。
banq老师您好,我用merge试了一下,merge后,表里的数据都没了,并且account有个email字段,我设置为了非空的,那么merge的时候也必须要设置email.请问老师还有什么解决办法吗?多谢了。

merge是针对一个实体而言的,但不能对一个实体的某个集合属性也merge。如果要求更新迅速,可以单独维护好友关系,比如建个FriendsCollection来管理关系,这样更新就迅速但查询就要费时,因为Account已经不能自己管理好友关系了,这在模型方面也不恰当。
反正要不就是更新高效查询低效,要不就反过来,似乎不能两全其美。

多谢freebox,我用那个ManyToMany映射,有一个中间表的,只不过那个中间表是hibernate维护的,无法操作它。我又分析了一下,加好友这种操作应该不是很频繁(相比起查询好友),好像只能更新的时候费点性能,并且加好友的时候有个严重的问题,比如我有几百个甚至更多的好友,每次加一个好友,都要把所有的好友加载进来,getFriends().add(account),这样以来有耗费内存,性能也不好,感觉不好。
哎,这东西还满头疼的,很费脑细胞呵呵。

Account聚合PersonalPage这个设计是不对的,PersonalPage属于业务特征,不是Account的天然属性,在财务系统中Account会有PersonalPage吗?但是有个人帐号,那么是否也将个人帐号作为Account的子对象呢?

所以,和业务活动相关的就是通过业务服务来实现的。

前面merge要对email等子对象有效果的话,使用cascade="all",将级联设置为all,这样,CRUD时,merge也会检查子对象,多了就新增,不同了就修改,相同就忽视。

>>前面merge要对email等子对象有效果的话,使用cascade="all",将级联设置为all,这样,CRUD时,merge也会检查子对象,多了就新增,不同了就修改,相同就忽视。

public class Account implements Serializable {

@Id
private String id;

private String userName;

private String password;

@Column(unique=true,nullable=false)老师您好,email是个字段,设置不了级联操作,我是把它设置为了非空。
private String email;

private Date createDate;

@ManyToMany(cascade={CascadeType.MERGE},fetch = FetchType.EAGER)
@JoinTable(name = "user_townees", joinColumns = { @JoinColumn(name = "account_id") }, inverseJoinColumns = { @JoinColumn(name = "townee_id") })
private Set<Account> friends;

}

对于PersonalPage来说,它的主要内容如下:用户的好友,最近访客等信息,这些信息都是通过查询数据库来完成的,PersonalPage是对于用户来说的,PersonalPage离开了用户就失去意义了,所以我觉得PersonalPage应该是附属与Account的。我是在AccountRepostory里把所有属于personalPage的内容查询组装好后,并且把它赋予account,然后再交给服务层。这样当用户登陆后自动导向到个人主页,或者其他人访问时,都是通过account去查询出相应的数据,填充到personalPage,然后表现层用personalPage的内容去渲染视图。

因为小弟对DDD不大熟悉,也是自己慢慢琢磨,不对的地方请老师指点。

PersonalPage是我帮助设计的,考虑里面有一些属性需要持久例如cssType,而这些属性在脱离Account之后就没有意义了,如果仅当做中间计算对象将无法区分各自的属性。
当成内部对象后说明必须通过account访问主页,这变成了一个广义上的用户,实际应该在AccountDetail类里。如果不这样,PersonalPage和Account就是一对一关联,独立出来做为一个实体。
class Account{
@OneToOne
private AccountDetail detail;
}
class AccountDetail{
private PersonalPage page;
private Blog blog;
}

哦,多谢了。看来我是把DDD中的实体和hibernate里的实体给搞混了,我以为实体就是要持久化的,而banq老师和freebox老师所说的是DDD中的实体。我只所以没把personpage当作实体来考虑是因为它的内容不需要持久化到其对应的表中,它的内容是动态的从account等表中查询出来的。按照现在freebox老师的模型,accountDetail当作一个实体,它里面的personalpage可以设置为@Transient,这样虽然accountDetail的personalpage不需要持久化,但是在DDD中它还是实体。这样理解对吗?请老师指点。现在也挺郁闷的,大学里也不教DDD,只能自己学习,幸好还有jdon这样的一个论坛,以及像banq老师和freebox老师这样的热心人帮忙。多谢两位老师指点了。

老师可不敢当,我也不过是初学,有时候分析的欠妥,需要大家共同指正,以免误导旁人。

我的理解实体是要持久的,并且要区分出这个对象其实以前尽管状态不同,但是还是同一个对象。
为什么是实体,为什么是值对象,要看到底要不要区分他们,具体的说是从状态上区分他们,两个对象引用如果有同样的状态,那到底是不是同一个对象呢?这时候状态无法区分他们,就要根据实体的标识来区分。
假设现在有两个PersonPage状态是一样的,那到底要不要区分他们两个呢?在不同的问题里面结果也是不同的,要区分就是实体,不需要就是值对象。那怎么确定PersonPage和Account的关系呢?DDD讲实体和值对象之前不是还有一块是讲关联的吗?其实我在10楼给的那个是为了说明一对一关联,应该在AccountDetail里关联Account,或者是直接用PersonPage关联Account,但要有约束就是一个Account只有一个PersonPage。