Day03处理上级部门(树状结构)和用户管理

Posted beihai2018

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day03处理上级部门(树状结构)和用户管理相关的知识,希望对你有一定的参考价值。

  1. 系统管理:部门管理-处理上级部门

    添加或者修改时,上级部门的格式如图:技术分享图片.解决此功能将面临两个问题:1.将所有部门名称以下拉选的形式展示出来2.以树状结构展示.在此我们先解决问题1.

  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"/>&nbsp;</td>

                    <td><s:property value="parent.name"/>&nbsp;</td>

                    <td><s:property value="description"/>&nbsp;</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);

        }

    }

  1. 完成修改页面的数据回显(显示原来的上级部门名称),并完成修改功能

    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";

    }

}

  1. 部门的级联删除,列表页面默认显示顶级部门列表,点击部门名称才会看到其相应的子部门列表

    部门的级联删除(删除一个部门时,其子部门也会被删除)(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"/>&nbsp;</td>

                    <td><s:property value="description"/>&nbsp;</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方法中),列表页面就会显示相应的子部门列表了.

  1. 从列表转到添加页面时,默认选中原列表的上级部门;增加"返回上一级"的功能;添加,修改,删除成功后还转到原列表页面,而不是顶级列表页面,解决懒加载的问题

    从列表转到添加页面时,下拉选默认显示原列表的上级部门名称:

    技术分享图片

    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"/>&nbsp;</td>

                    <td><s:property value="description"/>&nbsp;</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"/>&nbsp;</td>

                    <td><s:property value="description"/>&nbsp;</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>

 

    <!-- 配置SpringOpenSessionInViewFilter以解决懒加载异常的问题 -->

    <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的核心过滤器的前面,不然他不给放行.

  1. 展示树状结构.在选择或修改上级部门时,上级部门列表中不能显示当前修改的部门及其子孙部门。

    使用递归遍历来展示树状结构.

    遍历部门数,把所有部门名称都改掉后放到同一个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}&nbsp;</td>

<td>${name}&nbsp;</td>

<td>${department.name}&nbsp;</td>

 

<td>

<s:iterator value="roles">

    ${name}

</s:iterator>&nbsp;

</td>

 

<td>${description}&nbsp;</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:空格千万不能省略.

以上是关于Day03处理上级部门(树状结构)和用户管理的主要内容,如果未能解决你的问题,请参考以下文章

如何用报表工具实现树状层级结构的填报表

如何用报表工具实现树状层级结构的填报表

如何用报表工具实现树状层级结构的填报表

Day03-01阿铭Linux-用户和组管理

系统管理模块_部门管理_设计(映射)本模块中的所有实体并总结设计实体的技巧_懒加载异常问题_树状结构

帮助树状结构