系统管理:部门管理-处理上级部门
添加或者修改时,上级部门的格式如图:.解决此功能将面临两个问题:1.将所有部门名称以下拉选的形式展示出来2.以树状结构展示.在此我们先解决问题1.
在添加页面中展示所有部门名称,并完成添加功能
DepartmentAction:
public String addUI() throws Exception { //准备数据 List<Department> departmentList = departmentService.findAll(); ActionContext.getContext().put("departmentList", departmentList);
return "saveUI"; } |
SaveUI:(以下拉选形式展示所有部门名称)
<tr><td width="100">上级部门</td> <td> <s:select name="parentId" cssClass="SelectStyle" list="departmentList" listKey="id" listValue="name" headerKey="" headerValue="==请选择=="> </s:select> </td> </tr> <tr><td>部门名称</td> <td><s:textfield name="name" cssClass="InputStyle"/> *</td> </tr> <tr><td>职能说明</td> <td><s:textarea name="description" cssClass="TextareaStyle"/></td> </tr> |
listKey是要提交过去的值,listValue是要在页面上展示的值.name是数据库中对应的字段.(可以这么理解:listKey对应的值要赋值给name对应的字段,在此为parentId=id)
若是在添加页面选择了上级部门,因为Deparent.java中只有parent对象,并没有parentId属性.(这里parentId是上级部门和下级部门的外键,数据库中有但是JavaBean中没有)
所以模型驱动封装不了页面提交过来的parentId,这时候要利用属性驱动:在Action中声明变量,并提供get,set方法:
完成添加页面后,要完成添加(上级部门)的功能:
DepartmentAction:
public String save() throws Exception { //处理上级部门 Department parent = departmentService.getById(parentId); model.setParent(parent);
departmentService.save(model); return "toList"; } |
自己做的时候,展示上级部门出现了问题,原因是list.jsp没修改好:
DepartmentAction:
public String list() throws Exception { List<Department> departmentList = departmentService.findAll(); ActionContext.getContext().put("departmentList", departmentList); return "list"; } |
List.jsp:
<s:iterator value="departmentList"> <tbody id="TableData" class="dataContainer" datakey="departmentList"> <tr class="TableDetail1 template"> <td><s:property value="name"/> </td> <td><s:property value="parent.name"/> </td> <td><s:property value="description"/> </td> <td><s:a action="department_delete?id=%{id}" onclick="return confirm(‘确定要删除么?‘)">删除</s:a> <s:a action="department_editUI?id=%{id}">修改</s:a><br/> </td> </tr> </tbody> </s:iterator> |
若是在添加页面上,未选择上级部门(即此部门为顶级部门),则程序会报错:
因为若是未选择上级部门,则表单会将headerKey=""提交过去,此时id为空,所以对应的parentId字段为空.headerKey="" headerValue="==请选择=="
解决办法:在BaseDaoImpl中的getById()加入对id的判断:
BaseDaoImpl:
public T getById(Long id) { if(id == null){ return null; } else{ return (T) getSession().get(clazz, id); } } |
完成修改页面的数据回显(显示原来的上级部门名称),并完成修改功能
DepartmentAction:
public String editUI() throws Exception { //准备回显的数据 Department department = departmentService.getById(model.getId()); ActionContext.getContext().getValueStack().push(department); if(department.getParent() != null){ this.parentId = department.getParent().getId(); }
//处理上级部门 List<Department> departmentList = departmentService.findAll(); ActionContext.getContext().put("departmentList", departmentList);
return "saveUI"; } |
SaveUI:
<tr><td width="100">上级部门</td> <td> <s:select name="parentId" cssClass="SelectStyle" list="departmentList" listKey="id" listValue="name" headerKey="" headerValue="==请选择=="> </s:select> </td> </tr> <tr><td>部门名称</td> <td><s:textfield name="name" cssClass="InputStyle"/> *</td> </tr> <tr><td>职能说明</td> <td><s:textarea name="description" cssClass="TextareaStyle"/></td> </tr> |
<S:…></S:…>自带回显功能,隐藏了value属性,value等于对应属性的值. (先从栈顶找对应属性的值,找不到再去map中找对应key值,所以action中最好将对应属性push进栈顶)
<s:select name="parentId" …></s:select>在这里,value=parentId,因为listValue=name,所以下拉选会回显name.
完成修改页面的回显功能后,再完成修改功能:
DepartmentAction:
public String edit() throws Exception { //获取要修改的数据库中的原始数据 Department department = departmentService.getById(model.getId()); //修改 department.setName(model.getName()); department.setDescription(model.getDescription()); department.setParent(departmentService.getById(parentId)); //修改数据库中的数据 departmentService.update(department); return "toList"; } } |
部门的级联删除,列表页面默认显示顶级部门列表,点击部门名称才会看到其相应的子部门列表
部门的级联删除(删除一个部门时,其子部门也会被删除)(inverse 是否维护关系cascade 级联)
想要级联删除哪个对象,就要在那个对象的映射文件中写上…如这里要级联删除子部门,
<!-- children属性,表达的是本对象与Department(下级)的一对多 --> <set name="children" cascade="delete"> <key column="parentId"></key> <one-to-many class="Department"/> </set> |
列表页面默认显示顶级部门列表,点击部门名称才会显示相应子部门列表:
DepartmentAction:
/** * 列表:列表页面只显示一层的(同级的)部门数据,默认显示最顶级的部门列表。 */ public String list() throws Exception { List<Department> departmentList = null; if(parentId == null){//默认显示顶级部门列表 departmentList = departmentService.findTopList(); }else{ //显示指定部门的子部门列表 departmentList = departmentService.findChildren(parentId); } ActionContext.getContext().put("departmentList", departmentList); return "list"; } |
Ctrl+1在departmentService中创建方法,在departmentServiceImpl中实现方法(调用departmentDao中的方法)….
DepartmentDaoImpl:
/** * 查找顶级部门列表 */ public List<Department> findTopList() { return getSession().createQuery(// "From Department d where d.parent.id IS NULL")// .list(); }
/** * 查找指定部门的子部门列表 */ public List<Department> findChildren(Long parentId) { return getSession().createQuery(// "From Department d where d.parent.id=?")// .setParameter(0, parentId) .list(); } |
List:
<!--显示数据列表--> <s:iterator value="departmentList"> <tbody id="TableData" class="dataContainer" datakey="departmentList"> <tr class="TableDetail1 template"> <td> <s:a action="department_list?parentId=%{id}">${name}</s:a> </td> <td><s:property value="parent.name"/> </td> <td><s:property value="description"/> </td> <td><s:a action="department_delete?id=%{id}" onclick="return confirm(‘确定要删除么?‘)">删除</s:a> <s:a action="department_editUI?id=%{id}">修改</s:a><br/> </td> </tr> </tbody> </s:iterator> |
解析:列表页面一开始默认显示顶级部门列表,当在列表页面点击某个部门名称后(parentId会被提交到DepartmentAction中的list方法中),列表页面就会显示相应的子部门列表了.
从列表转到添加页面时,默认选中原列表的上级部门;增加"返回上一级"的功能;添加,修改,删除成功后还转到原列表页面,而不是顶级列表页面,解决懒加载的问题
从列表转到添加页面时,下拉选默认显示原列表的上级部门名称:
DepartmentAction:
public String list() throws Exception { List<Department> departmentList = null; if(parentId == null){//默认显示顶级部门列表 departmentList = departmentService.findTopList(); }else{ //显示指定部门的子部门列表 departmentList = departmentService.findChildren(parentId); Department parent = departmentService.getById(parentId); ActionContext.getContext().put("parent", parent); } ActionContext.getContext().put("departmentList", departmentList); return "list"; }
public String addUI() throws Exception { //准备数据 List<Department> departmentList = departmentService.findAll(); ActionContext.getContext().put("departmentList", departmentList); return "saveUI"; } |
解析:这里departmentService.getById(parentId)有parentId,是因为list中的<s:a action="department_list?parentId=%{id}">${name}</s:a>.在列表页面每点击一个部门名称,就会传递parentId到Action中的list()方法重新查询列表.
若是没有parentmentId,则说明是顶级列表.
List:
<!--显示数据列表--> <s:iterator value="departmentList"> <tbody id="TableData" class="dataContainer" datakey="departmentList"> <tr class="TableDetail1 template"> <td> <s:a action="department_list?parentId=%{id}">${name}</s:a> </td> <td><s:property value="parent.name"/> </td> <td><s:property value="description"/> </td> <td><s:a action="department_delete?id=%{id}" onclick="return confirm(‘确定要删除么?‘)">删除</s:a> <s:a action="department_editUI?id=%{id}">修改</s:a><br/> </td> </tr> </tbody> </s:iterator> </table>
<!-- 其他功能超链接 --> <div id="TableTail"> <div id="TableTail_inside"> <s:a action="department_addUI?parentId=%{ #parent.id }"> <img src="${pageContext.request.contextPath}/style/images/createNew.png" /> </s:a> </div> </div> |
解析:<s:a action="department_addUI?parentId=%{ #parent.id }">不写#的话,会先从栈顶找,此时Action中的model(Deaprtment)真的有值,但model的parent值为空,所以会用这个空值.加上#的话,会直接从map中找.(在action中已经将parent put进map)
SaveUI:
<tr><td width="100">上级部门</td> <td> <s:select name="parentId" cssClass="SelectStyle" list="departmentList" listKey="id" listValue="name" headerKey="" headerValue="==请选择=="> </s:select> </td> </tr> |
解析:要让saveUI的下拉选中回显原上级部门名称,关键就是要让Action中有parentId(此时在Action中是用属性驱动的方式封装parentId的).因为<s:select name="parentId"…/>会先去栈顶找parentId,而此时在栈顶的是Action(因为在addUI中并没有将什么值push进栈顶),所以Action中的parentId自然会被找到.因此虽然Action中的addUI()方法并没有什么改变(没有特意去接收list传递过来的parentId),但是saveUI中<s:select name="parentId"…/>还是可以获取到Action中的parentId.
注意: <s:a action="department_addUI?parentId=%{ #parent.id }">是将parentId传递给addUI(),这样saveUI(addUI的返回值就是saveUI)才能拿到parentId.并不是只要让action中有parentId就好.
比如<s:a action="department_list?parentId=%{id}">${name}</s:a>就将parentId传过来了,但他是将parentId传递给list(),而不是给addUI(),所以saveUI()拿不到这个parentId.
重点是要将值栈,map,什么时候有值,值什么时候有效弄明白.
增加返回上一级的功能:
打个比方,有一级,二级,三级三个部门.一级为二级的上级部门,二级为三级的上级部门.现处在三级,若想回到二级,因为二级的上级部门为一级,所以得将二级的parentId设置为一级的Id.即将parentId=parent.parentId.id传递过去.
DepartmentAction:
public String list() throws Exception { List<Department> departmentList = null; if(parentId == null){//默认显示顶级部门列表 departmentList = departmentService.findTopList(); }else{ //显示指定部门的子部门列表 departmentList = departmentService.findChildren(parentId); Department parent = departmentService.getById(parentId); ActionContext.getContext().put("parent", parent); } ActionContext.getContext().put("departmentList", departmentList); return "list"; } |
这里departmentList是指定部门的子部门集合,而parent是那个指定的部门,在此的作用为存储id,便于赋值给parentId.虽说他们的parentId都一样,但departmentList的parentId并不好获取,所以parent并不多余.
List:
<!-- 其他功能超链接 --> <div id="TableTail"> <div id="TableTail_inside"> <s:a action="department_addUI?parentId=%{ #parent.id }"> <img src="${pageContext.request.contextPath}/style/images/createNew.png" /> </s:a> <!-- 不是顶级部门时,才需要显示‘返回上一级‘按钮 --> <s:if test="#parent != null"> <s:a action="department_list?parentId=%{ #parent.parent.id }">返回上一级</s:a> </s:if> </div> </div> |
解析:不加#的话,会先从栈顶找,而此时栈顶是Action,Action的model(Department)中有parent,但为空 (每个Action都会有model属性(模型驱动使然),但Action中的list()并没有用到model(没有什么值被传递过来),所以model为空),那么他就会用空值.加了#之后,他会直接去map中找.
添加,修改,删除后回到原列表页面,而不是顶级列表页面:
之前做完这些操作后都会回到顶级列表页面,这是因为增删改方法的返回值都是toList,而struts.xml中toList的跳转代码已经写死了.
<result name="toList" type="redirectAction">department_list</result>
所以想要回到原列表页面的话,就要修改Struts.xml:
<result name="toList" type="redirectAction">department_list?parentId=${parentId}</result>
他会回到Action中寻找parentId.
List:
<!--显示数据列表--> <s:iterator value="departmentList"> <tbody id="TableData" class="dataContainer" datakey="departmentList"> <tr class="TableDetail1 template"> <td> <s:a action="department_list?parentId=%{id}">${name}</s:a> </td> <td><s:property value="parent.name"/> </td> <td><s:property value="description"/> </td> <td><s:a action="department_delete?id=%{id}&parentId=%{parent.id}" onclick="return confirm(‘确定要删除么?‘)">删除</s:a> <s:a action="department_editUI?id=%{id}">修改</s:a><br/> </td> </tr> </tbody> </s:iterator> |
DepartmentAction:
public String delete() throws Exception { departmentService.delete(model.getId()); return "toList"; } |
由于之前的删除方法并没有传递parentId,所以Struts.xml中的<result name="toList" type="redirectAction">department_list?parentId=${parentId}</result>
取不到parentId.
可以在list中把parentId传过去,因为它在循环语句中,迭代的变量departmentList中的parent不为空,所以可以直接取他的parent的id.所以不用加#.(被put进map的parent和departmentList的parent的id是一样的)
解决懒加载的问题:
Web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 配置Spring的用于初始化ApplicationContext对象的监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param>
<!-- 配置Spring的OpenSessionInViewFilter以解决懒加载异常的问题 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>
<!-- 配置Struts2的核心的过滤器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> |
注意:解决懒加载问题的代码一定要放到Struts2的核心过滤器的前面,不然他不给放行.
展示树状结构.在选择或修改上级部门时,上级部门列表中不能显示当前修改的部门及其子孙部门。
使用递归遍历来展示树状结构.
遍历部门数,把所有部门名称都改掉后放到同一个list中返回,通过名称中的空格(这里是通过=)来表现层次
DepartmentAction:
public String addUI() throws Exception { /*//准备数据 List<Department> departmentList = departmentService.findAll(); ActionContext.getContext().put("departmentList", departmentList);*/
//准备数据 List<Department> topList = departmentService.findTopList(); List<Department> departmentList = DepartmentUtils.getAllDepartmentList(topList); ActionContext.getContext().put("departmentList", departmentList); return "saveUI"; }
public String editUI() throws Exception { //准备回显的数据 Department department = departmentService.getById(model.getId()); ActionContext.getContext().getValueStack().push(department); if(department.getParent() != null){ this.parentId = department.getParent().getId(); }
/*//处理上级部门 List<Department> departmentList = departmentService.findAll(); ActionContext.getContext().put("departmentList", departmentList);*/
//处理上级部门 List<Department> topList = departmentService.findTopList(); List<Department> departmentList = DepartmentUtils.getAllDepartmentList(topList); ActionContext.getContext().put("departmentList", departmentList); return "saveUI"; } |
DepartmentUtils:
public class DepartmentUtils { /** * 遍历部门数,把所有部门名称都改掉后放到同一个list中返回,通过名称中的空格(这里是通过=)来表现层次 * @author Tan * */ public static List<Department> getAllDepartmentList(List<Department> topList){ List<Department> list = new ArrayList<Department>(); walkTree(topList, "|-", list); return list; }
//递归遍历 private static void walkTree(Collection<Department> topList, String prefix, List<Department> list) { for (Department top : topList) { //创建副本,不要修改session缓存中的对象,最好使用副本 Department copy = new Department(); copy.setId(top.getId()); //顶点 copy.setName(prefix + top.getName()); list.add(copy); //注意:添加的是copy对象 //子树 walkTree(top.getChildren(), "="+prefix, list);//如果想使用空格来表现层次结构,就必须使用全角的空格,不然在html上只能显示一个空格. } } } |
Tips: 1.不要修改session缓存中的对象,最好使用副本,若是不使用副本:
Session中有一级缓存机制,在调用DepartmentUtils时,因为DepartmentUtils中并没有开事务,所有虽然在DepartmentUtils中修改了session缓存中的数据(改了部门名称),但是却不会将修改的数据更新到数据库.但若之后又开了一个事务:
数据库中的数据就会被更新.所以不要修改session缓存中的对象,最好使用副本,除非想要更新数据到数据库.
2. 如果想使用空格来表现层次结构,就必须使用全角的空格,不然在html上只能显示一个空格.
上级部门列表中不能显示当前修改的部门及其子孙部门。因为不能选择自已或自已的子部门作为上级部门。
DepartmentAction:
public String addUI() throws Exception { /*//准备数据 List<Department> departmentList = departmentService.findAll(); ActionContext.getContext().put("departmentList", departmentList);*/
//准备数据 List<Department> topList = departmentService.findTopList(); List<Department> departmentList = DepartmentUtils.getAllDepartmentList(topList, null); ActionContext.getContext().put("departmentList", departmentList); return "saveUI"; }
public String editUI() throws Exception { //准备回显的数据 Department department = departmentService.getById(model.getId()); //当前要修改的部门 ActionContext.getContext().getValueStack().push(department); if(department.getParent() != null){ this.parentId = department.getParent().getId(); }
/*//处理上级部门 List<Department> departmentList = departmentService.findAll(); ActionContext.getContext().put("departmentList", departmentList);*/
//处理上级部门 List<Department> topList = departmentService.findTopList(); List<Department> departmentList = DepartmentUtils.getAllDepartmentList(topList, department); ActionContext.getContext().put("departmentList", departmentList); return "saveUI"; } |
DepartmentUtils:
public class DepartmentUtils { /** * 遍历部门数,把所有部门名称都改掉后放到同一个list中返回,通过名称中的空格(这里是通过=)来表现层次 * @param topList * @param removedDepartment * 这个部门和这个部门的子孙部门都不要,如果为空,表示没有要移除的部门分支 * @author Tan * */ public static List<Department> getAllDepartmentList(List<Department> topList, Department removedDepartment){ List<Department> list = new ArrayList<Department>(); walkTree(topList, "|-", list, removedDepartment); return list; }
//递归遍历 private static void walkTree(Collection<Department> topList, String prefix, List<Department> list, Department removedDepartment) { for (Department top : topList) { //去掉指定部门的分支 if(removedDepartment.getId() != null && top.getId().equals(removedDepartment.getId())){ continue; }
//创建副本,不要修改session缓存中的对象,最好使用副本 Department copy = new Department(); copy.setId(top.getId()); //顶点 copy.setName(prefix + top.getName()); list.add(copy); //注意:添加的是copy对象 //子树 walkTree(top.getChildren(), "="+prefix, list, removedDepartment);//如果想使用空格来表现层次结构,就必须使用全角的空格,不然在html上只能显示一个空格. } } } |
添加时没有要移除的部门,所以可以穿个null过去.Continue:结束此次循环,进行下一次的循环.
效果:在此,研发部>二级研发部>三级研发部,则在修改研发部的页面上,上级部门下拉选中就不会出现二级和三级研发部,在修改二级研发部的页面上,上级部门下拉选中就不会出现三级研发部.
6.一些改进
修改代码模板(比如syso,让代码效率更高)
抽取BaseAction,其中声明了service还提供了对ModelDriven的支持.以后其他Action只要继承他就好了,不必再声明service和支持modeldriven.
BaseAction:
package cn.itcast.oa.base; import java.lang.reflect.ParameterizedType; import javax.annotation.Resource; import cn.itcast.oa.service.DepartmentService; import cn.itcast.oa.service.RoleService; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven;
public class BaseAction<T> extends ActionSupport implements ModelDriven<T>{
//------------声明service------------ @Resource protected DepartmentService departmentService; @Resource protected RoleService roleService;
//------------对ModelDriven的支持------------ protected T model;
public BaseAction() { //通过反射获取T的真实类型 try{ ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); Class<T> clazz = (Class<T>) pt.getActualTypeArguments()[0]; model = clazz.newInstance(); }catch(Exception e){ throw new RuntimeException(e); } } public T getModel() { return model; } } |
其他Action要做的修改,如DepartmentAction,只需继承BaseAction,提供类型,并将对service的声明和对modeldriven的支持删除即可.
public class DepartmentAction extends BaseAction<Department>
抽取JSP页面中的公共代码:
Header.jspf:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script language="javascript" src="${pageContext.request.contextPath}/script/jquery.js"></script> <script language="javascript" src="${pageContext.request.contextPath}/script/pageCommon.js" charset="utf-8"></script> <script language="javascript" src="${pageContext.request.contextPath}/script/PageUtils.js" charset="utf-8"></script> <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/style/blue/pageCommon.css" />
<script type="text/javascript"> </script> |
然后在其他jsp中静态包含Header.jspf
<%@ include file="/WEB-INF/jsp/public/header.jspf" %> |
将service和dao合并:
首先将有关dao的测试代码,Dao和DaoImpl包都删除,其次将BaseDao和BaseDaoImpl分别改名为DaoSupport和DaoSupportImpl,在DaoSupportImpl中开启事务(在类上加@Transactional).
此时其他service接口要继承DaoSupport接口,需要删掉service接口中的公共方法,但是非公共方法要保留.ServiceImpl要继承DaoSupportImpl,删除其中的公共方法,保留非公共的方法,再将原来相应DaoImpl中非公共方法的代码拷贝过来.如:
DepartmentService:
public interface DepartmentService extends DaoSupport<Department>{ /* *//** * 查询所有 *//* List<Department> findAll();
*//** * 删除 *//* void delete(Long id);
*//** * 添加 *//* void save(Department model);
*//** * 通过id查询 *//* Department getById(Long id);
*//** * 修改 *//* void update(Department department); */ /** * 查找顶级部门列表 */ List<Department> findTopList();
/** * 查找指定部门的子部门列表 */ List<Department> findChildren(Long parentId);
} |
DepartmentServiceImpl:
@Service @Transactional @SuppressWarnings("unchecked") public class DepartmentServiceImpl extends DaoSupportImpl<Department> implements DepartmentService {
/* @Resource private DepartmentDao departmentDao;
public List<Department> findAll() { return departmentDao.findAll(); }
*//** * 获取对象 *//* public Department getById(Long id) { return (Department) departmentDao.getById(id); }
*//** * 删除数据 *//* public void delete(Long id) { departmentDao.delete(id); }
*//** * 保存数据 *//* public void save(Department model) { departmentDao.save(model); }
*//** * 修改数据 *//* public void update(Department department) { departmentDao.update(department); } */ /** * 查找顶级部门列表 */ public List<Department> findTopList() { return getSession().createQuery(// "From Department d where d.parent.id IS NULL")// .list(); }
/** * 查找指定部门的子部门列表 */ public List<Department> findChildren(Long parentId) { return getSession().createQuery(// "From Department d where d.parent.id=?")// .setParameter(0, parentId) .list(); } } |
Tips:一定要在DaoSupportImpl中开启事务(在类上加@Transactional).
因为
@Transactional注解,当写到类上时:
1,对本类中的公共方法有效。
2,对子类中的公共方法有效(即这个注解可以被继承)
3,对父类中的方法无效!!!
二.用户管理,增删改查
创建代码模板crud(即增删改查):
详细代码如下:
/** 列表 */ public String list() throws Exception {
return "list"; }
/** 删除 */ public String delete() throws Exception {
return "toList"; }
/** 添加页面 */ public String addUI() throws Exception {
return "saveUI"; }
/** 添加 */ public String add() throws Exception {
return "toList"; }
/** 修改页面 */ public String editUI() throws Exception {
return "saveUI"; }
/** 修改 */ public String edit() throws Exception {
return "toList"; } |
在UserServiceImpl类上写不写@Transactional都可以,因为DaoSupportImpl中已经开启了事务管理.(因为@Transactional可以被继承)(不过最好写,方便看)
附用户管理源代码:
UserAction:
package cn.itcast.oa.view.action;
import java.util.HashSet; import java.util.List;
import org.apache.commons.codec.digest.DigestUtils; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller;
import com.opensymphony.xwork2.ActionContext;
import cn.itcast.oa.base.BaseAction; import cn.itcast.oa.domain.Department; import cn.itcast.oa.domain.Role; import cn.itcast.oa.domain.User; import cn.itcast.oa.util.DepartmentUtils;
@Controller @Scope("prototype") public class UserAction extends BaseAction<User>{
private Long departmentId; private Long[] roleIds;
/** 列表 */ public String list() throws Exception {
List<User> userList = userService.findAll(); ActionContext.getContext().put("userList", userList);
return "list"; }
/** 删除 */ public String delete() throws Exception {
userService.delete(model.getId());
return "toList"; }
/** 添加页面 */ public String addUI() throws Exception {
//准备数据 >>departmentList List<Department> topList = departmentService.findTopList(); List<Department> departmentList = DepartmentUtils.getAllDepartmentList(topList, null); ActionContext.getContext().put("departmentList", departmentList);
//准备数据 >>roleList List<Role> roleList = roleService.findAll(); ActionContext.getContext().put("roleList", roleList);
return "saveUI"; }
/** 添加 */ public String add() throws Exception { //封装数据 // >>处理关联的一个部门 model.setDepartment(departmentService.getById(departmentId)); // >>处理关联的多个岗位 List<Role> roleList = roleService.getByIds(roleIds); model.setRoles(new HashSet<Role>(roleList));
//保存到数据库 userService.save(model); return "toList"; }
/** 修改页面 */ public String editUI() throws Exception { //准备回显的数据 User user = userService.getById(model.getId()); ActionContext.getContext().getValueStack().push(user); //处理部门 if(user.getDepartment() != null){ departmentId = user.getDepartment().getId(); } //处理岗位 roleIds = new Long[ user.getRoles().size() ]; int index = 0; for (Role role : user.getRoles()) { roleIds[ index++ ] = role.getId(); }
//准备数据 >>departmentList List<Department> topList = departmentService.findTopList(); //将用户放到哪个部门都可以,所以没有要移除的部门 List<Department> departmentList = DepartmentUtils.getAllDepartmentList(topList, null); ActionContext.getContext().put("departmentList", departmentList);
//准备回显的数据 >>roleList List<Role> roleList = roleService.findAll(); ActionContext.getContext().put("roleList", roleList);
return "saveUI"; }
/** 修改 */ public String edit() throws Exception { //从数据库中取出原对象 User user = userService.getById(model.getId());
//设置要修改的属性 user.setDepartment(departmentService.getById(departmentId));
user.setLoginName(model.getLoginName()); user.setName(model.getName()); user.setGender(model.getGender()); user.setPhoneNumber(model.getPhoneNumber()); user.setEmail(model.getEmail()); user.setDescription(model.getDescription());
user.setRoles(new HashSet<Role>(roleService.getByIds(roleIds)));
//保存到数据库 userService.save(user); return "toList"; }
/** * 初始化密码为1234 */ public String initPassword() throws Exception { //从数据库中取出原对象 User user = userService.getById(model.getId()); //设置要修改的属性 String md5Hex = DigestUtils.md5Hex("1234");//密码要使用md5摘要 user.setPassword(md5Hex); //保存到数据库 userService.save(user);
return "toList"; }
public Long getDepartmentId() { return departmentId; } public void setDepartmentId(Long departmentId) { this.departmentId = departmentId; } public Long[] getRoleIds() { return roleIds; } public void setRoleIds(Long[] roleIds) { this.roleIds = roleIds; } } |
- Tips:这里使用MD5加密的方法来加密,而这种方式是API的方式,所以要导入jar包:.
UserService:
public interface UserService extends DaoSupport<User>{
} |
UserServiceImpl:
@Service @Transactional public class UserServiceImpl extends DaoSupportImpl<User> implements UserService{
@Override /** * 初始密码为1234 */ public void save(User user) { String md5Hex = DigestUtils.md5Hex("1324"); //要使用md5摘要 user.setPassword(md5Hex); //保存到数据库 getSession().save(user); } } |
Tips:这里是重写了save()方法,创建用户的同时就将密码初始化为"1234".
Struts.xml:
<!-- 用户管理 --> <action name="user_*" class="userAction" method="{1}"> <result name="list">/WEB-INF/jsp/userAction/list.jsp</result> <result name="saveUI">/WEB-INF/jsp/userAction/saveUI.jsp</result> <result name="toList" type="redirectAction">user_list</result> </action> |
List.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <title>用户列表</title> <%@include file="/WEB-INF/jsp/public/header.jspf" %> </head> <body>
<div id="Title_bar"> <div id="Title_bar_Head"> <div id="Title_Head"></div> <div id="Title"><!--页面标题--> <img border="0" width="13" height="13" src="${pageContext.request.contextPath}/style/images/title_arrow.gif"/> 用户管理 </div> <div id="Title_End"></div> </div> </div>
<div id="MainArea"> <table cellspacing="0" cellpadding="0" class="TableStyle">
<!-- 表头--> <thead> <tr align=center valign=middle id=TableTitle> <td width="100">登录名</td> <td width="100">姓名</td> <td width="100">所属部门</td> <td width="200">岗位</td> <td>备注</td> <td>相关操作</td> </tr> </thead>
<!--显示数据列表--> <tbody id="TableData" class="dataContainer" datakey="userList"> <s:iterator value="userList"> <tr class="TableDetail1 template"> <td>${loginName} </td> <td>${name} </td> <td>${department.name} </td>
<td> <s:iterator value="roles"> ${name} </s:iterator> </td>
<td>${description} </td> <td><s:a action="user_delete?id=%{id}" onClick="return delConfirm()">删除</s:a> <s:a action="user_editUI?id=%{id}" >修改</s:a> <s:a action="user_initPassword?id=%{id}" onClick="return window.confirm(‘您确定要初始化密码为1234吗?‘)">初始化密码</s:a> </td> </tr> </s:iterator> </tbody> </table>
<!-- 其他功能超链接 --> <div id="TableTail"> <div id="TableTail_inside"> <s:a action="user_addUI"><img src="${pageContext.request.contextPath}/style/images/createNew.png" /></s:a> </div> </div> </div>
</body> </html> |
Tips:在遍历userList时,roles也要被遍历出来,这里roles只是user对象的一个属性.
正常情况下EL表达式是不能搜索值栈中的数据的,但在Struts2环境中却可以,这是因为Struts2对其进行了增强:
所以EL表达式的查找顺序:
1, 原始的顺序:page, request, session, application
2, 在Struts2中:page, request, ValueStack(即值栈), session, application
所以在list.jsp中对userList进行的迭代,可以用EL表达式.(现在用的也是EL表达式)
addUI.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <title>用户信息</title> <%@include file="/WEB-INF/jsp/public/header.jspf" %> </head> <body>
<!-- 标题显示 --> <div id="Title_bar"> <div id="Title_bar_Head"> <div id="Title_Head"></div> <div id="Title"><!--页面标题--> <img border="0" width="13" height="13" src="${pageContext.request.contextPath}/style/images/title_arrow.gif"/> 用户信息 </div> <div id="Title_End"></div> </div> </div>
<!--显示表单内容--> <div id=MainArea> <s:form action="user_%{id == null ? ‘add‘ : ‘edit‘}"> <s:hidden name="id"></s:hidden> <div class="ItemBlock_Title1"><!-- 信息说明 --><div class="ItemBlock_Title1"> <img border="0" width="4" height="7" src="${pageContext.request.contextPath}/style/blue/images/item_point.gif" /> 用户信息 </div> </div>
<!-- 表单内容显示 --> <div class="ItemBlockBorder"> <div class="ItemBlock"> <table cellpadding="0" cellspacing="0" class="mainForm"> <tr><td width="100">所属部门</td> <td> <s:select name="departmentId" cssClass="SelectStyle" list="departmentList" listKey="id" listValue="name" headerKey="" headerValue="==请选择=="> </s:select> </td> </tr> <tr><td>登录名</td> <td><s:textfield type="text" name="loginName" cssClass="InputStyle"/> * (登录名要唯一) </td> </tr> <tr><td>姓名</td> <td><s:textfield type="text" name="name" cssClass="InputStyle"/> *</td> </tr> <tr><td>性别</td> <td> <s:radio name="gender" list="%{ {‘男‘ , ‘女‘} }"></s:radio> </td> </tr> <tr><td>联系电话</td> <td><s:textfield type="text" name="phoneNumber" cssClass="InputStyle"/></td> </tr> <tr><td>E-mail</td> <td><s:textfield type="text" name="email" cssClass="InputStyle"/></td> </tr> <tr><td>备注</td> <td><s:textarea name="description" cssClass="TextareaStyle"></s:textarea></td> </tr> </table> </div> </div>
<div class="ItemBlock_Title1"><!-- 信息说明 --><div class="ItemBlock_Title1"> <img border="0" width="4" height="7" src="${pageContext.request.contextPath}/style/blue/images/item_point.gif" /> 岗位设置 </div> </div>
<!-- 表单内容显示 --> <div class="ItemBlockBorder"> <div class="ItemBlock"> <table cellpadding="0" cellspacing="0" class="mainForm"> <tr> <td width="100">岗位</td> <td> <s:select multiple="true" cssClass="SelectStyle" size="10" name="roleIds" list="roleList" listKey="id" listValue="name"> </s:select>按住Ctrl键可以多选或取消选择 </td> </tr> </table> </div> </div>
<!-- 表单操作 --> <div id="InputDetailBar"> <input type="image" src="${pageContext.request.contextPath}/style/images/save.png"/> <a href="javascript:history.go(-1);"><img src="${pageContext.request.contextPath}/style/images/goBack.png"/></a> </div> </s:form> </div>
<div class="Description"> 说明:<br /> 1,用户的登录名要唯一,在填写时要同时检测是否可用。<br /> 2,新建用户后,密码被初始化为"1234"。<br /> 3,密码在数据库中存储的是MD5摘要(不是存储明文密码)。<br /> 4,用户登录系统后可以使用"个人设置→修改密码"功能修改密码。<br /> 5,新建用户后,会自动指定默认的头像。用户可以使用"个人设置→个人信息"功能修改自已的头像<br /> 6,修改用户信息时,登录名不可修改。 </div>
</body> </html> |
Tip:图示效果其实也是由下拉选来完成的,只不过添加了multiple="true",这样就可以多选了.
在添加用户时,若是没有选择任一岗位,则会报错,因为ids为空,故要对DaoSupportImpl进行相应修改:
DaoSupportImpl:
/** * 根据id数组查询多个 */ public List<T> getByIds(Long[] ids) { if(ids == null || ids.length == 0){ return Collections.EMPTY_LIST; }
// 注意空格 sql语句:from与类名之间有一空格不能省略 类名与where之间有一空格不能省略 return getSession().createQuery(// "from " + clazz.getSimpleName() + " where id in (:ids)")// .setParameterList("ids", ids)//注意:一定要使用setParameterList()方法 .list(); } |
Tips:空格千万不能省略.