OA的学习--第五天的内容--优化和权限模块的初步功能

    第五天的学习,是从35集到41集,这7集的内容,改进了Service和Dao,去掉了Dao层,将Service层改为Service+Dao层.并且实现了用户的一些功能,以及对密码的MD5摘要处理,这个主要是引入一个jar包commons-codec.jar,调用里面的DigestUtils.md5Hex("字符串")就可以了.还有就是进行了权限的设计,包括分析功能,设计类和写完类的映射文件,以及权限数据和超级用户数据的初始化.

Service层与Dao层合并

    首先,介绍Service与Dao层的合并.
    首先是,他们的合并的原因,从下图中可以看到这样的调用关系,在RoleAction中调用RoleServiceImpl,而RoleServiceImpl又调用RoleDaoImpl,但是对于简单的增删改查来说,DaoImpl并没有写任何代码,而中间的Service的实现也就是调用了Dao的实现,并没有加什么业务.所以在这种情况下,可以直接让Service中调用Dao,从而去掉现在Dao层,省掉一个接口和一个实现类.

    可以理解成这样的修改,从原来的View + Service + Dao,改为了View + Service(原Service + 原Dao).这样,三层就变两层了,中小型的项目可以这样做,但是大型项目还是需要严格的三层来做.毕竟三层的本来目的是为了解耦和,面对接口编程,灵活应对变化,这样后期维护也会容易些,而且据说三层比二层更稳定,更安全,
    所以将所有的Dao和DaoImpl都用@Deprecated,做过期注解.而调用Dao和DaoImpl的Service层,这样修改.将原来Dao和DaoImpl继承的BaseDao接口和BaseDaoImpl类,修改名为DaoSupport接口和DaoSupportImpl实现类.(不改名也行,只要不要搞错.)然后让Service的接口和Service的实现分别继承DaoSupport和DaoSupportImpl,这样Service中对于基本的增删改查就不用处理了,只需要处理一些其他的方法了.如DepartmentService,部门Service接口,基础了DaoSupport,不用些findAll()等等方法了,因为DaoSupport中都提供了.只需要写上自己的findTopList()这样的方法
public interface DepartmentService extends DaoSupport<Department> {

	/* 查询 */
//	List<Department> findAll();
//  /* 删除 */
//	void delete(Long id);
//  /* 添加 */
//	void add(Department model);
//  /* 根据Id查询实体 */
//	Department getById(Long id);
//  /* 更新 */
//	void update(Department department);
    //找到所有顶级部门
	List<Department> findTopList();
    //查找该部门下的所有子部门
	List<Department> findChildren(Long parentId);

}
     这样,做完之后,若再新建Action,只需要写一个Service接口和Service的实现类就可以了,不用再写Dao和Dao的实现了.但是这样还是会有问题,在删除的时候,删除需要在事务中,但是@Transactional注解,只在Service实现类上才有,而真正的删除是在Dao中,注解是可以继承的,但是继承只是对子类有效,对父类无效,所以在DaoSupportImpl中的真正的删除是不在事务中的,所以无法删除.解决的做法就是将@Transactional ,写到DaoSupportImpl中,这样DaoSupportImpl就在事务中,而它所有的的子类也是在事务中的.所以现在就是这样了.

    而现在的结构就是这样的.MyAction继承了BaseAction,而BaseAction又继承了ActionSupport,并且BaseAction提取了公共代码,实例化Service,以及注入model实体.而MyAction通过调用Service的接口,而调用了Service的实现类,并且这两个都提取了一些公共代码,方便公用.DaoSupport是增删改查的基本方法的接口,而DaoSupportImpl则是实现了的增删改查方法.

