什么是Grid-Json数据加载
我们开发前台Grid数据加载通常是Rpc查询返回List<...model>方式,后台的数据的序列化和反序列化都是gwt做了,用着其实很方便,但是在我们实际开发中遇到问题就是List的数据量大时,gwt的序列化和反序列化工作是很慢的,存在很大的提升空间。
思路:
我们想想普通的spingMvc模式,返回一个json字符串到前台,几百条的数据根本不会存在序列化的性能问题,是不是我们的gxt-grid请求后台数据也可以用json呢?
当然用json会存在一个问题,就是json怎么转换成前台用的model实体类(这个对于习惯了使用gxt的model实体对象的人来说是很在乎的一个问题)。
经过探索发现gxt已经帮我们封装API把json自动转换成model就是JsonLoadResultReader类,只是我们没有去挖掘它,下面将以出院患者查询说明(做普通的model查询模式500个患者加载时间用了20s-30s,改造通过json方案测试466条数据时间在3s-7s左右)。
Grid-Json使用方法
1、定义好Rpc方法接口,虽然还是用Rpc模式,但是我们的方法返回类型不用List<...model>,直接返回String类型(一个json字符串数据):
void
findLeaveHospitalPatientList (PaiVisitModel paiVisitInfoModel, AsyncCallback<String> callback);
|
2、写好Controller获取数据的实现,由于我们返回一个json字符串数据需要将数据转换为json字符串(我用的JsonLib工具),代码中为什么要把数据设置在一个key值为root的Map中,这里是为了配合前台反序列化的逻辑要求:
private
JsonConfig cfg =
new
JsonConfig();
public
PaiVisitServiceController() {
cfg.registerJsonValueProcessor(java.util.Date.
class
,
new
JsonValueProcessor() {
public
Object processObjectValue(String key, Object value,
JsonConfig arg2) {
if
(value
instanceof
Date) {
return
DateFormatUtils.format((Date)value,
"yyyy-MM-dd HH:mm:ss"
);
}
return
value;
}
public
Object processArrayValue(Object value,
JsonConfig arg1) {
return
null
;
}
});
}
@Override
public
String findLeaveHospitalPatientList(PaiVisitModel paiVisitInfoModel) {
PaiVisitInfoVO query = ConvertUtils.convert(PaiVisitInfoVO.
class
,
paiVisitInfoModel);
List<PaiVisitInfoVO> infoVOs = m_paiVisitService
.findLeaveHospitalPatientList(query);
Map<String, Object> map =
new
HashMap<String, Object>();
map.put(
"root"
, infoVOs);
String json = JSONObject.fromObject(map,cfg).toString();
return
json;
}
|
3、定义gxt前台反序列化配置,代码中modelType.setRoot("root");的root参数与Controller里面map设置的root参数对应:
public
static
final
ModelType getModelType(){
ModelType modelType =
new
ModelType();
modelType.setRoot(
"root"
);
DataField dataField =
null
;
dataField =
new
DataField(
"patientId"
);
modelType.addField(dataField);
dataField =
new
DataField(
"currentDeptName"
);
modelType.addField(dataField);
dataField =
new
DataField(
"currentWardName"
);
modelType.addField(dataField);
dataField =
new
DataField(
"currentNursingUnit"
);
modelType.addField(dataField);
dataField =
new
DataField(
"currentNursingUnitName"
);
modelType.addField(dataField);
dataField =
new
DataField(
"admissionDeptName"
);
modelType.addField(dataField);
dataField =
new
DataField(
"admissionDeptType"
);
modelType.addField(dataField);
dataField =
new
DataField(
"admissionDeptTypeName"
);
modelType.addField(dataField);
dataField =
new
DataField(
"introductionEmpId"
);
modelType.addField(dataField);
dataField =
new
DataField(
"introductionFromHospitalArea"
);
modelType.addField(dataField);
dataField =
new
DataField(
"introductionToHospitalArea"
);
modelType.addField(dataField);
dataField =
new
DataField(
"introductionEmpOrgCode"
);
modelType.addField(dataField);
dataField =
new
DataField(
"paiVisitId"
);
dataField.setType(Long.
class
);
modelType.addField(dataField);
dataField =
new
DataField(
"costType"
);
modelType.addField(dataField);
dataField =
new
DataField(
"admissionDate"
);
dataField.setType(Date.
class
);
dataField.setFormat(
"yyyy-MM-dd HH:mm:ss"
);
modelType.addField(dataField);
dataField =
new
DataField(
"endDate"
);
dataField.setType(Date.
class
);
dataField.setFormat(
"yyyy-MM-dd HH:mm:ss"
);
modelType.addField(dataField);
dataField =
new
DataField(
"charges"
);
dataField.setType(Double.
class
);
modelType.addField(dataField);
}
|
4、前台定义json反序列化成model的JsonLoadResultReader对象并放入store中(源码中重写newModelInstance方法很重要,返回一个自己要的model实体对象):
RpcProxy<String> proxy =
new
RpcProxy<String>() {
@Override
public
void
load(Object loadConfig, AsyncCallback<String> callback) {
// service.getPosts((PagingLoadConfig) loadConfig, callback);
m_paiVisitRpcAsync.findLeaveHospitalPatientList(getPaiVisitModelParam(), callback);
}
};
JsonLoadResultReader<ArrayList <PaiVisitModel>> reader =
new
JsonLoadResultReader<ArrayList <PaiVisitModel>>(PaiVisitModel.getModelType()){
protected
ModelData newModelInstance() {
return
new
PaiVisitModel();
};
};
final
BaseListLoader<ListLoadResult<PaiVisitModel>> loader =
new
BaseListLoader<ListLoadResult<PaiVisitModel>>(proxy,reader){
protected
void
onLoadSuccess(Object loadConfig, com.extjs.gxt.ui.client.data.ListLoadResult<PaiVisitModel> data) {
super
.onLoadSuccess(loadConfig, data);
unmask(LeaveHospitalPatientGridpanel.
this
.getParent());
}
protected
void
onLoadFailure(Object loadConfig, Throwable t) {
super
.onLoadFailure(loadConfig, t);
unmask(LeaveHospitalPatientGridpanel.
this
.getParent());
MessageBox.alert(
"查询出错"
, t.getMessage(),
null
);
t.printStackTrace();
};
};
protected
final
ListStore<PaiVisitModel> m_listStore =
new
ListStore<PaiVisitModel>(loader);
|
5、触发rpc请求:loader.load();
关于Gxt的json
先说缺点:
1、通过上面API的使用可以看出json转换成model需要定义ModelType配置对象,配置json每个key到model字段的类型映射,现在我们维护了pojo和model两个实体类不说,又多出一个ModelType来描述json到Model的转换映射关系,麻烦喽(所以我花了几分钟写了一个小工具,自动生成ModelType的代码);
public
static
void
main(String[] args) {
Method[] mes = PaiVisitModel.
class
.getMethods();
List<String> ecu = Arrays.asList(
"getClass"
,
"getPropertyNames"
,
"getProperties"
,
"get"
,
"getBean"
);
System.out.println(
"ModelType modelType = new ModelType();"
);
System.out.println(
"modelType.setRoot(\"root\");"
);
System.out.println(
"DataField dataField = null;"
);
for
(Method method : mes) {
String name = method.getName();
if
(ecu.contains(name)||!name.startsWith(
"get"
)) {
continue
;
}
name = StringUtils.uncapitalize(name.replaceFirst(
"get"
,
""
));
Class<?> returnType = method.getReturnType();
if
(returnType.isAssignableFrom(String.
class
)) {
System.out.println(
"modelType.addField(\""
+name+
"\");"
);
}
else
{
System.out.println(
"dataField = new DataField(\""
+name+
"\");"
);
System.out.println(
"dataField.setType("
+returnType.getSimpleName()+
".class);"
);
if
(returnType.isAssignableFrom(Date.
class
)) {
System.out.println(
"dataField.setFormat(\"yyyy-MM-dd HH:mm:ss\");"
);
}
System.out.println(
"modelType.addField(dataField);"
);
}
}
System.out.println(
"return modelType;"
);
}
|
2、另外我探索json转换成model的源码里发现:model里面在嵌套一个model,里面的model不会转换出来;
3、后台需要自己把对象序列化成json;
优点:
1、序列化速度提高了,绕开了gwt的序列化(护士站出院患者加载时间从20s-30s变成3s-7s也还是令人惊喜);
2、一番探索使我发现:不熟悉它时,觉得很麻烦,如果明白熟悉了,就会感觉它有着超灵活的API;
3、因为灵活的API,所以给了我们封装和个性化定制的机会