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>

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

  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:空格千万不能省略.

原文地址:https://www.cnblogs.com/beihai2018/p/8472478.html

时间: 2024-10-16 21:03:26

Day03处理上级部门(树状结构)和用户管理的相关文章

部门下拉框的树状结构展示小功能的实现

在OA项目中部门管理模块中,添部门需要选择上级部门,此时如果直接将所有的部门从数据库中查询出来,让用户选择是可以的,但是显示出来的效果,不是很友好,这样会导致用户体验不好,因此在这里,我们可以一个小技巧来实现一个好的显示效果,那就是简单的树状结构,如图: 改进前: 改进后: 这是页面的显示效果,因此很有可能大家会从jsp页面下手,但是仔细想想这样会较为麻烦,比较标签的操作不是web后台程序员擅长,同时在jsp中实现该功能并不容易,因此可以考虑在给jsp发送数据之前就将其排序完成,此时就涉及到遍历

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

系统管理模块_部门管理_设计本模块中的所有实体并总结设计实体的技巧 设计实体流程 1,有几个实体? 一般是一组增删改查对应一个实体. 2,实体之间有什么关系? 一般是页面引用了其他的实体时,就表示与这个实体有关联关系. 3,每个实体中都有什么属性? 1,主键.推荐使用代理主键 2,关联关系属性.在类图中,关联关系是一条线,有两端,每一端对应一个表达此关联关系的属性.有几个端指向本类,本类中就有几个关联关系属性. 3,一般属性.分析所有有关的页面,找出表单中要填写的或是在显示页面中要显示的信息等.

分享使用NPOI导出Excel树状结构的数据,如部门用户菜单权限

大家都知道使用NPOI导出Excel格式数据 很简单,网上一搜,到处都有示例代码. 因为工作的关系,经常会有处理各种数据库数据的场景,其中处理Excel 数据导出,以备客户人员确认数据,场景很常见. 一个系统开发出来,系统要运行起来,很多数据要初始化,这个时候也是需要客户提供各种业务的基础数据.客户提供的数据中,其中除了word.pdf,最常见的就是Excel. 废话不多说,直接上图上代码: 如图, 左侧三列,作为 一个系统 所有菜单的树状结构. 其他列 以用户的信息(如用户名.登录名) 作为表

PostgreSQL递归查询实现树状结构查询

在Postgresql的使用过程中发现了一个很有意思的功能,就是对于需要类似于树状结构的结果可以使用递归查询实现.比如说我们常用的公司部门这种数据结构,一般我们设计表结构的时候都是类似下面的SQL,其中parent_id为NULL时表示顶级节点,否则表示上级节点ID. CREATE TABLE DEPARTMENT ( ID INTEGER PRIMARY KEY, NAME VARCHAR(32), PARENT_ID INTEGER REFERENCES DEPARTMENT(ID) );

树状结构Java模型、层级关系Java模型、上下级关系Java模型与html页面展示

树状结构Java模型.层级关系Java模型.上下级关系Java模型与html页面展示 一.业务原型:公司的组织结构.传销关系网 二.数据库模型 很简单,创建 id 与 pid 关系即可.(pid:parent_id) 三.Java模型 (我们把这张网撒在html的一张表里.其实用ul来展示会简单N多,自己思考为什么LZ会选择放在表里) private class Table {        private Long id; // 当前对象的id         private int x; /

使用Map辅助拼装树状结构,消除递归调用

目前菜单或其他树状结构在数据库中的存储,多数是以一个parentid作为关联字段,以一维形式存储.使用时全部查询出来,然后在内存中拼装成树状结构.现在主要涉及的是拼装方法的问题. 一般可以进行 递归调用来实现 过程如下:1.首先找到根节(s)点即没有上级节点的元素 2.根据找到的节点的id,遍历数据,查看parentid为此id的元素 将其 放到此元素的childlist中去3.对此元素的childlist 重复步骤2 2-3这个步骤的重复 一般通过递归来实现. 我采用的方法,是使用map作为辅

由简入繁实现Jquery树状结构

在项目中,我们经常会需要一些树状结构的样式来显示层级结构等,比如下图的样式,之前在学.net的时候可以直接拖个服务端控件过来直接使用非常方便.但是利用Jquery的一些插件,也是可以实现这些效果的,比如说Jquery.treeview.js插件. 下面就直入主题,开始从简入繁的分析怎么使用treeview插件,从已知的知识开始轻松入手,让树状结构唾手可得. 显示树状结构的几个实现步骤: 一.HTML做初始静态原型. 首先通过<ul></ul><li></li>

Hibernate学习笔记_关系映射_树状结构练习

1         树状结构的设计(至关重要) a) 在同—个类中使用One2Many和Many20ne @Entity public class Org { private int id; private String name; private Set<Org> children=new HashSet<Org>(); private Org parent; @Id @GeneratedValue public int getId() { return id; } public

权限模块_分配权限_显示树状结构_页面中的选中效果

权限模块__分配权限__显示树状结构1 显示树状结构:依赖关系 setPrivilegeUI.jsp <!-- 显示权限树 --> <td> <!--用它回显 <s:checkboxlist name="privilegeIds" list="#privilegeList" listKey="id" listValue="name"></s:checkboxlist> --