用户管理的实现以及MD5的密码实现

    接下来是用户管理的实现,并且将流程做了一个总结.下面的图片就是总结的实现流程

    首先写userAction,继承BaseAction,并且写上注解@Controller和@Scope("prototype") 分别表示userAction交由容器管理,以及是多例的.每调用一次,生成一个userAction对象.然后由于userAction需要调用Service,所以创建userAction的userService接口和实现类.并且该Impl实现类中需要继承DaoSupportImpl和实现Service接口,以及写上注解@Service,表示将该Service放到容器中.若实现类中有方法不是基本的增删改查,则需要在Service的实现中,注入sessionFactory,写新方法用它来完成功能.举例,如

@Service
@Transactional
@SuppressWarnings("unchecked")
public class DepartmentServiceImpl extends DaoSupportImpl<Department> implements DepartmentService {
 
	@Resource
	private SessionFactory sessionFactory;
  
	public List<Department> findTopList() {
		return sessionFactory.getCurrentSession().createQuery(//
		"FROM Department d WHERE d.parent IS NULL")//
		.list();
	}

	public List<Department> findChildren(Long parentId) {
		return sessionFactory.getCurrentSession().createQuery(//
				"FROM Department d WHERE d.parent.id = ?")//
				.setParameter(0, parentId)//
				.list();
	 
	}
}

    然后在BaseAction中将该Service注入.

@Resource
protected UserService userService;

   再在struts.xml文件中写上新的Action配置

<!-- 用户管理 -->
<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?parentId=${parentId}</result>
</action>

    最后,只要写Action的方法,以及JSP页面就可以了.普通的增删改查就直接调用,Service方法就可以,但是对于跳转到页面,往往需要准备数据;以及添加数据的时候,页面传递过来的是id或者id数组,但是该属性为实体或者实体集合,所以还需要单独处理.

    如添加页面需要准备树状部门列表和岗位列表

/**
 * 添加页面
 * @return
 * @throws Exception
 */
public String addUI() throws Exception {
	//准备树状部门列表
	List<Department> topList = departmentService.findTopList();
	List<Department> departmentList = DepartmentUtils.getAllDepartments(topList);
	ActionContext.getContext().put("departmentList", departmentList);
	
	//准备岗位列表
	List<Role> roleList = roleService.findAll();
	ActionContext.getContext().put("roleList",roleList);
	
	return "saveUI";
}
       以及添加的时候需要根据传递的departmentId和roleIds来获取部门实体和岗位集合.
/**
 * 添加
 * @return
 * @throws Exception
 */
public String add() throws Exception {
	
	//设置所有部门
	model.setDepartment(departmentService.getById(departmentId));
	//设置关联的岗位
	List<Role> roleList = roleService.getByIds(roleIds);
	model.setRoles(new HashSet<Role>(roleList));
	
	//设置默认密码为1234
	String md5Digest = DigestUtils.md5Hex("1234");
	model.setPassword(md5Digest);
	
//		model.setPassword("1234");
	
	userService.save(model);
	return "toList";

}

    其中由于要求设置用户名初始化密码为"1234",但是存储明文在数据库中一看就知道密码了,所以需要对密码加密,引入commons-codec.jar,调用它的md5Hex()方法就可以,将1234加密,变成一段看不懂的内容.

    在数据库中就是这样,其中81dc9bdb52d04dc20036dbd8313ed055就是1234的md5加密之后的结果.

权限的设计

    权限的设计,分析权限.所谓权限就是通过对url的访问控制,来达到对某些角色,某些功能可用,某些功能不可用的作用.
    设定权限模型就是这样的,有用户和角色,以及权限.其中用户和角色是多对多的关系,一个用户可以有多个角色,一个角色可以包含多个用户;角色和权限也是多对多的关系,一个角色有多个权限,一个权限属于多个角色.  用户的权限就是该用户所有角色的权限的合集,并且判断一个功能是否能被某用户使用,只需要通过判断用户权限是否有这个功能的使用许可就可以了.

    所有建立用户和角色和权限的实体和实体的映射文件.用户和角色都已经建立,只需要建立权限,以及修改一下角色,因为角色和权限还有关系,所以要加一个权限的属性;他们的类图就是这样

    在Role(可以翻译为角色或者岗位)中添加privileges 角色集合属性.
