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

来源:互联网 发布:微波炉选购 知乎 编辑:程序博客网 时间:2024/05/22 00:18
    第五天的学习,是从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 { @Resourceprivate 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注入.

@Resourceprotected 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));//设置默认密码为1234String 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 //注入SessionFactoryprivate 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;//顶级菜单,作用:对二级菜单分类,没有urlmenu = 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 0
原创粉丝点击