入离职管理系统,是我入职这个公司开发的第一个完全参与的项目,现在拿出来做Java的练习。入离职管理系统的功能如下:
- 管理员账号注册和登录;
- 录入新员工信息;
- 员工入职流程(简化流程,只设置一级审批);
- 员工离职流程(简化流程,只设置一级审批);
- 员工入职流程审批通过后,自动创建系统登录账号;
- 员工离职流程审批通过后,自动删除系统登录账号;
- 展示员工信息表(管理员可见所有员工的信息,普通员工只能看到自己的信息 )。
在搭建完开发环境后,开始着手做第一个功能:管理员账号的注册和登录。在做的过程中,发现dao层、action层的类可以进行抽取,减少冗余代码。
一、抽取Dao层
Dao层的作用是对数据库表进行操作,不涉及任何业务逻辑,而对数据库的操作最基本的是增、删、改、查,那么这部分功能可以抽取出来,因为每一个dao类都需要对数据库进行操作。另外,进行数据库的操作需要获取SessionFactory对象,所以最终抽取的内容为SessionFactory对象和数据库的增删改查方法。抽取后的BasicDao如下:
package com.entry_exit.dao.impl;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.entry_exit.dao.BasicDao;
public class BasicDaoImpl<T> implements BasicDao<T> {
private Class clazz; //clazz中存储了当前操作的类型,即泛型T
private SessionFactory sessionFactory;
public BasicDaoImpl(){
System.out.println("this代表的是当前调用构造方法的对象" + this);
System.out.println("获取当前this对象的父类信息" + this.getClass().getSuperclass());
System.out.println("获取当前this对象的父类信息(包括泛型信息)" + this.getClass().getGenericSuperclass());
ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
clazz = (Class) type.getActualTypeArguments()[0];
System.out.println(clazz);
System.out.println(clazz.getSimpleName());
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
protected Session getSession() {
//从当前线程获取session,如果没有则创建一个新的session
return sessionFactory.getCurrentSession();
}
@Override
public void save(T t) {
// TODO Auto-generated method stub
getSession().save(t);
}
@Override
public void update(T t) {
// TODO Auto-generated method stub
getSession().update(t);
}
@Override
public void delete(int id) {
// TODO Auto-generated method stub
String hql = "delete " + clazz.getSimpleName() + " as c where c.id=:id";
getSession().createQuery(hql).setInteger("id", id).executeUpdate();
}
@Override
public T get(int id) {
// TODO Auto-generated method stub
return (T) getSession().get(clazz, id);
}
@Override
public List<T> query(int page, int rows) {
// TODO Auto-generated method stub
String hql = "from " + clazz.getSimpleName();
return getSession().createQuery(hql).setFirstResult((page-1)*rows).setMaxResults(rows).list();
}
}
具体的Dao类继承BasicDao类即可,修改后的UserDaoImpl类得到了简化,如下:
package com.entry_exit.dao.impl;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.entry_exit.dao.UserDao;
import com.entry_exit.vo.UserVo;
public class UserDaoImpl extends BasicDaoImpl<UserVo> implements UserDao {
@Override
public void delete(String accountid) {
// TODO Auto-generated method stub
String hql = "delete UserVo u where u.accountid = :accountid";
getSession().createQuery(hql).setString("accountid", accountid).executeUpdate();
}
@Override
public UserVo get(String accountid, String password) {
// TODO Auto-generated method stub
UserVo user = null;
String hql = "from UserVo where accountid = :accountid and password = :password";
user = (UserVo) getSession().createQuery(hql).setString("accountid", accountid).setString("password", password).uniqueResult();
return user;
}
}
上面的UserDaoImpl类中的方法是在BasicDaoImpl类中没有的方法,是需要实现业务逻辑而专门写的方法。其中,delete(String accountid)方法在注销账号时调用;get(String accountid, String password)方法在登录账号时调用。
二、Service层
Service层主要用于处理具体业务逻辑,而业务逻辑各不相同,基本没有抽取基本类的可能,这里列出登录和注册的方法代码:
package com.entry_exit.service.impl;
import com.entry_exit.dao.UserDao;
import com.entry_exit.service.UserService;
import com.entry_exit.vo.UserVo;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void register(UserVo user) {
// TODO Auto-generated method stub
if(user != null){
userDao.save(user);
}
}
@Override
public UserVo login(String accountid, String password) {
// TODO Auto-generated method stub
if(accountid != null && password != null){
return userDao.get(accountid, password);
}
return null;
}
}
抽取Action层
Action层用于和页面进行交互,根据页面的请求调用Service层的服务,并将服务返回的结果返回给页面。直接上代码:
package com.entry_exit.action;
import java.lang.reflect.ParameterizedType;
import java.util.Map;
import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class BasicAction<T> extends ActionSupport implements SessionAware, ApplicationAware, RequestAware, ModelDriven<T> {
//域对象
protected Map<String, Object> request;
protected Map<String, Object> session;
protected Map<String, Object> application;
@Override
public void setRequest(Map<String, Object> request) {
// TODO Auto-generated method stub
this.request = request;
}
@Override
public void setApplication(Map<String, Object> application) {
// TODO Auto-generated method stub
this.application = application;
}
@Override
public void setSession(Map<String, Object> session) {
// TODO Auto-generated method stub
this.session = session;
}
protected T model;
@Override
public T getModel() {
// TODO Auto-generated method stub
ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
Class clazz = (Class)type.getActualTypeArguments()[0];
try {
model = (T)clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
return model;
}
}
可以看到,BasicAction实现了四个接口,分别是SessionAware、 ApplicationAware、RequestAware、ModelDriven。其中,实现SessionAware、 ApplicationAware、RequestAware这三个接口,是为了方便将Service层服务穿回来的值返回到页面相应的域中;实现ModelDriven接口是为了方便的获取页面属性,并set进相应的实体类中。关于ModelDriven接口的知识,可以参考博文Struts2中的ModelDriven机制及其运用 。
四、注意事项
注意事项集中在配置文件中。
1、在抽取Dao层时,BasicDaoImpl是泛型类,所以不能在启动tomcat时直接注入,需要加入lazy-init=”true”属性,如下:
<!-- 因为basicDao是泛型类,所以必须加上lazy-init="true"属性 -->
<bean id="basicDao" class="com.entry_exit.dao.impl.BasicDaoImpl" lazy-init="true">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
2、其余的Dao类需要指定parent属性,如下:
<bean id="userDao" class="com.entry_exit.dao.impl.UserDaoImpl" parent="basicDao"></bean>
3、在配置action类的bean时,需要加上scope=”prototype”属性,无论是BasicAction还是其他的Action,都需要加上这个属性,如下:
<!-- 装配action类的bean -->
<bean id="basicAction" class="com.entry_exit.action.BasicAction" scope="prototype">
</bean>
<bean name="userAction" class="com.entry_exit.action.UserAction" parent="basicAction" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
这样做的原因,是为了确保每一次请求都是一个新的action对象,解决了实现ModelDriven接口后造成的问题,详见博文sping+struts2中配置Action的bean中scope=”prototype”的作用。
4、因为实现了ModelDriven接口,struts.xml文件和jsp页面也变得简洁了很多。
(1)struts.xml文件:action的name属性和method属性需要修改如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" extends="struts-default">
<action name="user_*" class="userAction" method="{1}">
<result name="home" type="redirect">/home.jsp</result>
<result name="login" type="redirect">/login.jsp</result>
</action>
</package>
</struts>
(2)jsp页面变得简单了,以注册页面为例,name属性的值与vo类的属性名一致即可,不需写成UserVo.accountid这样的形式:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="user_register.action" method="post">
<div>
<table>
<tr>
<td>Accountid:</td>
<td><input type="text" name="accountid"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td>FullName:</td>
<td><input type="text" name="fullname"></td>
</tr>
<tr>
<td>Role</td>
<td>
<select>
<option value="fme" selected="selected">FME</option>
<option value="manager">Manager</option>
</select>
</td>
</tr>
</table>
<input type="submit" value="Submit">
</div>
</form>
</body>
</html>