haskell-persistent:如何在第二个表中使用外键作为主键

Posted

技术标签:

【中文标题】haskell-persistent:如何在第二个表中使用外键作为主键【英文标题】:haskell-persistent: how to use a foreign key as primary key in a second table 【发布时间】:2016-12-23 10:38:34 【问题描述】:

更新 - 找到了错误的根本原因 - 但不知道如何解决它

我刚刚发现这种行为的根源不是使用sql=…引起的,而是我使用第一个表的主键作为外部主键。

Post
  topic String

  deriving Show Eq

PostContent
  Id PostId
  content String

  deriving Show Eq

所以问题仍然存在:

我可以以某种方式持久地表示主键是外键吗? - 从 SQL 的角度来看,这是有道理的(至少我是这么认为的)?

原创

我正在将 simon marlow Fun With HAXL pt1 的 haxl 示例移植到 oracle/docker - 以进行概念验证。

我正在使用现有的 sql 脚本来生成数据库(在现实世界的情况下,我没有数据库表) - 我有以下数据库布局

表发布信息

| POSTID NUMBER | POSTDATE DATE | POSTTOPIC VARCHAR2(512 CHAR) |

表后内容

| POSTID NUMBER | CONTENT CLOB |

表后视图

| POSTID NUMBER | VIEWS INT |

当然我想在对应的haskell持久化QuasiQuoter中表达POSTIDpostcontentpostview中的外键和唯一键的关系。在维基链接的yesod-book、wiki 和test cases 之后。

我创建了以下模板 haskell 拼接:

share [ mkPersist sqlSettings mpsGeneric = False , mkMigrate "compositeMigrate" , mkDeleteCascade sqlSettings mpsGeneric = False] [persistUpperCase|

Post sql=POSTINFO
  Id Int sql=POSTID
  date UTCTime sql=POSTDATE
  topic Text sql=POSTTOPIC

  deriving Show Eq

PostContent sql=POSTCONTENT
  Id PostId sql=POSTID
  content Text sql=CONTENT
  deriving Show Eq

PostViews sql=POSTVIEWS
  Id PostId sql=POSTID
  views Int sql=VIEWS
  deriving Show Eq
|]

编译出错

error:
    • Not in scope: type constructor or class ‘PostId’
    • In the quasi-quotation:
        [persistUpperCase|

Post sql=POSTINFO
  Id Int sql=POSTID
  date UTCTime sql=POSTDATE
  topic Text sql=POSTTOPIC

  deriving Show Eq

PostContent
  Id PostId sql=POSTID
  content Text sql=CONTENT
  deriving Show Eq

PostViews
  Id PostId sql=POSTID
  views Int sql=VIEWS
  deriving Show Eq
|]

要注意以下测试用例准引用器 - 有效,

  Citizen
    name String
    age Int Maybe
    deriving Eq Show
  Address
    address String
    country String
    deriving Eq Show
  CitizenAddress
    citizen CitizenId
    address AddressId
    Primary citizen address
    deriving Eq Show

