2014-09-23 55 views
0

Story:hibernate(或SQL或HQL)查找擁有另一個集合的子集的對象

我擁有此用戶 - >角色 - >特權機制。每個用戶都有一些角色。每個角色都有一些特權。

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來實現,請不要猶豫,回覆。乾杯!!!

+0

在這個例子中用戶id = 1只能進入房間11,44吧?如果是的話,將嘗試SQL – radar 2014-09-23 17:37:46

+0

用戶1應該能夠輸入11(需要1000和3000),33(需要3000),44(不需要) – user2375809 2014-09-23 18:36:46

回答

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: [email protected] 
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(沒有明顯的/ group by子句)。

IN子查詢通常由數據庫翻譯爲半連接,NOT IN用於半反連接,意思是:不分配來自連接表的值,並且對於多個連接匹配,結果不會相乘。

+0

我今天早上仔細查看了你的查詢。它從完全不同的角度解決問題!好東西!它處理「如果一個房間不需要特權,那麼任何人都可以輸入」。我喜歡這個!非常感謝。 – user2375809 2014-09-24 09:58:49

1

第一個查詢獲取用戶有權訪問的所有房間 第二個查詢獲取所有不需要任何特權的房間。 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) 
+0

聯合可能無法正常工作,因爲兩列的查詢都不同。請更正 – Zeus 2014-09-23 17:47:02

+0

@Zeus,謝謝你讓我知道,更正了它 – radar 2014-09-23 17:48:15

+0

謝謝你的回答。但它似乎並沒有產生預期的結果。根據文章中的示例進行測試,它給出了11,33和55. 55應該不存在,因爲55需要2000用戶1沒有的。正確的結果應該是11,33,44 – user2375809 2014-09-23 18:59:10

相關問題