我遇到Hibernate生成无效SQL的问题。具体来说,混合和匹配隐式和显式连接。这似乎是open bug。
但是,我不确定为什么这是无效的SQL。我想出了一个小玩具示例,它会生成相同的语法异常。
CREATE TABLE Employee (
employeeID INT,
name VARCHAR(255),
managerEmployeeID INT
)
INSERT INTO Employee (employeeID, name) VALUES (1, 'Gary')
INSERT INTO Employee (employeeID, name, managerEmployeeID) VALUES (2, 'Bob', 1)
这两个查询都有效。我意识到有笛卡尔积;那是故意的。
明确加入:
SELECT e1.name,
e2.name,
e1Manager.name
FROM Employee e1
CROSS JOIN Employee e2
INNER JOIN Employee e1Manager
ON e1.managerEmployeeID = e1Manager.employeeID
隐含联接:
SELECT e1.name,
e2.name,
e1Manager.name
FROM Employee e1,
Employee e2,
Employee e1Manager
WHERE e1.managerEmployeeID = e1Manager.employeeID
此查询不适用于MSSQL 2000/2008或MySQL:
SELECT e1.name,
e2.name,
e1Manager.name
FROM Employee e1,
Employee e2
INNER JOIN Employee e1Manager
ON e1.managerEmployeeID = e1Manager.employeeID
在MS2000中,我收到错误:
列前缀'e1'不匹配 使用表名或别名 在查询中。
在MySQL中,错误是:
未知列'e1.managerEmployeeID' 在'on clause'中。
答案 0 :(得分:13)
导致错误,因为根据SQL标准,JOIN
关键字的优先级高于逗号。棘手的是,在之后在FROM
子句中评估了相应的表之后,表别名才可用。
因此,当您在e1
表达式中引用JOIN...ON
时,e1
尚不存在。
我在研究Hibernate时请等待,并了解是否可以说服它在所有情况下都使用JOIN
。
嗯。 Hibernate.org上的所有内容似乎都会重定向到jboss.org。因此,现在无法在线阅读HQL文档。我相信他们最终会找出他们的名字。
答案 1 :(得分:0)
PostgreSQL也会出错:
ERROR: invalid reference to FROM-clause entry for table "e1"
LINE 7: ON e1.managerEmployeeID = e1Manager.employeeID;
^
HINT: There is an entry for table "e1", but it cannot be referenced from this part of the query.
我认为问题是当你连接两个表a和b(在这种情况下为e2和e1Manager)时,你只能在“ON”子句中引用这两个表。因此,您可以在此ON子句中引用e2和e1Manager,但不能引用e1。
我认为这扩展了如果你有一个“JOIN”语句链,你可以在“ON”子句中引用同一链中的其他表,但你不能越过“,”。所以像'a JOIN b on a.a_id = b.a_id JOIN c ON c.b_id = b.b_id AND c.a_id = a.a_id“是允许的。
用于生成此SQL的HQL是什么?像“从员工e1,员工e2中选择e1.name,e2.name,e1.manager.name”这样的东西?
答案 2 :(得分:0)
这可能有点偏离主题,因为它根本不涉及休眠,但来自Bill Karwin的评论确实让我眼前一亮。您需要先进行显式连接,而不是先写隐式连接。如果您有多个隐式连接,则此语法特别有用。
在MS SQL中检查以下示例。并非所有联系人都定义了国家/地区代码,但所有联系人都有一个属性val,将在表Tbl中查找。所以直观的解决方案不起作用:
SELECT * FROM
contacts, Tbl
LEFT OUTER JOIN country ON CtryCod = country.CtryCod
WHERE val = Tbl.val
相反,您可能更愿意使用以下语法:
SELECT * FROM
contacts LEFT OUTER JOIN country ON CtryCod = country.CtryCod,
Tbl
WHERE val = Tbl.val