JavaEE项目实战(OA系统)之十三_员工管理之一
之前,我们已经完成了“部门管理”模块的练习,并初步掌握了ssh框架的编程。
下面,我们再做一个练习即“员工管理”模块,通过这个练习,将进一步熟悉ssh框架。
页面一,员工列表:
页面二,员工新增/修改:
“员工管理”模块重点解决几个关键点,一是部门下拉列表的展示,二是员工列表中如何显示部门名称,三是新增/编辑页面的展示细节。
一、部门下拉列表的展示
向数据库中加入如下示例数据:
部门表:
员工表:
1. SQL语句
我们想让部门下拉列表展示为:
可是用普通的SQL语句,不管怎么排序,是很难达到这个显示顺序的。
幸好,Oracle数据库针对树形数据,提供了connect by语法。
基本语法如下:
select * from tbl_dept
connect by prior dept_id=dept_p_id
start with dept_p_id=0;
第一行是select,用于指定查询内容。
第二行是connect by,用于指定连接条件,connect by有点类似于自连接,指定当前记录的一个字段与上一条记录的一个字段连接。其中prior这个单词表示“上一条记录”,它的位置不同,会产生两种效果:从根到叶和从叶到根。
第三行是start with,用于指定起始条件,从树的哪个位置开始。
我们以scott用户的emp表为例,emp(员工)表有empno(员工号)和mgr(上级领导的员工号),是典型的树形数据。
示例1:
select * from scott.emp
connect by prior mgr = empno
start with ename = 'SCOTT';
查询结果:
这条语句可以这样解释,connect by prior mgr = empno,即使用上一条记录的“上级领导的员工号”作为当前记录的“员工号”,start with ename = 'SCOTT',表示起始点为名为SCOTT的员工。然后下一个员工,就使用上一条记录的mgr字段作为当前记录的empno,产生的效果是从下级到上级。
示例2:
select * from scott.emp
connect by prior empno = mgr
start with ename = 'KING';
查询结果:
这条语句可以这样解释,connect by prior empno = mgr,即使用上一条记录的“员工号”作为当前记录的“上级领导的员工号”,start with ename = 'KING',表示起始点为名为KING的员工,KING是公司总裁。然后下一个员工,就使用上一条记录的empno字段作为当前记录的mgr,产生的效果是从上级到下级。从上级到下级时,对树的遍历算法使用先序遍历,即先访问根节点,再左树,再右树。
回到我们的问题,通过下面的SQL语句可以实现目的:
select * from tbl_dept
connect by prior dept_id=dept_p_id
start with dept_p_id=0;
其中,connect by prior dept_id=dept_p_id,会产生从上级到下级的效果,start with dept_p_id=0,符合条件的起始点会有多个,它们都是一级部门。
查询结果:
2. DAO类
修改一下接口,在接口中添加一个方法:
List<Dept2> getTree();
这里的Dept2,是一个自定义的实体类,代码如下:
public class Dept2 {
private int id;
private String name;
private int level;
// getter & setter
// ...
}
DAO类的代码如何编写呢?首先,我们要清楚Hibernate的查询有两种,一种使用HQL(Hibernate Query Language,是Hibernate的查询语言,功能很强大),它查询的是实体类;另外,Hibernate也可以用原生的SQL。
Hibernate进行HQL查询时,使用Query对象:
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("from Dept");
return (List<Dept>) query.list();
上述的connect by语法是Oracle特有的SQL语法,并没有相应的HQL,属于原生的SQL,需要使用Hibernate的SQLQuery对象。
SQLQuery对象的list方法返回的数据一般是List<Object[]>,是值的数组,不太方便,所以我们要将数据封装成自定义实体的列表:
public List<Dept2> getTree() {
String sql = "select dept_id id, dept_name name, level from tbl_dept connect by prior dept_id=dept_p_id start with dept_p_id=0";
Session session = sessionFactory.getCurrentSession();
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.addScalar("id", StandardBasicTypes.INTEGER);
sqlQuery.addScalar("name", StandardBasicTypes.STRING);
sqlQuery.addScalar("level", StandardBasicTypes.INTEGER);
sqlQuery.setResultTransformer(Transformers
.aliasToBean(Dept2.class));
List<Dept2> list = (List<Dept2>) sqlQuery.list();
return list;
}
在SQL语句中,为字段取别名,别名与自定义实体类的属性一致,然后调用addScalar方法一个个地添加字段,最后调用转换器将结果封装成自定义实体的列表。
至此,DAO类代码完成。
3. Action类
在员工管理的Action类中写一个方法,对列表数据加以处理:
private void getDeptTree() {
deptList = deptBiz.getTree();
for (Dept2 dept : deptList) {
String name = dept.getName();
if (dept.getLevel() > 1) {
name = "└" + name;
}
for (int i = 1; i < dept.getLevel(); i++) {
name = " " + name;
}
dept.setName(name);
}
}
4. JSP页面
要将部门列表展示在下拉框中,JSP页面使用如下代码:
请选择部门: <select id="deptId" name="deptId">
<option value="0">---请选择部门---</option>
<c:forEach items="${deptList}" var="dept">
<option value="${dept.id}"
<c:if test="${dept.id==deptId}">selected="selected"</c:if>>${dept.name}</option>
</c:forEach>
</select>
最终完成的部门下拉列表的展示效果如下: