花了整整3天时间做一个feature,别的事都耽误了。
feature是,系统中有多种事件原因,做一个前台管理它们。
1. 后台
有两个domain object, Reason and ReasonMessage, n..1。Reason到ReasonMessage的ManyToOne很显然,但是反过来的OneToMany花了很长时间搞明白mappedBy, List/Set/Map.
class Reason:
@ManyToOne
private ReasonMessage message;
@Enumerated(EnumType.STRING)
private ReasonType type;
class ReasonMessage:
@ManyToOne
private ReasonMessage externalReason;
@OneToMany(cascade={CascadeType.ALL},mappedBy = "message")
private List<Reason> reasons;
mappedBy就是One的一方(Reason)的message字段指向Many的一方。List允许重复,Set不允许,Map还没想明白有什么好处,别人说用map可以很容易地利用keySet()拿到所对应的one的一方。
CRUD需要loadAll和delete,EntityRepository里都已经有了。
add的时候需要操作两种表(ReasonMessageManagerImpl, 生成多个Reason和一个ReasonMessage,在一个transaction里),因此需要自己写Manager。现在似乎很多人用先写interface再写impl的模式。
前后台的交互方面,前台需要显示ReasonMessage的List<Reason>,但不能把真正的Reason对象传下去,因此在ReasonMessage里加了一个get方法getReasonTypes():String,得到每个Reason的用于显示的部分,前台根据这些来render。
2. REST
REST负责把数据在前后台间搬运。读数据时候,调用后台的repository或manager,用dozer自动把读到的数据转成前台需要的数据。写数据的时候,前台传回来的可能是一个id,需要用dozer把它转成object(比如,ReasonMessage有一个字段externalReason,指向系统中已经有的一个ReasonMessage。用户在前端创建ReasonMessage的时候可以选择它,在传回的json string里,可以用externalReason:{id:xx}。这样服务端就可以根据id自动找到ReasonMessage对象。这需要配置dozer需要的bean-mappings.xml)。关于dozer( http://dozer.sourceforge.net/)。
这时要考虑到是dto该怎么写:有哪些字段,哪些字段有getter哪些有setter。没深入看,经验是,前台需要读到字段要有getter,前台需要写的自段要有setter。
3. 前台
GWT这里比较简单,Toolbar里放一个comboBox,下面一个grid, 再下面add/delete buttons. comboBox onSelect的时候filter下面grid的store(grid.getStore().filter(field, regexp)。
comboBox的数据源比较简单(String[]),hardcoded,因此用SimpleStore就可以了。
用xml reader的时候,如果一个字段名在上下两级都存在(<item><id/><child><id/><msg/></child></item>)且上一级的字段在child的后面,那么直接IntegerFieldDef("id")会取到child的相应自动。此时调整顺序把child放在最后,或者用IntegerFieldDef("/id"),用"/"表示根。
add时,拼的json string要和rest的dto想对应:string, object, array。参加 www.json.org.
feature是,系统中有多种事件原因,做一个前台管理它们。
1. 后台
有两个domain object, Reason and ReasonMessage, n..1。Reason到ReasonMessage的ManyToOne很显然,但是反过来的OneToMany花了很长时间搞明白mappedBy, List/Set/Map.
class Reason:
@ManyToOne
private ReasonMessage message;
@Enumerated(EnumType.STRING)
private ReasonType type;
class ReasonMessage:
@ManyToOne
private ReasonMessage externalReason;
@OneToMany(cascade={CascadeType.ALL},mappedBy = "message")
private List<Reason> reasons;
mappedBy就是One的一方(Reason)的message字段指向Many的一方。List允许重复,Set不允许,Map还没想明白有什么好处,别人说用map可以很容易地利用keySet()拿到所对应的one的一方。
CRUD需要loadAll和delete,EntityRepository里都已经有了。
add的时候需要操作两种表(ReasonMessageManagerImpl, 生成多个Reason和一个ReasonMessage,在一个transaction里),因此需要自己写Manager。现在似乎很多人用先写interface再写impl的模式。
前后台的交互方面,前台需要显示ReasonMessage的List<Reason>,但不能把真正的Reason对象传下去,因此在ReasonMessage里加了一个get方法getReasonTypes():String,得到每个Reason的用于显示的部分,前台根据这些来render。
2. REST
REST负责把数据在前后台间搬运。读数据时候,调用后台的repository或manager,用dozer自动把读到的数据转成前台需要的数据。写数据的时候,前台传回来的可能是一个id,需要用dozer把它转成object(比如,ReasonMessage有一个字段externalReason,指向系统中已经有的一个ReasonMessage。用户在前端创建ReasonMessage的时候可以选择它,在传回的json string里,可以用externalReason:{id:xx}。这样服务端就可以根据id自动找到ReasonMessage对象。这需要配置dozer需要的bean-mappings.xml)。关于dozer( http://dozer.sourceforge.net/)。
这时要考虑到是dto该怎么写:有哪些字段,哪些字段有getter哪些有setter。没深入看,经验是,前台需要读到字段要有getter,前台需要写的自段要有setter。
3. 前台
GWT这里比较简单,Toolbar里放一个comboBox,下面一个grid, 再下面add/delete buttons. comboBox onSelect的时候filter下面grid的store(grid.getStore().filter(field, regexp)。
comboBox的数据源比较简单(String[]),hardcoded,因此用SimpleStore就可以了。
用xml reader的时候,如果一个字段名在上下两级都存在(<item><id/><child><id/><msg/></child></item>)且上一级的字段在child的后面,那么直接IntegerFieldDef("id")会取到child的相应自动。此时调整顺序把child放在最后,或者用IntegerFieldDef("/id"),用"/"表示根。
add时,拼的json string要和rest的dto想对应:string, object, array。参加 www.json.org.