休眠:具有递归深度搜索的存储过程:映射/输出问题

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 顺便说一句,没有性感的解决方案。就像我写的:我还发现我必须使用“用户类型”,但我仍然不明白我如何使用/实现它。而且我还没有找到任何“好的”解释 用户类型是一个接口的实现,可以在映射文件/注解中使用。实施起来并不容易,因为有一些记录不良的成员对于正确实施至关重要。 (即用于缓存的深拷贝内容,可确保正确跟踪更改。存在缓存通过引用存储数据的风险,并且更改也会意外应用于缓存并破坏更改跟踪。)

以上是关于休眠:具有递归深度搜索的存储过程:映射/输出问题的主要内容,如果未能解决你的问题,请参考以下文章

数据结构题目,广度优先和深度优先

深度优先搜索和广度优先搜索的比较与分(转)

深度优先搜索算法解释下?

多级树 深度优先搜索算法和广度优先搜索算法

如何使用类属性映射休眠中的列?

算法总结深搜