这是一个最小的示例,它重现了错误和一些刚刚运行的工作版本(并相应地更改 #define

> stack runhaskell --package persistent-template minimal.hs

minimal.hs

-# LANGUAGE FlexibleInstances          #-
-# LANGUAGE GADTs                      #-
-# LANGUAGE GeneralizedNewtypeDeriving #-
-# LANGUAGE MultiParamTypeClasses      #-
-# LANGUAGE QuasiQuotes                #-
-# LANGUAGE TemplateHaskell            #-
-# LANGUAGE TypeFamilies               #-
-# LANGUAGE CPP                        #-

module Minimal where

import Database.Persist.TH

#define FAILS
main :: IO ()
main = putStrLn "It works"


#ifdef WORKS
share [ mkPersist sqlSettings mpsGeneric = False , mkMigrate "compositeMigrate" , mkDeleteCascade sqlSettings mpsGeneric = False] [persistUpperCase|

Post sql=POSTINFO
  Id Int sql=POSTID
  topic String sql=POSTTOPIC

  deriving Show Eq
|]
#endif

#ifdef ALSOWORKS
share [ mkPersist sqlSettings mpsGeneric = False , mkMigrate "compositeMigrate" , mkDeleteCascade sqlSettings mpsGeneric = False] [persistUpperCase|

Post sql=POSTINFO
  Id Int sql=POSTID
  topic String sql=POSTTOPIC

  deriving Show Eq


PostContent sql=POSTCONTENT
  post PostId sql=POSTID
  content String sql=POSTCONTENT

  deriving Show Eq
|]
#endif

#ifdef FAILS
share [ mkPersist sqlSettings mpsGeneric = False , mkMigrate "compositeMigrate" , mkDeleteCascade sqlSettings mpsGeneric = False] [persistUpperCase|

Post sql=POSTINFO
  Id Int sql=POSTID
  topic String sql=POSTTOPIC

  deriving Show Eq

PostContent sql=POSTCONTENT
  Id PostId sql=POSTID
  content String sql=POSTCONTENT

  deriving Show Eq
|]
#endif

-- UPDATE

#ifdef FAILSTOO
share [ mkPersist sqlSettings mpsGeneric = False , mkMigrate "compositeMigrate" , mkDeleteCascade sqlSettings mpsGeneric = False] [persistUpperCase|

Post
  topic String

  deriving Show Eq

PostContent
  Id PostId
  content String

  deriving Show Eq
|]
#endif

【问题讨论】:

我认为您应该将其作为单独的答案发布,而不是编辑您的问题。 我认为我找到了原因-但我不知道如何解决-绝对是用词错误 啊,好的。一个明显的问题:您知道 Oracle 没有持久绑定吗? 嗯 - 我已经开始使用 persistent-odbc - 我花了一段时间,但我设法将查询从 Haskell 发送到 Oracle 数据库。 【参考方案1】:

我可以以某种方式持久地表示主键是外键吗?

是的。假设 Sqlite 作为数据库的示例代码示例:

#!/usr/bin/env stack
- stack
     --resolver lts-7.14
     --install-ghc
     runghc
     --package yesod
     --package yesod-core
     --package blaze-html
     --package text
     --package persistent
     --package persistent-template
     --package persistent-sqlite
     --package shakespeare
     --package aeson
-

-# LANGUAGE EmptyDataDecls #-
-# LANGUAGE FlexibleContexts #-
-# LANGUAGE GADTs #-
-# LANGUAGE GeneralizedNewtypeDeriving #-
-# LANGUAGE MultiParamTypeClasses #-
-# LANGUAGE OverloadedStrings #-
-# LANGUAGE QuasiQuotes #-
-# LANGUAGE TemplateHaskell #-
-# LANGUAGE TypeFamilies #-

import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger (runStderrLoggingT)
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH

share
  [mkPersist sqlSettings, mkMigrate "migrateAll"]
  [persistLowerCase|
Post
    topic String
    deriving Show 
PostContent
    pid PostId
    Primary pid
    deriving Show
|]

main :: IO ()
main = mockMigration migrateAll

在执行时,你会得到这个:

CREATE TABLE "post"("id" INTEGER PRIMARY KEY,"topic" VARCHAR NOT NULL)
CREATE TABLE "post_content"("pid" INTEGER NOT NULL REFERENCES "post", PRIMARY KEY ("pid"))

在上面的例子中可以看到post_content表中的pid列既是主键也是外键。

【讨论】:

以上是关于haskell-persistent:如何在第二个表中使用外键作为主键的主要内容,如果未能解决你的问题,请参考以下文章

如何在第二个 ViewController 表格视图中使用模型快速显示第一个 ViewController 值

如何在第二个视图控制器上显示核心数据?

左外连接 - 如何在第二个表中返回一个布尔值?

我如何在 android 中显示从 listView 到第二个活动的相同数据,如果我点击 google,它会在第二个活动中显示 google

如何在第二个 VC 中使用第一个 VC 的方法?

SwiftUI TabView 如何在第二个选项卡单击时加载更多数据