//角色和权限的关系(多对多)
private Set<Privilege> privileges = new HashSet<Privilege>();
    新建的权限实体,是树形结构,本类和parent(上级)是多对1,而本类对children(下级)是1对多.
/**
 * 权限类
 * @author liu
 *
 */
public class Privilege {

	//主键
	private Long id;
	//角色集合
	private Set<Role> roles = new HashSet<Role>();
	private String url;//路径
	private String name;//权限名称
	
	//权限是树形结构,
	//上级
	private Privilege parent;
	//下级
	private Set<Privilege> children = new HashSet<Privilege>();
	 
	public Privilege() {};
	public Privilege( String name,String url,Privilege parent) {
		this.url = url;
		this.name = name;
		this.parent = parent;
	}
	
	//getter,setter方法省略
 }
    建立映射文件,就是之前说的填空的方式.先写注释,然后再按注释填空,将属性填到name中,将类填到class中,并且多对多,一对多/多对一两两为1对,对着填就好了.由于用户和角色的关系已经写好了,不用在写映射文件了,所以只用修改Role的映射文件以及添加一个权限privilege的映射文件.
    其中,Role中的权限是多对多的关系.注释是关系是多对多,并且权限类型是set集合,所以用set,并且要有table第三张表,而key对应的是该映射文件的主键,它作为权限的外键存在.写好之后,如"roleId",拷到和他一对的manytomany标签的column属性中.

<!-- privileges属性,本类与Privilege的多对多  -->
<set name="privileges" table="itcast_role_privilege">
	<key column="roleId"></key>
	<many-to-many class="Privilege" column="privilegeId"></many-to-many>
</set>
    这个和在Privilege权限类中的roles属性是一对.
    按照注释,属性填到name中,类填到class中.table的名字,一对的要一样.key是当前的映射实体(权限)说明的id要叫的名字,所以在Role映射文件中的manytmany标签的column的属性中写上privilegeId,而manytomany的column是Role类对应的实体主键.其中table,key的column和manytomany的column一对的都是要互相对应着的.

<!-- roles属性,本类与Role的多对多  -->
<set name="roles" table="itcast_role_privilege">
	<key column="privilegeId"></key>
	<many-to-many class="Role" column="roleId"></many-to-many>
</set>

    还有就是权限类中的树形结构的自关联.manytoone和onetomany也是一对.manytoone和manytoone会在多的一端加上一的一端的主键,所以一端说明关联字段为parentId,多端把这个作为外键放在表中.

<!-- parent属性,本类与Privilege(上级)的多对一 -->
<many-to-one name="parent" class="Privilege" column="parentId" ></many-to-one>

<!-- children属性,本类与Privilege(下级)的一对多 -->
<set name="children" cascade="delete" order-by="id ASC"  >
	<key column="parentId"></key>
	<one-to-many class="Privilege"/>
</set>

    其中总结下,对于多对一和一对多,是根据本类与该属性的关系来判断的,本类与该属性为一对多的关系,就用onetomany.并且多端和一端都是需要写name,class和column的属性的,只是多端需要设置外键,所以column写在了key标签中.而多对多是需要再加一张第三表的,两个表的id合起来才是一个主键,所以除了在key中写外键列column,还需要在manytomany中写主键列column.
    最后,写完之后,不要忘了添加到hibernate.cfg.xml中.
权限数据的初始化
    对于权限来说,有一些数据是初始化在数据库中的,你可以采用写slq脚本的方式,insert数据到数据库中,但是由于数据库的差异性,sql脚本可能不通用,需要为不同的数据库写不同的脚本,所以为了解决这个问题.就写代码,用程序来生成初始化数据,这样只要hibernate支持的数据库,我们都能插入数据.
    做法就是写一个叫Installer的工具类.
@Component //放在容器中
public class Installer {

