休眠:具有递归深度搜索的存储过程:映射/输出问题
Posted
技术标签:
【中文标题】休眠:具有递归深度搜索的存储过程:映射/输出问题【英文标题】:Hibernate: StoredProcedure with recursive depthsearch: Mapping/Output Problems 【发布时间】:2016-09-08 13:33:49 【问题描述】:我正在寻求帮助。我必须用 Hibernate 5.2 映射我的 Postgres 9.4 数据库 (DB),当然这是一项学习任务。最大的问题是,我不是 Hibernate、Java 和编码本身的大脑 XD
这是一个 SozialNetwork 数据库。用 Hibernate 映射数据库做得很好。 现在我应该映射一个存储的产品。这个 Produce 应该找到两个人之间最短的友谊路径。在 Postgres 中,产品工作正常。
这是相关的数据库表: 对于人:
CREATE TABLE Person (
PID bigint NOT NULL,
firstName varchar(50) DEFAULT NULL,
lastName varchar(50) DEFAULT NULL,
(some more...)
PRIMARY KEY (PID)
);
对于人与人之间的关系:
CREATE TABLE Person_knows_Person (
ApID bigint NOT NULL,
BpID bigint REFERENCES Person (PID) (..)
knowsCreationDate timestamp,
PRIMARY KEY (ApID,BpID));
简而言之就是存储产品:
CREATE OR REPLACE FUNCTION ShortFriendshipPath(pid bigint, pid2 bigint)
RETURNS TABLE (a_pid bigint, b_pid bigint, depth integer, path2 bigint[], cycle2 boolean)
AS $$
BEGIN
RETURN QUERY
SELECT * FROM (
WITH RECURSIVE FriendshipPath(apid, bpid, depth, path, cycle) AS(
SELECT pkp.apid, pkp.bpid,1,
ARRAY[pkp.apid], false
FROM person_knows_person pkp
WHERE apid=$1 --OR bpid=$1
UNION ALL
SELECT pkp.apid, pkp.bpid, fp.depth+1, path || pkp.apid,
pkp.apid = ANY(path)
FROM person_knows_person pkp, FriendshipPath fp
WHERE pkp.apid = fp.bpid AND NOT cycle)
SELECT *
FROM FriendshipPath WHERE bpid=$2) AS OKOK
UNION
SELECT * FROM (
WITH RECURSIVE FriendshipPath(apid, bpid, depth, path, cycle) AS(
SELECT pkp.apid, pkp.bpid,1,
ARRAY[pkp.apid], false
FROM person_knows_person pkp
WHERE apid=$2 --OR bpid=$1
UNION ALL
SELECT pkp.apid, pkp.bpid, fp.depth+1, path || pkp.apid,
pkp.apid = ANY(path)
FROM person_knows_person pkp, FriendshipPath fp
WHERE pkp.apid = fp.bpid AND NOT cycle)
SELECT *
FROM FriendshipPath WHERE bpid=$1) AS YOLO
ORDER BY depth ASC LIMIT 1;
END;
$$ LANGUAGE 'plpgsql' ;
(对不起这么多代码,但它是双向的,在我发布一些复制+减少错误之前^^) 例如 Postgre 中的调用:
SELECT * FROM ShortFriendshipPath(10995116277764, 94);
给我这个输出: enter image description here
我使用互联网寻求帮助,并找到了 3 个呼叫解决方案:
-
直接 SQL 调用
使用 NamedQuery 调用和
通过 XML 映射
(找到最喜欢的here) 我都失败了XD
我最喜欢1。解决方案在会话中使用此调用:
Session session = HibernateUtility.getSessionfactory().openSession();
Transaction tx = null;
try
tx = session.beginTransaction();
System.out.println("Please insert a second PID:");
Scanner scanner = new Scanner(System.in);
long pid2 = Long.parseLong(scanner.nextLine());
// **Insert of second ID*/
Query query2 = session.createQuery("FROM " + Person.class.getName() + " WHERE pid = :pid ");
query2.setParameter("pid", pid2);
List<Person> listB = ((org.hibernate.Query) query2).list();
int cnt1 = 0;
while (cnt1 < listB.size())
Person pers1 = listB.get(cnt1++);
pid2 = pers1.getPid();
// Query call directly:
Query querySP = session.createSQLQuery("SELECT a_pid,path2 FROM ShortFriendshipPath(" + pid + "," + pid2 + ")");
List <Object[]> list = ((org.hibernate.Query) querySP).list();
for (int i=0; i<list.size();i++)
Personknowsperson friendship = (Personknowsperson)result.get(i);
catch (Exception e) (bla..)
finally (bla....)
比我得到以下错误:
javax.persistence.PersistenceException: org.hibernate.MappingException:没有 JDBC 类型的方言映射:2003 (..blabla...)
我明白为什么。因为我的输出不是 Personknowsperson 类型。我找到了答案:我不得不说 Hibernate 什么是正确的甲酸盐。并且应该使用'UserType'。所以我试图找到一些关于我如何创建我的 UserType 的解释。但我什么也没找到,我明白。第二个问题:我不确定我应该为 bigint[] (path2) 使用什么。你看我是专家-.-
比我有了尝试3.solution的想法。但是我遇到的第一个问题是我应该在哪里写 xml 的东西。因为我的输出没有表。所以我尝试在 .cfg.xml 但 Hibernate 说
Caused by: java.lang.IllegalArgumentException: org.hibernate.internal.util.config.ConfigurationException: Unable to perform unmarshalling at line number -1 and column -1 in RESOURCE hibernate.cfg.xml. Message: cvc-complex-type.2.4.a: Ungültiger Content wurde beginnend mit Element 'sql-query' gefunden. 'some links' wird erwartet.
翻译:
找到以“sql-query”开头的无效内容
现在我很紧张。并问你。 有人可以解释我必须做什么以及我做错了什么(请假人)。如果需要更多代码(java 类或其他),请告诉我。也欢迎批评编码,因为我想要改进 =)
【问题讨论】:
【参考方案1】:好吧,我不是 postgressql 方面的专家,不是 hibernate,也不是 java。 (我正在使用 C#、SQL Server、NHibernate 所以......)我仍然尝试给你一些提示。
您可能可以使用addXyz
方法设置列的类型:
Query querySP = session
.createSQLQuery("SELECT * FROM ShortFriendshipPath(...)")
.addScalar("a_pid", LongType.INSTANCE)
...
// add user type?
您需要为数组创建一个用户类型。我不知道如何以及是否可以将其添加到查询中。请在此处查看this answer。
您也可以添加整个实体:
Query querySP = session
.createSQLQuery("SELECT * FROM ShortFriendshipPath(...)")
.addEntity(Personknowsperson.class)
...;
希望是对应映射文件的映射定义,可以指定用户类型。
通常,获得一个平面值列表要容易得多,我的意思是数组中每个不同的值都有一个单独的行。像这样:
代替
1 | 2 | (3, 4, 5) | false
你会得到:
1 | 2 | 3 | false
1 | 2 | 4 | false
1 | 2 | 5 | false
这似乎是非规范化的,但实际上是您构建关系数据的方式。
一般情况下:将 id 等内容传递给查询时使用参数。
Query querySP = session
.createSQLQuery("SELECT * FROM ShortFriendshipPath(:pid1, :pid2)")
.setParameter("pid1", pid1)
.setParameter("pid2", pid2)
...
【讨论】:
您好 Stefan,感谢您帮助我。非规范化它是没有选择的,因为我需要正确的路径顺序。我尝试用正确的列编写一个额外的实体并将其添加到 sqlquery 中,但它不起作用 XD 顺便说一句,没有性感的解决方案。就像我写的:我还发现我必须使用“用户类型”,但我仍然不明白我如何使用/实现它。而且我还没有找到任何“好的”解释 用户类型是一个接口的实现,可以在映射文件/注解中使用。实施起来并不容易,因为有一些记录不良的成员对于正确实施至关重要。 (即用于缓存的深拷贝内容,可确保正确跟踪更改。存在缓存通过引用存储数据的风险,并且更改也会意外应用于缓存并破坏更改跟踪。)以上是关于休眠:具有递归深度搜索的存储过程:映射/输出问题的主要内容,如果未能解决你的问题,请参考以下文章