详细解读权限控制中的ER关系(新手入门必备知识)
2017-01-12 20:14
405 查看
本文将结合权限控制进行对ER关系的详细讲解,希望能给JavaWeb开发的新手朋友们带来一点帮助,本文中叙述可能存在不足之处,还请大家指出!(写博不易,尊重版权)
关于权限控制,无外乎用户User、角色Role、菜单Menu三张表,所以先建表如下:
以上为常用两种建表模式,是针对于用户和角色的ER关系不同进行分类的,下面进行详细解读:
首先角色和权限是多对多关系,这毫无疑问,一个角色可以使用多个菜单,一个菜单可以被多个角色使用
图一:用户和角色是多对多关系,这是最符合实际情况也是最常用的,因为一个用户可以担任多个角色(用户A可以同时担任项目经理和架构师两种角色),一个角色也可以对应多个用户(项目经理有A、B、C三人)
图二:用户和角色是多对一关系,这是结合实际问题中对权限控制进行的简化处理,即认为一个用户只能担任一个角色或者只考虑用户的主要角色,次要角色不予考虑,如A是管理员(一对一),B、C都是普通用户(多对一)
开始编写javabean和解析SSM和SSH中的ER关系映射文件(以简化形式图二为例):
User表实体类:(仅仅写主要部分)
public class Admin {
private Integer id;
private String account;
private String password;
private String name;
private Integer state;
// 多对一:外键r_id
private Role role;
}
Role表实体类:
public class Role {
private Integer id;
private String name;
private Integer state;
// 一对多
private Set<Admin> admins;
// 一对多 中间表mid
private Set<Menu> menus;
}
Menu表实体类:
public class Menu {
private Integer id;
private Integer state;
private String name;
private String url;
// 多对一:外键p_id
private Menu parent;
// 一对多:(非表中数据,只是方便后面将根据p_id查询出来的子菜单结果进行封装到menu实体)
private List<Menu> childs;
// 一对多:中间表rid
private Set<Role> roles;
}
分析以上实体类设计的理由和ER关系如下:
ER关系分析的前提是要搞清楚是针对哪个实体对象(Java是面向对象设计的)而言的,如果没有这个前提还大夸奇谈一对一、一对多、多对多关系简直是扯淡,也是很多初学者搞不清的根本原因。
一对一关系: 实体和普通属性是一对一关系,针对User实体而言, User实体和 name属性的关系;
多对一关系: 实体和实体属性的关系(涉及外键),针对User实体而言,User实体和role属性(r_id外键)的关系;针对Menu实体而言,Menu实体和父菜单parent的关系;
一对多关系: 本质与多对一同理,只是针对对象不一样(涉及集合) 针对Role实体而言,Role实体和属性set<Admin> admins的关系;
多对多关系: 实体和实体的关系(表与表的关系,涉及中间表) 本质是针对不同的两个实体一对多关系的结合,实际分析处理拆分为针对不同实体的一对多关系;
说完大家应该已经理清了ER关系的本质,下面我们看看Hibernate和Mybatis框架中ER关系映射配置文件的设计不同:
Hibernate中:
以Menu实体为例:
mybatis中:
(mybatis中除了一对一关系,其他所有关系转化为 多对一association或一对多collection关系,二者可嵌套使用,即多对多转化为两个针对不同实体对象的一对多)
下面简单说说菜单表/权限表的设计问题的个人心得:(直接看图,不详细叙述)
实际中经常碰到编写菜单的增删改操作,实际很多时候这是没有任何意义,因为实际操作中不是说你增加一个菜单就真的有这个菜单(项目的实际菜单是固定的,不是你想增加菜单就写个增删改就行,还得有实际页面和功能,这明显不可能,所以一般对于添加的“伪菜单”进行抛异常处理),但是删除操作是会删除"真菜单"的,防止误操作导致系统崩溃,我们采用"保留部分数据区的方式"对“真菜单”权限进行权限设置,允许查,不允许增删改!对“伪”菜单(途中id大于100的用户自定义菜单)进行完全允许增删改查!(CRUD加id>100的条件)
再简要叙述下关于数据库外键的问题:
发现很多新人,会把外键(实体属性)当作普通属性处理以避免上述繁杂的ER关系配置(即全为一对一关系),这种设计方案极大简化sql查询和ER关系配置,相当所有sql查询拆分为分步查询,但这会给controller层和页面处理带来极大负面影响,多表查询时controller要注入多个daoimpl对象并将其传到页面按需求处理。不说该方法完全不可取或毫无优点(最起码再难的sql语句分步写几乎无须动脑),但建议合理按需使用,严重不建议在多表多关联查询使用,建议尽量不用!
关于权限控制,无外乎用户User、角色Role、菜单Menu三张表,所以先建表如下:
以上为常用两种建表模式,是针对于用户和角色的ER关系不同进行分类的,下面进行详细解读:
首先角色和权限是多对多关系,这毫无疑问,一个角色可以使用多个菜单,一个菜单可以被多个角色使用
图一:用户和角色是多对多关系,这是最符合实际情况也是最常用的,因为一个用户可以担任多个角色(用户A可以同时担任项目经理和架构师两种角色),一个角色也可以对应多个用户(项目经理有A、B、C三人)
图二:用户和角色是多对一关系,这是结合实际问题中对权限控制进行的简化处理,即认为一个用户只能担任一个角色或者只考虑用户的主要角色,次要角色不予考虑,如A是管理员(一对一),B、C都是普通用户(多对一)
开始编写javabean和解析SSM和SSH中的ER关系映射文件(以简化形式图二为例):
User表实体类:(仅仅写主要部分)
public class Admin {
private Integer id;
private String account;
private String password;
private String name;
private Integer state;
// 多对一:外键r_id
private Role role;
}
Role表实体类:
public class Role {
private Integer id;
private String name;
private Integer state;
// 一对多
private Set<Admin> admins;
// 一对多 中间表mid
private Set<Menu> menus;
}
Menu表实体类:
public class Menu {
private Integer id;
private Integer state;
private String name;
private String url;
// 多对一:外键p_id
private Menu parent;
// 一对多:(非表中数据,只是方便后面将根据p_id查询出来的子菜单结果进行封装到menu实体)
private List<Menu> childs;
// 一对多:中间表rid
private Set<Role> roles;
}
分析以上实体类设计的理由和ER关系如下:
ER关系分析的前提是要搞清楚是针对哪个实体对象(Java是面向对象设计的)而言的,如果没有这个前提还大夸奇谈一对一、一对多、多对多关系简直是扯淡,也是很多初学者搞不清的根本原因。
一对一关系: 实体和普通属性是一对一关系,针对User实体而言, User实体和 name属性的关系;
多对一关系: 实体和实体属性的关系(涉及外键),针对User实体而言,User实体和role属性(r_id外键)的关系;针对Menu实体而言,Menu实体和父菜单parent的关系;
一对多关系: 本质与多对一同理,只是针对对象不一样(涉及集合) 针对Role实体而言,Role实体和属性set<Admin> admins的关系;
多对多关系: 实体和实体的关系(表与表的关系,涉及中间表) 本质是针对不同的两个实体一对多关系的结合,实际分析处理拆分为针对不同实体的一对多关系;
说完大家应该已经理清了ER关系的本质,下面我们看看Hibernate和Mybatis框架中ER关系映射配置文件的设计不同:
Hibernate中:
以Menu实体为例:
<hibernate-mapping package="com.xxx.bean"> <!-- lazy表示懒加载技术 ,inverse指定维护关系,order-by根据某属性排序--> <class name="Menu" table="MENU" lazy="false" schema="SCOTT"> <!-- 主键 --> <id name="id" column="id" type="long"> <generator class="sequence"> <param name="sequence">seq_menu</param> </generator> </id> <!-- 中间表many-to-many ,针对于Menu实体和属性roles,一对多关系--> <set name="roles" lazy="false" inverse="true" table="ROLE_MENU" > <key column="mid" /> <many-to-many class="Role" column="rid" /> </set> <!-- 多对一关系用many-to-one 属性parent --> <many-to-one name="parent" column="p_id" class="Menu" lazy="false" /> <!-- 一对多关系用one-too-many 属性childs --> <list name="childs" lazy="false" inverse="true" order-by="id"> <key column="p_id" /> <one-to-many class="Menu" /> </list> <!-- 其他属性 --> <property name="name" column="name" /> <property name="url" column="url" /> <property name="status" column="status" /> </class> </hibernate-mapping>
mybatis中:
(mybatis中除了一对一关系,其他所有关系转化为 多对一association或一对多collection关系,二者可嵌套使用,即多对多转化为两个针对不同实体对象的一对多)
<mapper namespace="com.salmon.mapper.RoleMapper"> <resultMap type="com.salmon.model.Role" id="role_ID"> <id property="id" column="r_id" /> <result property="name" column="rname" /> <result property="state" column="rstate" /> <!-- 一对多: 中间表,针对Role实体和admins属性 --> <collection property="admins" ofType="com.salmon.model.Admin"> <id property="id" column="a_id" /> <result property="account" column="account" javaType="java.lang.String" jdbcType="VARCHAR" /> <result property="password" column="password" /> <result property="realname" column="realname" /> <result property="state" column="astate" /> </collection> <!-- #### 嵌套使用 #### --> <!-- 一对多: 中间表, 针对Role实体和menus属性 --> <collection property="menus" ofType="com.salmon.model.Menu"> <id property="id" column="m_id" /> <result property="state" column="mstate" /> <result property="name" column="mname" /> <result property="url" column="url" /> <!-- 多对一: 外键,针对Menu实体和parent属性 --> <association property="parent" javaType="com.salmon.model.Menu" column="pid"> <id property="id" column="pm_id" /> <result property="state" column="pm_state" /> <result property="name" column="pm_name" /> </association> </collection> </resultMap> <!-- 注意resultMap中的属性用到了才配,没用到无须配置(多配不用不影响),且一定要确保包含所有对应的sql查询的结果映射关系; 此处仅作参考模板,实际按需求配置resultMap和撰写sql语句即可,不用完全配置 --> <!-- 模糊分页查询 --> <sql id="fuzzy"> <!-- 条件省略--> </sql> <select id="fuzzyFindByPage" resultMap="role_ID" parameterType="java.util.Map"> <if test="page!=null"> select * from ( select rownum as rn ,temp.* from ( </if> select r.id as r_id , r.name as rname,r.state as rstate ,a.id as a_id,a.account ,a.password,a.realname,a.state as astate,
a.rid,m.id as m_id,m.pid,m.state as mstate ,m.name as mname,m.url,pid.id as pm_id,pid.state as pm_state,pid.name as pm_name
from role r ,admin a,menu m ,role_menu rm ,menu pid where pid.id=m.pid and r.id=rm.rid and m.id=rm.mid and a.rid=r.id <include refid="fuzzy" /> <if test="page!=null"> ) temp where rownum <![CDATA[<=]]> #{page.pageNum}*#{page.pageSize} ) tp where tp.rn <![CDATA[>=]]> (#{page.pageNum}-1)*#{page.pageSize}+1 </if> </select> </mapper>
下面简单说说菜单表/权限表的设计问题的个人心得:(直接看图,不详细叙述)
实际中经常碰到编写菜单的增删改操作,实际很多时候这是没有任何意义,因为实际操作中不是说你增加一个菜单就真的有这个菜单(项目的实际菜单是固定的,不是你想增加菜单就写个增删改就行,还得有实际页面和功能,这明显不可能,所以一般对于添加的“伪菜单”进行抛异常处理),但是删除操作是会删除"真菜单"的,防止误操作导致系统崩溃,我们采用"保留部分数据区的方式"对“真菜单”权限进行权限设置,允许查,不允许增删改!对“伪”菜单(途中id大于100的用户自定义菜单)进行完全允许增删改查!(CRUD加id>100的条件)
再简要叙述下关于数据库外键的问题:
发现很多新人,会把外键(实体属性)当作普通属性处理以避免上述繁杂的ER关系配置(即全为一对一关系),这种设计方案极大简化sql查询和ER关系配置,相当所有sql查询拆分为分步查询,但这会给controller层和页面处理带来极大负面影响,多表查询时controller要注入多个daoimpl对象并将其传到页面按需求处理。不说该方法完全不可取或毫无优点(最起码再难的sql语句分步写几乎无须动脑),但建议合理按需使用,严重不建议在多表多关联查询使用,建议尽量不用!
相关文章推荐
- SVN权限控制手册新手必备
- PS新手入门必备知识
- Linux基础入门及系统管理01-Linux运维必备知识-用户及权限详解10
- XML 新手入门基础知识
- 新手搭建PHP环境必备知识:windows下PHP5+APACHE+MYSQL完整配置
- 【转载】java新手必读,一些入门知识
- VC入门必备--基础知识(一)
- 音量控制相关知识解读1
- 八大Java基础知识[Java入门程序员必备]
- 新手搭建PHP环境必备知识:windows下PHP5+APACHE+MYSQL完整配置
- 初学者必备:C++经典入门详细教程
- 新手入门:解读OSI七层网络协议之传输层 (上)
- java新手必读,一些入门知识
- 新手入门:解读OSI七层网络协议之传输层 (下)
- 内核模块编程之入门(二)—必备知识
- iPhone入门知识普及(新手必看)
- 新手入门:从最灵活的知识学起
- 新手必读 详细介绍J2ME的基础知识
- java新手必读,一些入门知识。
- VC入门必备--基础知识(三)