	@Resource //注入SessionFactory
	private SessionFactory sessionFactory;
	
	@Transactional //需要事务
	public void install() {
		Session session = sessionFactory.getCurrentSession();

		//=================================================================
		//保存超级管理员用户
		User user = new User();
		user.setLoginName("admin");
		user.setName("超级管理员");
		user.setPassword(DigestUtils.md5Hex("admin"));
		session.save(user); //保存

		//======================================================================
		//保存权限数据
		Privilege menu,menu1,menu2,menu3,menu4,menu5;
		//顶级菜单,作用:对二级菜单分类,没有url
		menu = new Privilege("系统管理",null,null);
		menu1 = new Privilege("岗位管理","/role_list",menu);
		menu2 = new Privilege("部门管理","/department_list",menu);
		menu3 = new Privilege("用户管理","/user_list",menu);
		//menu4 = new Privilege("岗位管理","/ItcastOA/role_list.action",parent);
		
		session.save(menu);
		session.save(menu1);
		session.save(menu2);
		session.save(menu3);
		
		session.save(new Privilege("岗位列表","/role_list",menu1));
		session.save(new Privilege("岗位删除","/role_delete",menu1));
		session.save(new Privilege("岗位添加","/role_add",menu1));
		session.save(new Privilege("岗位修改","/role_edit",menu1));
		
		session.save(new Privilege("部门列表","/department_list",menu2));
		session.save(new Privilege("部门删除","/department_delete",menu2));
		session.save(new Privilege("部门添加","/department_add",menu2));
		session.save(new Privilege("部门修改","/department_edit",menu2));
		
		session.save(new Privilege("用户管理","/user_list",menu3));
		session.save(new Privilege("用户删除","/user_delete",menu3));
		session.save(new Privilege("用户添加","/user_add",menu3));
		session.save(new Privilege("用户修改","/user_edit",menu3));
		session.save(new Privilege("初始化密码","/user_initPassword",menu3));
		
		//--------------------------------
		menu = new Privilege("网上交流",null,null);
		menu1 = new Privilege("论坛管理","/forumManage_list",menu);
		menu2 = new Privilege("论坛","/forum_list",menu);
		
		session.save(menu);
		session.save(menu1);
		session.save(menu2);
		
		//------------------------------------------
		menu = new Privilege("审批流转",null,null);
		menu1 = new Privilege("审批流程管理","/processDefinition_list",menu);
		menu2 = new Privilege("申请模板管理","/template_list",menu);
		menu3 = new Privilege("起草管理","/flow_templateList",menu);
		menu4 = new Privilege("待我审批","/flow_myTaskList",menu);
		menu5 = new Privilege("我的申请查询","/flow_myApplicationList",menu);
		
		session.save(menu);
		session.save(menu1);
		session.save(menu2);
		session.save(menu3);
		session.save(menu4);
		session.save(menu5);
		
	}
	
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		Installer installer = (Installer)ac.getBean("installer");
		installer.install();
	}
}
    其中,install()方法用来初始化数据,由于需要将数据写入数据库中,所以需要session和事务.将该类写上注解@Component,表示放入容器中,让容器来管理事务.而session,则通过注入sessionFactory,然后获取当前的session来获得.需要初始化两部分数据,一是一个超级管理员;2.是一对的权限数据,写到是不难,只是数据有些多.最后,就是要执行该方法,由于执行该方法写在main中,所以可以不用启动tomcat,直接执行java Application就可以了.但是不启动tomcat,容器对象在监听器中创建,现在就不能创建了,怎么获取到容器中的Installer对象?所以直接读取配置文件applicationContext.xml,让它实例化一个Installer对象,我们再来用.
    以上就是我第五天的学习,主要学习到的就是MD5的加密,以及action项目的流程,还有实体映射文件的写法的再次巩固,还有用main不启动tomcat,如何获取容器中的对象.应该再有4天就差不多学完了,加油哦!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值