部门管理----添加部门信息功能修正
部门列表功能完成后,针对添加功能进行二次测试
1. 添加新数据
添加完毕后,页面显示正常
2. 使用iframe框架的重新载入框架功能
执行完毕后,出现错误数据
添加操作被重复执行了
问题分析:
用于添加操作执行完毕后,通过调用list()方法完成页面跳转。对于服务器来说,本次操作是请求是一个独立的请求,先添加数据,然后再读取数据,最终展示数据。当进行重新载入框架页面时,页面将上一次发送的请求重新发送到服务器端进行再次执行,因此出现了全新添加数据。
解决方案:使添加操作转化为独立操作,添加完毕后跳转到列表页面重新发送请求。Structs2提供专有的Action跳转方式,可以完成本操作。
3. 设置添加操作完毕后,转入structs的跳转页面流程,而跳转流程中设置重新发送请求,访问列表操作对应的独立操作。
Action类中对应的add方法
public String save(){
depEbi.save(dm);
return “list”;
}
public String save(){
depEbi.save(dm);
return “toList”;
}
Struct.xml中设置全新结果集,Action重定向到list()操作
<action name=”dep_*” class=”depAction”method=”{1}”>
<resultname=”list”>/WEB-INF/jsps/dep/list.jsp</result>
<resultname=”toList”>dep_list</result>
</action>
4. 重启服务器,刷新页面,测试功能。
重复提交现象消失,功能测试成功。
PS:项目中所有模块均照以上流程进行制作,后面将不再重复此类笔记。
[知识总结]
Structs2的结果集重定向可以使当前请求访问另一个请求时,使用不同的请求对像,当使用不同的请求对象访问Action时,上一次请求对象消失。页面进行重新载入操作或刷新时,执行的是最后一次请求操作,因此重复提交现象将消失。
参看:structs结果集result对象type参数设定。
[开发技巧]
上述功能包括下面要完成的修改与删除功能在诸多模块中都有体现,可以使用公共配置完成上述配置,避免大量重复的配置出现。在通用基类抽取中详细讲解。
部门管理----修改信息
1. 功能入口为修改连接
业务分析:修改部门数据需要跳转到修改页面,而修改页面要显示被修改的数据。
修改页面与添加页面基本相同,唯一区别就是修改页面中有数据,添加页面中无数据。考虑将两个页面组成一个页面。
2. 设置页面跳转为转入后台获取数据
页面访问设置为structs2标签,请求action设置为dep_input
<imgscr=”images/icon_3.gif”/>
<spanstyle=”line-height:12px;text-align:center;”>
<s:aaction=”dep_input” cssClass=”xiu”>
<s:paramname=”dm.uuid” value=”uuid”/>
</s:a>
</span>
Action类中提供input方法,并根据传入的参数读取对应的数据
//转入修改页面
publicString input(){
dm=depEbi.get(dm.getUuid());
return “input”;
}
Structs.xml中添加input结果对应的配置
部门
<actionname=”dep_*” class=”depAction” method=”{1}”>
<resultname-“list”>/WEB-INF/jsps/dep/list.jsp</result>
<result name=”tolist”type=”redirectAction”>dep_list</result>
<resultname=”input”>/WEB-INF/jsps/dep/input.jsp</result>
</action>
3. 重启服务器,刷新页面,测试功能
由于页面使用了struct2的UI标签,本身具有回显数据的功能,跳转前又对页面所要回显的数据进行了初始化,因此跳转页面中已经有数据。
4. 修改功能在提交时,与添加功能唯一的区别就是修改时,具有数据对应的OID值,而添加操作不具有OID值,可以利用此特点对添加修改操作进行区分。
5. 在页面上封装uuid表单
<s:form action=”dep_save” method=”post”>
<s:hidden name=” dm.uuid” >
6. 刷新页面,查看页面源代码,是否成功收集此值。
修改操作对应源代码
添加操作对应源代码
7. 以来上述特点,将修改功能的后台代码与添加功能使用同一方法,功能使用是否提交由uuid进行区分。
//添加部门信息
public String save(){
If(dm.getUuid()==null){
depEbi.save(dm);
}else{
depEbi.update(dm);
}
return “toList”;
}
8. 重启服务器,刷新页面,测试功能
功能测试成功,再次验证添加功能,之间无影响。
[知识总结]
Srtuct2的属性驱动方式可以用于页面Action间传值,同时该方式也是比较方便的一种形式,推荐使用此方式进行数据传输。
[开发技巧]
1. 实际开发过程中往往修改与添加使用同一页面,但是由于后台业务层代码,通常不相同,所以业务层要划分成两个方法,杜绝使用saveOrupdate操作完成两个业务。因为添加与修改时所进行的数据校验与逻辑校验可能是不同的,混合成一个方法无法进行区分。
2. 在进行业务修改时,如果页面收集的数据不能做到与数据库中对应的数据百分之百对应,上述方法会造成数据错误。
部门管理----删除部门信息
1. 功能入口为删除链接
业务分析:删除部门数据时,仅需要删除的数据oid传入后台即可。
2. 设置页面跳转为转入后台删除数据
页面访问设置为struct2标签,请求action设置为dep_delete
<imgsrc=”images/icon-04.gif”/>
<spanstyle=”line-height:12px”;text-align:center;”>
<s:a action=”dep_delete”cssClass=”xiu”>
<s:paramname=”dm.uuid” value=”uuid”/>
</s:action>
Action类中提供delete方法,并根据传入的参数执行删除操作。
//删除
public String delete(){
depebi.delete(dm);
return “toList”;
}
3. 重启服务器,刷新页面,测试功能
功能测试成功
[开发技巧]
1. 删除任何数据之前,必须提示用户是否删除该数据,让用户最后一次确认,避免误操作。
2. 实际业务中真正意义上的数据删除操作比较少见,多数情况在数据中设置标记,通过标记的值来区分数据是否可用,而不是将数据真正删除。
[扩展业务]
根据业务需要,为数据添加标记位,同时对数据的维护中添加启用/停用按钮,用于替换删除业务。所有的查询操作默认携带条件标记值为可以数据。除特殊业务外,标记为不可用的数据将不参与日常数据操作。
部门管理----按条件查询部门信息
按条件查询是企业级应用中比较重要的操作,用户通过设置某些条件查询,精确获取对应的数据。
1. 功能入口为查询按钮
业务分析:用户输入某些条件查询值,将该数据传入后台,作为查询条件获取满足要求的数据,并将查询结果返回到页面上。
2. 设置条件获取
页面访问设置为structs2标签,请求action设置为dep_queryList
为id为query的对象绑定提交form事件
$(function(){
$(“#query”).click(function(){
$(“form:first”).submit();
});
});
Action中设置用户保存查询条件属性,将收集的条件后台,进行操作按条件查询操作
publicDepQueryModel dqm=new DepQueryModel();
//安条件查询
public StringqueryList(){
List<DepModel>depList=depEbi.getAll(dqm);
ActionContext.getContext().put(“depList”,depList);
return “list”;
}
数据层添加具体的查询条件比对方式
publicList<DepModel> getAll(DepQueryModel dqm){
DetacheCriteriadc= DetacheCriteria.forClass(DepModel.class);
If(dqm.getName()!=null&&dqm.getName.trim().length()>0){
dc.add(Restrictions.like(“name”,”%”+dqm.getName().trim()+”%”));
}
If(dqm.getTele()!=null&&dqm.getTele().trim().length()>0){
dc.add(Restrictions.like(“tele”,”%”+dqm.getTele().trim()+”%”));
}
return this.getHibernateTemplate().findByCriteria(dc);
}
3. 添加辅助测试,重启服务器,刷新页面,测试功能
功能测试成功。中文数据无问题
4. 当前主菜单和查询操作使用的是不同的后台方法完成,观察两个方法区别
//跳转到部门列表页
public Stringlist(){
List<DepModel> depList=depEbi.getAll();
ActionContext.getContext.put(“depList”,depList);
return “list”;
}
//按条件查询
public StringqueryList(){
List<DepModel> depList=depEbi.getAll(dqm);
ActionContext.getContext.put(“depList”,depList);
return “list”;
}
两个方法中的内容除获取数据调用的业务方法不同,其他完全相同,list方法不包含查询条件获取queryList方法是包含查询条件获取。
将思路进行转换,list方法是一种特殊方式查询,差别在于查询条件中没有设置任何条件查询。这样可以将两个方法进行合并,使用一个方法代替。
该操作的合并将为后面的分页提供很大的操作简化
将queryList方法删除,使用list方法获取,修改list方法获取数据的方式为传入查询条件。
//跳转到部门列表
public Stringlist(){
List<DepModel>depList=depEbi.getAll(dqm);
ActionContext.getContext().put(“depList”,depList);
return “list”;
}
修改页面中查询操作对应的action访问
<s:formaction=”dep_list” method=”post”>
</s:form>
5. 重启服务器,测试成功
PS:项目中所有模块均参照以上流程进行,后面不再重复
[知识总结]
使用字段驱动用于封装查询条件,此设计避免使用原始模型封装查询时无法保存范围属性的问题
[开发技巧]
1. 将通用查询和条件查询进行合并是实际开发中常见的模式,在进行面向对象设计模型抽象过程中,应慢慢掌握这种抽象方式,即特殊与通用之间的关系,通过需求分析理清所有关系后,开发将变得快捷简便。
2. 此处按条件查询与CRM条件查询的不同是,此处的查询条件与数据展示页面在同一个页面中,查询条件使用完毕后基于structs的UI标签的回显功能,再次出现查询页面中,避免了查询条件的缺失。
3. 对于被替换掉的方法,不能进行随意删除。根据OPC法则,针对代码中修改功能关闭,尤其是删除功能,可能会引发不必要的麻烦。所以在进行代码编写过程中无效的方法经过确认后方可删除。此处的Action中的queryList方法可以删除因为这是临时测试方法,而业务层与数据层的查询方法不能删除,防止其他开发者使用。
l 部门管理——查询分页
分页显示功能是企业级应用中比较常见的操作,要求开发者必须掌握其原理与实现方式。常见的分页有两种形式:内存分页与数据库分页。实际开发中这两种形式有可能混合使用。本项目中使用数据库分页方式完成分页功能。
1.为分页链接添加id,以便于为其绑定事件
2.添加绑定事件,并测试是否有效
测试效果
3.分页获取数据就是获取对应数据的局部数据,首先将要获取数据对应的页码值传递到后台,因此需要在页面提交事件中封装要获取的数据对应的页码值表单。
4.所谓分页操作实际上是重新发送获取数据的请求,差别在于每次获取的数据不同而已,因此需要将封装页码值的表单加入提交查询任务请求对应的form中。
被提交的form要包含所有查询过程中使用的数据。因此查询条件应该被包裹在其中。扩大form表单的范围,从当前页面开始到结束的所有表单均参与提交。其中包含有上述页码值表单。
5.页面中为下一页按钮添加动态提交form事件,并将页码值暂时设置为第2页。
6.Action中初始化用于接收页码值的属性驱动字段名以及每页显示的数据总量。
7.Action中修改list方法调用的操作为分页查询
8.完成业务层调用与数据层调用的修改
Ebo
Impl
数据层实现换用HibernateTemplate支持分页查询QBC查询操作即可
9. 重启服务器,刷新页面,测试功能
问题分析:
通过Debug查看抛出异常的位置,由于Action中pageNum变量没有初始化,导致进入到数据层进行运算的时抛出异常。而从页面主导航点击进入部门列表时,并没有对该变量进行初始化。
解决方案
为Action类中的pageNum值设置初始化值为1
publicInteger pageNum=1;
10.重启服务器,刷新页面,测试功能
主导航功能显示正常
点击下一页之后
10. 修改事件中的跳转页码值为动态值
11. 重启服务器,刷新页面,测试功能
验证功能成功
12. 为其他按钮添加对应的事件
用于末页按钮需要设定设置当前页码值的最大页码值,而该值必须由后台动态传递过来。因此需要首先计算出该值,才可以完成末页方法的书写。
最大页码值由数据总条目与每页显示条数计算出来,因此需要在后台获取数据总条目,然后计算出最大页码值后,将其传递到页面中。
14.Action类中获取数据总条目数
由于该值必须在进入列表页面之前计算完毕,因此,在list方法获取该值。由于数据层对应的方法完成。由于当前list方法所完成的任务是按条件查询,因此获取数据总条目数也需要传入相应的条件,否则与获取数据的数据将不一致。
Action
Impl
数据层实现中,getCount方法中的查询条件必须与getAll方法中的查询条件一致。由于查询条件过多,因此使用QBC查询,设置投影为行计数。
15.数据层计算得到数据总条目数后,在Action中计算出最大页码值,并将其保存到Action的成员变量中,用于该值传入页面。
Action
16.页面获取Action中计算出最大页码值
17.重启服务器,刷新页面测试成功
修改页面的显示型数据为正确数据。
由于页面中设置显示总记录数,因此后台获取的数据总量count值,需要传入页面,声明用于传递该值的变量
Action
List.jsp
18.重启服务器,刷新页面,测试功能
四个按钮功能测试成功。重复点击下一页按钮出现以下现象
当前页面上用于分页的按钮显示未进行控制,容易造成错误的操作。对按钮的显示进行条件判断控制
19.刷新页面,测试功能
分页操作完成,测试成功
不设置查询条件进行查询,查询末页数据。
输入查询条件,进行查询
出现错误现象,通过查看Firebug的网络信息获取DEBUG跟踪数据可知最后一次发送请求所查询的页面数据为上一次查询所使用的页码数据。必须设置输入条件查询后查询内容从第一页显示。设置页码查询页为第一页。
20.刷新页面,测试功能
[知识总结]
1. jquery选择器获取数据组件非常灵活,此处使用属性作为选择器标准,通过name属性获取组件
2. Hibernate投影查询使用HQL设置非常简单,使用QBC查询设置时相对来说比较繁琐。此处要用复杂条件查询,优先使用QBC,只能使用QBC设置投影的格式进行。
[开发技巧]
1. javascript中如果需要对页面组件获取的值进行数字加操作,必须保障两个操作数据都是数字,否则将进行字符串连接运算。此处使用*1操作,将字符串快速转换为数字格式。这也是实际开发中常用的转换格式,毕竟该操作的压力是在客户端的,该操作所消耗的时间不由服务器承担,即便慢一些对客户端压力基本可以忽略。
2. 页面组件的显示控制通常使用css进行,实际开发中往往使用切换class属性进行,而不是直接修改css属性。
[扩展功能]
1. 可以对分页控件进行进一步扩展,添加指定页快速导航,下拉列表快速导航,每页显示数据总量设置导航,页面横向列表快速导航等常见分页导航组件。
当前功能如果进入到不是第一页的数据后,修改某个数据完成时,将跳转到第一页显示,这种效果是不友好的。