用于分配用户角色的首选数据库设计方法? (帽子与团体)
我有一个中等规模的 MySQL 数据库,其中有一个主要的“人员”表,其中包含与剧院和戏剧学校相关的每个人的基本联系信息,我负责维护和开发许多 Web 应用程序。
有些人只是联系人 - 也就是说,他们的“人”表记录是我们需要存储的有关他们的所有信息。 但许多其他人必须能够为各种系统承担不同的角色。 其中,大多数人都是从学生开始的。 有些人从雇员开始。 学生可以成为实习生或表演者; 员工可以成为学生; 所有教师都是雇员和表演者等。
本质上,他们是各种不同的“帽子”,任何个人都可能必须戴上这些帽子才能访问系统的不同部分并与之交互,并获取有关他们的信息可在我们网站的公共页面上找到。
我实现此模型的选择是使用几个其他表来表示这些“帽子”,这些表包含元信息以补充基本的“人员”信息,所有这些表都使用“人员”id 作为主键。 例如,一名教师在教师表中有一条记录,其中包含他或她的简短简历信息和工资率。 所有教师也是雇员(但并非所有雇员都是教师),这意味着他们在雇员表中有一条记录,允许他们将工作时间提交到我们的工资系统中。
我的问题是,实施该模型有哪些缺点? 我能想到的唯一的其他选择是用对大多数条目来说都是空且无用的字段来膨胀人员表,然后有一个人员可以所属的繁琐的“组”表,然后为每个人提供几乎每个表系统有一个 person person_id
外键,然后依赖业务逻辑来验证引用的 person_id 是否属于适当的组; 但这很愚蠢,不是吗?
下面是一些示例表声明,希望能够演示我当前如何将所有这些组合在一起,并希望说明为什么我认为这是对系统必须处理的各种情况的现实进行建模的更明智的方法。
欢迎任何建议和意见。 我很感激你的时间。
编辑 一些受访者提到使用 ACL 来确保安全 - 我在最初的问题中没有提到我实际上正在使用单独的 ACL 包来对不同系统的实际用户进行细粒度的访问控制。 我的问题更多是关于在数据库模式中存储有关人员的元数据的最佳实践。
CREATE TABLE persons (
`id` int(11) NOT NULL auto_increment,
`firstName` varchar(50) NOT NULL,
`middleName` varchar(50) NOT NULL default '',
`lastName` varchar(75) NOT NULL,
`email` varchar(100) NOT NULL default '',
`address` varchar(255) NOT NULL default '',
`address2` varchar(255) NOT NULL default '',
`city` varchar(75) NOT NULL default '',
`state` varchar(75) NOT NULL default '',
`zip` varchar(10) NOT NULL default '',
`country` varchar(75) NOT NULL default '',
`phone` varchar(30) NOT NULL default '',
`phone2` varchar(30) NOT NULL default '',
`notes` text NOT NULL default '',
`birthdate` date NOT NULL default '0000-00-00',
`created` datetime NOT NULL default '0000-00-00 00:00',
`updated` timestamp NOT NULL,
PRIMARY KEY (`id`),
KEY `lastName` (`lastName`),
KEY `email` (`email`)
) ENGINE=InnoDB;
CREATE TABLE teachers (
`person_id` int(11) NOT NULL,
`bio` text NOT NULL default '',
`image` varchar(150) NOT NULL default '',
`payRate` float(5,2) NOT NULL,
`active` boolean NOT NULL default 0,
PRIMARY KEY (`person_id`),
FOREIGN KEY(`person_id`) REFERENCES `persons` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE classes (
`id` int(11) NOT NULL auto_increment,
`teacher_id` int(11) default NULL,
`classstatus_id` int(11) NOT NULL default 0,
`description` text NOT NULL default '',
`capacity` tinyint NOT NULL,
PRIMARY KEY(`id`),
FOREIGN KEY(`teacher_id`) REFERENCES `teachers` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY(`classstatus_id`) REFERENCES `classstatuses` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
KEY (`teacher_id`,`level_id`),
KEY (`teacher_id`,`classstatus_id`)
) ENGINE=InnoDB;
CREATE TABLE students (
`person_id` int(11) NOT NULL,
`image` varchar(150) NOT NULL default '',
`note` varchar(255) NOT NULL default '',
PRIMARY KEY (`person_id`),
FOREIGN KEY(`person_id`) REFERENCES `persons` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE enrollment (
`id` int(11) NOT NULL auto_increment,
`class_id` int(11) NOT NULL,
`student_id` int(11) NOT NULL,
`enrollmenttype_id` int(11) NOT NULL,
`created` datetime NOT NULL default '0000-00-00 00:00',
`modified` timestamp NOT NULL,
PRIMARY KEY(`id`),
FOREIGN KEY(`class_id`) REFERENCES `classes` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY(`student_id`) REFERENCES `students` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY(`enrollmenttype_id`) REFERENCES `enrollmenttypes` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
I have medium sized MySQL database with a primary "persons" table which contains basic contact information about every human being connected to the theatre and theatre school for which I am responsible for maintaining and developing a number of web applications.
Some persons are just contacts - that is, their "persons" table record is all the information we need to store about them. Many others though have to be able to assume different roles for a variety of systems. Of these, most start out as students. Some start as employees. People who are students can become interns or performers; employees can become students; all teachers are employees and performers, etc.
In essence, their are a variety of different "hats" that any individual person may have to wear in order to access and interact with different parts of the system, as well as have information about them made available on public pages on our site.
My choice for implementing this model is to have several other tables which represent these "hats" - tables which contain meta-information to supplement the basic "person" info, all of which use the "persons" id as their primary key. For example, a person who is a teacher has a record in a teachers table containing his or her short biographical information and pay rate. All teachers are also employees (but not all employees are teachers), meaning they have a record in the employees table which allows them to submit their hours into our payroll system.
My question is, what are the drawbacks to implementing the model as such? The only other option I can think of is to inflate the persons table with fields that will be empty and useless for most entries and then have a cumbersome table of "groups" to which persons can belong, and then to have almost every table for every system have a person person_id
foreign key and then depend on business logic to verify that the person_id referenced belongs to the appropriate group; But that's stupid, isn't it?
A few example table declarations follow below, which hopefully should demonstrate how I'm currently putting all this together, and hopefully show why I think it is a more sensible way to model the reality of the various situations the systems have to deal with.
Any and all suggestions and comments are welcome. I appreciate your time.
EDIT A few respondents have mentioned using ACLs for security - I did not mention in my original question that I am in fact using a separate ACL package for fine-grained access control for actual users of the different systems. My question is more about the best practices for storing metadata about people in the database schema.
CREATE TABLE persons (
`id` int(11) NOT NULL auto_increment,
`firstName` varchar(50) NOT NULL,
`middleName` varchar(50) NOT NULL default '',
`lastName` varchar(75) NOT NULL,
`email` varchar(100) NOT NULL default '',
`address` varchar(255) NOT NULL default '',
`address2` varchar(255) NOT NULL default '',
`city` varchar(75) NOT NULL default '',
`state` varchar(75) NOT NULL default '',
`zip` varchar(10) NOT NULL default '',
`country` varchar(75) NOT NULL default '',
`phone` varchar(30) NOT NULL default '',
`phone2` varchar(30) NOT NULL default '',
`notes` text NOT NULL default '',
`birthdate` date NOT NULL default '0000-00-00',
`created` datetime NOT NULL default '0000-00-00 00:00',
`updated` timestamp NOT NULL,
PRIMARY KEY (`id`),
KEY `lastName` (`lastName`),
KEY `email` (`email`)
) ENGINE=InnoDB;
CREATE TABLE teachers (
`person_id` int(11) NOT NULL,
`bio` text NOT NULL default '',
`image` varchar(150) NOT NULL default '',
`payRate` float(5,2) NOT NULL,
`active` boolean NOT NULL default 0,
PRIMARY KEY (`person_id`),
FOREIGN KEY(`person_id`) REFERENCES `persons` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE classes (
`id` int(11) NOT NULL auto_increment,
`teacher_id` int(11) default NULL,
`classstatus_id` int(11) NOT NULL default 0,
`description` text NOT NULL default '',
`capacity` tinyint NOT NULL,
PRIMARY KEY(`id`),
FOREIGN KEY(`teacher_id`) REFERENCES `teachers` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY(`classstatus_id`) REFERENCES `classstatuses` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
KEY (`teacher_id`,`level_id`),
KEY (`teacher_id`,`classstatus_id`)
) ENGINE=InnoDB;
CREATE TABLE students (
`person_id` int(11) NOT NULL,
`image` varchar(150) NOT NULL default '',
`note` varchar(255) NOT NULL default '',
PRIMARY KEY (`person_id`),
FOREIGN KEY(`person_id`) REFERENCES `persons` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE enrollment (
`id` int(11) NOT NULL auto_increment,
`class_id` int(11) NOT NULL,
`student_id` int(11) NOT NULL,
`enrollmenttype_id` int(11) NOT NULL,
`created` datetime NOT NULL default '0000-00-00 00:00',
`modified` timestamp NOT NULL,
PRIMARY KEY(`id`),
FOREIGN KEY(`class_id`) REFERENCES `classes` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY(`student_id`) REFERENCES `students` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
FOREIGN KEY(`enrollmenttype_id`) REFERENCES `enrollmenttypes` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
去年我也经历过类似的事情。 问题是:我们是明确地还是通用地对我们的实体进行建模? 在您的示例中,这意味着实体/表(例如教师、学生等)之间是否有直接关系。
最后我们选择了通用的“派对”模型。 Party模型如下:
这是一种极其强大的模型,在 CRM 类型的系统中非常常见。 该模型几乎来自“数据模型资源书籍:第 1 卷”,这是此类事情的绝佳资源。
I went through a similar thing last year. There the question was: do we model our entities explicitly or generically? In your example, that would mean having entities/tables like teacher, student, etc with direct relationships between them or not.
In the end we went for a generic "Party" model. The Party model is as follows:
It's an extremely powerful model, one that is pretty common in CRM type systems. This model pretty much came from "The Data Model Resource Book: Volume 1", which is an excellent resource for such things.
您描述的组和帽子模型是可以相互转换的。 无需真正担心数据丢失。 具体来说,“主组”表可以通过“帽子人”表与各种“帽子详细信息”表的外连接来产生。
如果您使用“帽子”模型,则需要确保给定的“帽子表”准确地封装了该帽子的独特特征。 与团体模型相比,这里的宽容度要低得多。
如果您这样做,您可能会想要为常见任务设置一些视图 - 例如,如果有人在“教师姓名”字段中输入内容,而您想要弹出一些自动完成功能,那么有一个视图基本上
会帮助很大。
顺便说一句,我发现有用的一件事是使用与原始表中主键相同的名称来调用外键。 这样你就可以只
在你的 FROM 中而不用胡闹于 WHERE 或 ON 等价物。
The groups and hats models you describe are convertible, one to the other. There's no real worry about data loss. Specifically, the "master groups" table can be produced by outer joins of the "hat person" table with the various "hat detail" tables.
If you're using a "hat" model you need to make sure that a given "hat table" accurately encapsulates the unique characteristics of that hat. There's a lot less forgiveness there than with the groups model.
You'll probably want to set up a few views for common tasks if you go this way - for example, if somebody's typing into a field for "teacher name" and you want to pop up some autocompletes, having a view which is basically
will help enormously.
On a tangential note, one thing I've found to be useful is to call foreign keys by the same name as the primary key in their original table. That way you can just
in your FROM instead of monkeying with WHERE or ON equivalencies.
教师是唯一有工资的“人”吗? 这样做可能会限制您的设计。 您可能想要做的是拥有一个属性表来存储“人”的附加属性。 这将允许将来进行修改。
Are teachers the only 'person' that has a pay rate? You may be limiting your design by doing it this way. What you may want to do is have an attributes table that stores additional attributes for a 'person'. This will allow for future modifications.
我喜欢帽子方法。 过去,我实现了帽子和组的组合。 基本上,有一个用户可以执行的所有可能操作(权限)的列表。 然后我有一个组表。 每个组可以有 1 个或多个操作(权限)。 然后我将用户分配到组。
这为我提供了很大的灵活性。 我可以获得非常细粒度的许可。 我还可以通过编辑组来快速更改许多人的权限。 事实上,我有权限页面设置来使用相同的权限。 这允许最终用户(不是我)为其他用户设置权限。
I like the Hat approach. In the past, I implemented a combination of hats and groups. Basically, there is a list of all possible actions (permissions) a user can do. I then have a table of groups. Each group can have 1 or many actions (permissions). I then assign users to groups.
This provides me with a lot of flexibility. I can get very fine grain in my permissioning. I also can change many peoples permissions quickly by just editing the group. In fact, I have the permissioning page setup to use the same permissions. This allows the end user (not me) to setup permissions for other users.
是的,教师是唯一享有此类工资的人。 该字段应该更准确地称为“classPayRate” - 这是教师员工的特殊情况。 非教师员工将其总工作时间作为单独的行项目提交到我们的工资系统中。
yes, teachers are the only people who have a pay rate as such. That field should more accurately be called "classPayRate" - it's a special case for teacher employees. Non-teacher employees submit their total hours as a separate line item in our payroll system.
我可能会将教师更改为员工并添加员工类型。
然而,我绝不会以任何形式或形式将电子邮件、地址、电话存储在人员表中。 这些都应该是自己的单独表格,因为人们有多个电子邮件地址(工作和家庭)、多个电话号码(工作、家庭、手机、传真)和多个地址(工作、家庭 1、家庭 2、学校等)。 我会将每个数据放在自己的表中并为其分配一个类型,以便您可以识别地址、电话等的类型。
此外,对于地址、电子邮件、电话,您可能需要一个标志来识别哪个是主要记录用于首先联系。 我们调用我们的通信,它是一个布尔值,通过触发器保持最新,因为每个拥有记录的人都必须有一个且只有一个通信,所以如果它发生变化,旧的必须自动重置以及新的进去,如果是第一条记录,则自动设置,如果删除了对应的记录,如果还有剩余记录,则将其分配给另一条记录。
I might change teachers to employees and add employee type.
However in no way shape or form would I ever store email, address, phone in the person table. These should all be separate tables of their own as people have multiple email addresses (work and home), multiple phone numbers (work, home, cell, fax) and multiple addresses (work, home1, home 2, school, etc.). I would put each in its own table and assign a type to it so you can identify which is what type of address, phone, etc.
Also for address, email, phone you might want a flag to identify which is the the main record to use for contacting first. We cal ours correspondence and it is a boolean that is kept up-to-date with a trigger as each person who has a record must have one and only one correspondence, so if it changes, the old one must automatically be reset as well as the new one go in and if it is the first record it is set automaticially and if the correspondence reciord is deleted , it will be assigned to the another one if there are remaining records.
为了安全起见,我更喜欢使用访问控制列表(ACL)。 通过 ACL,您可以拥有主体(用户或用户组)、资源(例如文件、记录或记录组)和操作(例如读取、更新、删除)。
默认情况下,没有人拥有任何特权。 要授予权限,您可以添加一个条目,例如 Bob 对文件 Abc 具有读取访问权限。
您应该能够找到帮助您实现类似功能的代码。 在 Java 中,JAAS 支持这种方法。
For security I prefer to use Access Controls Lists (ACLs). With ACL's you have Principals (users or groups of users), Resources (such as a file, record or group of records) and Actions (such as read, update, delete).
By default nobody has any privilege. To grant a permission you add an entry like Bob has Read Access to File Abc.
You should be able to find code that helps you implement something like this. In Java the JAAS supports this method.
我正在使用 ACL 包来实现网站上的细粒度权限 - 我的问题的核心更多的是如何为具有不同角色的个人存储元数据,并在系统中构建一些保护措施以确保数据完整性(例如人必须有教师记录才能被设置为班级的教师 - 外键约束引用教师表,而不是人员表)。
I'm using an ACL package for fine-grained permissions on the site - the heart of my question is more about how to store the metadata for individuals who have different roles and build a few safeguards into the system for data integrity (such that a person must have a teacher record in order to be set as the teacher of a class - the foreign key constraint references the teacher table, not the person table).
我之前用过Party模型。 我确实解决了大部分缺点。
I used Party model before. I really solves most of the shortcomings.