《研磨struts2》第十二章 CRUD综合运用 之 12.3 使用Struts2来实现表现层
在前面学习的时候,每个Action类都只对应一个单独的web请求,那么,这样做会带来什么问题呢?
?????? 先想一下,用户在添加页面上点击添加按钮之后,会发生什么事呢?Struts2会提交到负责添加的Action中,这个Action只是接收前一个页面传过来的数据,然后把它填入数据库就可以了,所以,可以这样写:
?
java代码:查看复制到剪贴板打印这个Action用自己的一个user属性接收了上一个页面上传过来的数据,然后新建了一个dao,用dao把这个UserModel放入了数据库。看起来没有什么问题,它做了它该做的事情。
?????? 再想一想其他Action呢?如果用户在修改页面上点击了修改按钮之后,Action要做什么呢?
同样也是接收页面传过来的数据,然后用dao把这个UserModel放入数据库吧,大致的写法会是这样:
?
java代码:查看复制到剪贴板打印对比这两个类,会发现几乎是相同的,只是UserAddAction调用了dao的create方法,而UserUpdateAction调用了dao的update方法。
?????? 因此,在实际的开发中,并不会用一个Action来对应一个web请求的功能,而是把一组相关的web请求的功能,比如说都是处理UserModel的功能,放到一起,用一个Action来实现。
在struts.xml的配置中,仍然可以使用多份不同的配置,也就是一个Action对应多个<action>标签。这样做,既可以消除很多细粒度的类,又可以消除这些类之间的重复代码,比如说UserAddAction和UserUpdateAction都有的user属性及其getter/setter等。
在接下来的实现中,我们将使用一个Action,来完成对用户的增、删、改、查功能。
首先来实现显示全部用户的功能。要显示全部数据,通常是先进入一个Action来检索出所有数据,然后跳转到负责显示数据的页面,把数据展示出来。
1:准备Action
?????? 在上一个小节中,已经说过,真正的开发中往往把一些列相关的web请求的功能放到一起,由一个Action响应。那么,怎么用一个Action来响应多个web请求的功能呢?
可以采用第四章学到的给Action起别名的方法:在struts.xml中,一个Action类对应多个<action>元素,每一个<action>元素都可以指定一个方法名(method属性),使得响应的时候可以不调用execute方法,而是调用指定方法。
?????? 因此,可以写出Action的代码,示例如下:
?
java代码:查看复制到剪贴板打印在这里,代码本身并不难,只是需要注意,现在的响应方法不是execute方法了,而是写的toList方法。
2:准备JSP
?????? 在Action中,将从数据库里检索出来的数据放到了一个叫list的属性里,所以,列表页面(list.jsp)上只要循环的显示这个list属性里的所有东西就可以了。
这会用到<s:iterator/>标签,注意,这个标签会把当前正在循环的对象放到值栈的栈顶,所以,在引用正在循环对象的某个属性的时候,在<s:iterator/>标签范围内的<s:property/>标签里的OGNL表达式只需要写属性名就可以了。
示例代码如下:
?
java代码:查看复制到剪贴板打印3:struts.xml配置
在struts.xml中需要进行配置,才能让UserAction的toList方法被执行后,跳转到指定的list.jsp。
?
java代码:查看复制到剪贴板打印在这里,唯一要注意的就是<action>标签的method属性,指明了在访问名为userToList的Action时,要调用的不是execute方法,而是指定的toList方法。
?????? 启动数据库和Tomcat服务器,访问“http://localhost:9080/crud1/crud1/userToList.action”就可以看到全部数据了。当然,建完表之后还没有数据,可以先手工向数据库里加几条,看一下结果。
图12.1 显示全部用户页面
做完显示全部用户之后,接下来开始实现添加用户的功能。
添加用户的操作要分两个阶段:首先,要跳入一个“添加页面”,在这个页面上可以填入想添的数据;然后,点击添加按钮时,提交这个页面,进行真正的数据库操作。
1:准备跳转到添加页面的链接
先要在list.jsp页面上添加一个转到“添加用户”页面的链接:
?
java代码:查看复制到剪贴板打印点击这个链接,访问名为userToAdd的Action。
2:准备跳转到添加页面之前的Action
?????? 在添加页面上,需要收集用户的输入:分别用三个普通的文本框收集用户的编号、姓名、年龄;用一个下拉框来收集用户的性别,下拉框有两个选项,分别是男和女。因此,在跳转到添加页面之前的Action中,只需要准备一个字符串数组设定两个选项“男”和“女”。
?????? 把关于UserModel的一组操作都放到UserAction中,各个方法之间是互不影响的,UserAction中已经有了toList方法,那么,只要新添加的方法不叫toList就可以了。所以,在UserAction中添加以下内容,注意,这里的添加不要影响原有的内容,是在已有的内容上添加的,示例代码如下:
?
java代码:查看复制到剪贴板打印在这里,新建了一个toAdd方法,它什么也没做,只是返回了toAdd这个字符串。还新建了一个String数组,定义了“男”和“女”,这样,在下一个页面上就可以引用这个数组来生成下拉框了。
2:准备添加页面
?????? 添加页面,也就是add.jsp页面,示例代码如下:
?
java代码:查看复制到剪贴板打印在这个页面上,<s:form>标签,没有设置namespace属性,说明要提交到同包内的名为userAdd的Action去,没有指定theme属性,说明使用默认的xhtml主题。
<s:textfield>标签很简单,其label属性为文本框前面显示的文字,name属性指定了提交的参数名。
<s:select>标签的list属性指定了生成选项的数据源为上一个Action的sexs属性,正是建立的String数组,没有指定listKey属性和listValue属性说明生成<option>的时候value和<option>标签中间的文本都使用String数组中的字符串即可,所以,在页面上看到的选项是“男”、“女”,将来在下一个Action中接到的值也将是“男”或“女”。
3:struts.xml配置
?????? 那么,下一步是让UserAction的toAdd方法运行完之后,跳转到add.jsp页面,而进入这个页面是在list.jsp上跳转到名为userToAdd的Action,所以在配置的时候采取以下方式:
?
java代码:查看复制到剪贴板打印注意,这个<action>元素与上一个<action>名为userToList的元素的关系是:同一个包内的两个<action>元素,因此,这里就体现了解决问题的思路:同一个Action类通过取不同别名的方法,在struts.xml中配置为多个<action>元素,来执行一组相关的web请求所对应的功能。
图12.2 添加用户页面
4:提交Action
?????? 添加页面的初始化完成之后,可以来实现点击“添加按钮”后如何向数据库里提交了。在add.jsp上,点击“添加按钮”提交到名为userAdd的Action中。仍然使用UserAction,这时,UserAction的add方法只需要接收add.jsp传过来的参数,并把它添加到数据库即可,在向数据库添加完之后,只需要跳转到显示所有用户的页面,示例代码如下:
?
java代码:查看复制到剪贴板打印由于add方法最后调用了toList方法,而且add方法以toList方法的返回值作为自己的返回值,所以add方法的返回值实际上是toList。而且,添加用户最终要跳转到显示所有用户的页面。所以在配置struts.xml的时候要注意,这个<action>元素的<result>子元素应该以toList为名,最终指向到list.jsp。
6:struts.xml配置
去运行测试一下看看,现在的新增和列表功能是否好用。
做完显示所有用户和添加用户的功能之后,接下来实现修改用户的功能。
修改用户要分两个阶段:首先,要跳转到一个“修改页面”,在这个页面上展示需要修改的数据,用户在此基础上可以修改要改的数据,然后点击修改按钮时,提交这个页面,进行真正的数据库操作。
1:准备跳转到修改页面的链接
先要在list.jsp页面上为每条用户数据准备一个跳转到修改页面的链接,这个链接自然应该放到table中,每一行都有一个,而且每行都把自己这行显示的用户的userId数据传给后续处理的Action,示例代码如下:
?
java代码:查看复制到剪贴板打印在上面的代码中,加粗的代码就是跳转到名为userToUpdate的Action的链接,需要以user.userId的名字传入当前行显示的用户的userId,而且整个这一行在<s:iterator/>标签中,所以,使用<s:property value=”userId”/>即可引用当前行显示的用户的userId。
2:准备跳转到修改页面之前的Action
?????? 修改页面先要展示需要修改的数据,通常的做法是在转向修改页面之前的Action里面,就把这条数据检索出来并放到值栈里,等待修改页面去引用,示例代码如下:
?
java代码:查看复制到剪贴板打印在这个Action里,toUpdate方法操作了user属性,由于各个操作都共用这个UserAction,所以,在做add方法时,已经为UserAction增添了user属性,这里就不要再声明了。
由于在list.jsp页面上写链接传值的时候,用户的userId以名为user.userId属性传入这个Action,因此,自然要调用其user属性的getUserId()方法来获得。
3:准备修改页面
?????? 修改页面与添加页面的布局完全一样,只是要展示需要修改的数据,示例代码如下:
?
java代码:查看复制到剪贴板打印请读者比较一下这个update.jsp和原来的add.jsp,发现除了<title>不同,<s:form>要提交的目标不同,<s:submit/>显示的文字不同之外,别的都与add.jsp一模一样,不是要展示数据吗?怎么没有赋值呢?
请大家回忆一下前面学过的知识:<s:textfield/>标签的name属性,不仅仅指定了这个文本框在提交的时候的参数名;在没有指定value属性的时候,name属性还可以当作OGNL表达式来向值栈请求值,作为这个文本框的初始值。
?????? 因此,在update.jsp上的<s:textfield name=”user.userid”/>,由于在Action(userToUpdate)中初始化了其user属性,而这个属性的值被放到了值栈中,所以页面能够自动获取道这些值,并展示在相应的表单域里面。
3:struts.xml配置
?????? 首先是在list.jsp上跳转到名为userToUpdate的Action,然后让UserAction的toUpdate方法运行完之后,跳转到update.jsp页面,所以在配置的时候采取以下方式:
?
java代码:查看复制到剪贴板打印4:运行测试一下
?????? 先跳转到修改页面,如下图所示:
图12.3 修改用户页面
5:处理修改提交的Action
?????? 修改页面的初始化完成之后,用户将值修改成需要的数据,然后点击“修改按钮”向后台提交修改信息。在update.jsp上,点击“修改按钮”提交到名为userUpdate的Action去进行处理。
这时,UserAction的update方法只需要接收update.jsp传过来的参数,并把它更新到数据库即可,在向数据库更新完之后,仍然跳转到显示所有用户的页面,示例代码如下:
?
java代码:查看复制到剪贴板打印6:struts.xml配置
由于update方法最后调用了toList方法,而且update方法以toList方法的返回值作为自己的返回值,所以update方法的返回值实际上是“toList”。所以在配置struts.xml的时候要注意,这个<action>元素的<result>子元素应该以toList为名,最终指向到list.jsp。
?
java代码:查看复制到剪贴板打印现在,该考虑如何删除用户了。在做任何企业级应用软件时,在删除之前都必须进行询问,决不能让用户点击了删除按钮之后就马上删除,实现的方式很多,其中一种简单的方式就是使用javascript中的询问框confirm来完成这个功能。当用户选择确认删除之后,就真的执行删除并跳回到显示所有用户的页面就可以了。
1:准备删除的链接
在list.jsp页面上为每个用户准备一个删除的链接,这个链接自然也像修改一样应该放到table中,每一行都有一个,而且每行都把自己这行显示的用户的userid传给下一个Action:
?
java代码:查看复制到剪贴板打印在上面的代码中,“<a href="javascript:if (confirm('确认删除吗?')) window.location.href='/crud1/crud1/userDelete.action?user.userId=<s:property value='userId'/>'">删除</a>”这句话就是要进行删除的链接。虽然看着比较长,不要着急,一点一点来分析,对这个链接的href属性:
2:实现删除功能的Action
?????? 实现删除功能的Action,只需要接收list.jsp页面传过来的用户的userId,然后在数据库里把这个用户删除即可,删除后仍然跳转到显示全部用户的页面。示例代码如下:
?
java代码:查看复制到剪贴板打印3:struts.xml配置
由于delete方法最后调用了toList方法,而且delete方法以toList方法的返回值作为自己的返回值,所以delete方法的返回值实际上是“toList”。所以在配置struts.xml的时候要注意,这个<action>元素的<result>子元素应该以toList为名,最终指向到list.jsp。
?
java代码:查看复制到剪贴板打印在显示全部用户信息的list.jsp上放置一个转到查询页面的链接,点击这个链接可以跳转到输入查询条件页面。
1:准备跳转到查询页面的链接
先要在list.jsp页面上添加一个转到“查询用户”页面的链接:
?
java代码:查看复制到剪贴板打印点击这个链接,访问名为userToQuery的Action。
2:准备跳转到查询页面之前的Action
?????? 查询页面用一个下拉框来收集用户的性别。但它与添加页面和修改页面不同,添加页面和修改页面上的下拉框只需要“男”和“女”两个选项;但是查询页面上,可以不用性别作为条件,因此还要添加“请选择”这一项代表不以性别作为选项。
?????? 在之前准备添加页面时,已经用一个字符串数组sexs,存入了“男”和“女”,因此,现在的Action里面其实什么都不用做,示例代码如下:
?
java代码:查看复制到剪贴板打印3:准备查询页面
?????? 查询页面需要在性别下拉框中添上一项“请选择”,这一项传到下一个页面的值使用空字符串就可以了。
可是前面并没有在Action的字符串数组sexs上加上请选择这一项,那么页面上该怎么实现呢?很简单,使用select标签的headerKey和headerValue属性就可以了。
示例代码如下:
?
java代码:查看复制到剪贴板打印仔细的你肯定会发现,这里使用的标签写法跟以前不太一样,这里给userId、age和age2三个输入框设置了初始值0,这又是为何呢?
这是因为这几个值在Model中对应的都是int类型的数据,对于查询页面,这几个输入框都可以不填写任何值,那么提交到Action的时候就应该是“null”或者是空字符串,不管是哪一个值,Struts2都无法把它转换成Model需要的int类型的数据,从而导致报错,错误信息示例如下:
?
java代码:查看复制到剪贴板打印怎么处理这种情况呢?
?????? 其实,不处理都行,这个例外信息只是一个警告信息,程序照样能正常运行。如果不想看到这个警告信息,就可以采用上面添加初始值的方式。
在使用<s:select/>标签时,如果同时使用headerKey属性和headerValue属性,则会在第一项的位置添加一项<option>,其value就是headerKey属性的值,而显示的文字就是headerValue属性的值。
?????? 在检索页面上,要把所有的操作组合成一个UserQueryModel传给后一个Action,其中不仅包括UserModel原有的属性,还包括了表示年龄最大值的age2属性。
4:struts.xml配置
5:运行测试一下
?????? 在list.jsp上点击查询用户的超链接,将会跳转到查询页面,运行如下:
图12.4 查询用户页面
6:实现查询功能的Action
?????? 在实现查询功能的Action中,需要接收前一个页面传过来的组合查询条件数据,把这些数据组合称为一个UserQueryModel,然后把这个对象传给DAO,查询出合适的数据来,然后跳转到list.jsp,把这些数据展示出来即可。
?????? 注意:显示全部用户和显示符合查询条件的用户,可以共用list.jsp,list.jsp上只是把值栈中“list”(由<s:iterator/>标签引用的)的内容显示在页面上。所以,在查询之后,把查询结果放到Action的list属性就可以了。
Action现在需要接收用户查询条件的数据,因此只需要新建立UserQueryModel类型的uqm属性及其getter/setter即可。示例代码如下:
?
java代码:查看复制到剪贴板打印7:struts.xml配置
?
在以前的学习中都是简单的就知识而知识的学习,在本节中真正体会了如何综合使用所学的这些Struts2的核心知识。在这里,有两点要注意:
1:一组与同一个域对象相关的Action会有好多重复的代码,比如多个Action会拥有域对象类型的属性及其getter/setter。那么,可以把这一组Action实现为一个类,根据不同的配置别名去访问不同的方法,这样就减少了类的数量,同时减少了冗余代码。
注意:在本小节中多次出现的UserAction代码是以累加的方式逐步演化的,每步的UserAction里面只是列出新加的代码。因而完整的UserAction有好多方法,toList、toAdd、add、toUpdate、udpate、delete、toQuery、Query。
虽然一组Action合在一起了,但是struts.xml中的<action>标签仍然可以分开来配置的,它们之间也可能由相似的代码,比如<result name="toList">/jsp/list.jsp</result>就出现了好多次。那么,可不可以把它们也合在一起呢?合起来之后会有什么副作用呢?这个以后再说。
2:Struts2表单标签提供了良好的帮助,它的name属性还可以在页面初始化时取值,因此add.jsp与update.jsp之间的差别很小了。
要是使用普通的<input>标签,写法大致如下:
?
java代码:查看复制到剪贴板打印可以看出,使用普通的<input>标签,修改页面上还需要自己手动设置初始值。
?
私塾在线网站原创《研磨struts2》系列
转自请注明出处:【http://sishuok.com/forum/blogPost/list/4111.html】
欢迎访问http://sishuok.com获取更多内容
</action>