hibernate(或SQL或HQL)查找具有集合的对象,该集合是另一个集合的子集

时间:2014-09-23 17:31:54

标签: sql hibernate orm hql criteria

故事:

我有这个用户 - >角色 - >特权机制。每个用户都有一些角色。每个角色都有一些特权。

CREATE TABLE user (id int);
INSERT INTO user VALUES (1);
INSERT INTO user VALUES (2);
INSERT INTO user VALUES (3);

CREATE TABLE role (id int);
INSERT INTO role VALUES (100);
INSERT INTO role VALUES (200);

CREATE TABLE user__role (user_id int, role_id int);
INSERT INTO user__role VALUES (1, 100);
INSERT INTO user__role VALUES (2, 200);

CREATE TABLE privilege (id int);
INSERT INTO privilege VALUES (1000);
INSERT INTO privilege VALUES (2000);
INSERT INTO privilege VALUES (3000);
INSERT INTO privilege VALUES (4000);

CREATE TABLE role__privilege (role_id int, privilege_id int);
INSERT INTO role__privilege VALUES (100, 1000);
INSERT INTO role__privilege VALUES (100, 3000);
INSERT INTO role__privilege VALUES (200, 2000);

(用户,角色和权限都有名称和其他东西。但我省略了它们以保持示例简单)

然后我有一些房间。你需要一些特权才能进入房间。

CREATE TABLE room (id int);
INSERT INTO room VALUES (11);
INSERT INTO room VALUES (22);
INSERT INTO room VALUES (33);
INSERT INTO room VALUES (44);
INSERT INTO room VALUES (55);

CREATE TABLE room__privilege (room_id int, privilege_id int);
INSERT INTO room__privilege VALUES (11, 1000);
INSERT INTO room__privilege VALUES (11, 3000);
INSERT INTO room__privilege VALUES (22, 2000);
INSERT INTO room__privilege VALUES (33, 3000);
INSERT INTO room__privilege VALUES (55, 1000);
INSERT INTO room__privilege VALUES (55, 2000);
INSERT INTO room__privilege VALUES (55, 3000);

这是交易:如果用户拥有房间所需的所有权限,则用户可以进入房间。如果房间不需要特权,那么任何人都可以进入。

就对象而言,我有类似

的东西
class User {
  int id;
  Set<Role> roles;
}

class Role {
  int id;
  Set<Privilege> privileges;
}

class Room {
  int id;
  Set<Privilege> requirements;
}

现在我有一个用户说id = 1.我想知道这个用户可以输入哪些房间。如何使用hibernate条件或SQL实现此目的?

我想我可以使用一些查询来查找用户拥有的所有权限(并将它们存储在一个集合中)。然后我发现房间的要求是这个集合的一个子集。但我找不到合适的标准/限制。此外,在阅读了stackoverflow中的一些帖子后,我感觉整个事情可以通过单个SQL / HQL查询来完成。

请有人给我一些帮助。提前谢谢!

更新1

整个晚上我一直在努力。我设法得到了一些结果

SELECT requirements.room_id
FROM (
    SELECT room.id AS room_id, room__privilege.privilege_id FROM room
    JOIN room__privilege ON room__privilege.room_id = room.id
    ) requirements
INNER JOIN (
    SELECT room__privilege.room_id, COUNT(*) as count FROM room__privilege
    GROUP BY room__privilege.room_id
    ) hits ON requirements.room_id = hits.room_id
INNER JOIN (
    SELECT user.id AS user_id, rp.privilege_id FROM user
    JOIN user__role ur ON user.id = ur.user_id
    JOIN role__privilege rp ON ur.role_id = rp.role_id
    ) up ON requirements.privilege_id = up.privilege_id
WHERE up.user_id = 1
GROUP BY requirements.room_id, up.user_id, hits.count HAVING COUNT(*) = hits.count
UNION 
SELECT room.id FROM room
WHERE room.id NOT IN (
    SELECT room_id FROM room__privilege
    );

这似乎给了我想要的东西。我似乎相当复杂,我不确定我是否可以将其包含在标准或HQL中。

我检查了@Rajesh和@Erik Hart的答案。他们的查询似乎也适用于这个例子。我要做一个分析,看看哪个表现更好。

非常感谢你的帮助。对此,我真的非常感激。如果有人知道如何通过标准或HQL实现这一点,请不要犹豫回复。干杯!!!

2 个答案:

答案 0 :(得分:1)

第一个查询是获取用户有权访问的所有房间 第二个查询提取所有不需要任何priveleges的房间。 UNION会给出理想的结果

select A.room_id
FROM( 
    SELECT room_id, 
           count(privilege_id) as count1
    FROM room__privilege 
    GROUP BY room_id) A
    INNER JOIN
    (
    SELECT room_id, 
           count(RP.privilege_id) as count2
    FROM room__privilege RP
    INNER join 
     (select RLP.privilege_id as privilege_id
      FROM role__privilege RLP
      inner join user__role UR
      on UR.role_id = RLP.role_id
      and UR.user_id = 1 ) T
    on T.privilege_id = RP.privilege_id
    group by room_id) B
    ON A.count1 = B.count2
    AND A.room_id = B.room_id
union
select R.id from Room R
where R.id not in ( select room_id from room__privilege )

答案 1 :(得分:1)

SQL将是:

select id from room where id not in ( 
select room_id from room_privilege where privilege_id not in (
select id from privilege where id in ( -- can omit
select privilege_id from role_privilege where role_id in (
select id from rol where id in ( -- can omit
select role_id from user_role where user_id in ( -- if user table omitted: user_id=@userid
select id from usr where id=1  -- can omit
)))))) -- remove closing brackets when omitting subselects!

在SQL Server中检查过这个(用户#1:11,33,44;#2:22,44;仅限#3:44),由于保留关键字,表名略有变化。

第2行选择用户没有的房间权限,这会阻止他进入。然后第1行选择房间而不阻止room_privileges。

主对象表上的选择通常可以省略(第一个除外),但也可以保留没有孤立交叉引用的安全性(如果没有被外键阻止,则删除级联)。

这应该返回不同的房间ID(没有distinct / group by子句)。

IN子选择通常由数据库转换为半连接,NOT IN转换为半反连接,这意味着:未分配连接表中的值,并且多个连接匹配的结果不会相乘。