Yesod中的复合主键

Posted

技术标签:

【中文标题】Yesod中的复合主键【英文标题】:Composite Primary Key in Yesod 【发布时间】:2015-05-02 18:16:14 【问题描述】:

我对 Haskell 还很陌生,并且已经用 yesod 试验了大约一个星期。我一直在尝试连接到在 sqlite 中具有复合主键的现有数据库。我设法让代码与 Database.Persist.Sqlite 作为独立应用程序一起使用。

这是使用 persistent-sqlite 作为独立应用程序运行的代码。

-# LANGUAGE EmptyDataDecls             #-
-# LANGUAGE FlexibleContexts           #-
-# LANGUAGE GADTs                      #-
-# LANGUAGE GeneralizedNewtypeDeriving #-
-# LANGUAGE MultiParamTypeClasses      #-
-# LANGUAGE OverloadedStrings          #-
-# LANGUAGE QuasiQuotes                #-
-# LANGUAGE TemplateHaskell            #-
-# LANGUAGE TypeFamilies               #-
-# LANGUAGE FlexibleInstances #-
-# LANGUAGE DeriveGeneric #-
import           Control.Monad.IO.Class  (liftIO)
import           Database.Persist
import           Database.Persist.Sqlite
import           Database.Persist.TH
import           System.Environment
import           Data.Text (Text,pack)
import           Data.Time (UTCTime)

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
    Movie
    title Text maxlen=20
    year Int maxlen=11
    genre Text Maybe maxlen=128
    mpaa Text Maybe maxlen=16
    director Text Maybe maxlen=128
    actors Text Maybe maxlen=512
    description Text Maybe maxlen=512
    path Text Maybe maxlen=128
    codec Text Maybe maxlen=32
    length Int Maybe maxlen=11
    poster Text Maybe maxlen=128
    added UTCTime default=CURRENT_TIMESTAMP
    Primary title year
    deriving Show
|]

main :: IO ()
main = do
  (path:args) <- getArgs
  movies <- runSqlite (pack path) $ do
    runMigration migrateAll
    selectList [] [Desc MovieTitle]

  mapM_ print movies

这是一个人为的例子,但它说明了我的观点。我想要一个由titleyear 组成的复合主键。一切都编译得很好,表是用我需要的模式创建的。当我尝试在我的 yesod 应用程序中使用上面定义的 Movie 类型和 persistent-sqlite 时,我收到以下编译时错误

Foundation.hs:35:1:
    No instance for (PathPiece MovieId)
      arising from a use of ‘toPathPiece’
    In the first argument of ‘(:)’, namely ‘(toPathPiece dyn_apvA)’
    In the second argument of ‘(:)’, namely
      ‘((toPathPiece dyn_apvA) : [])’
    In the expression:
      ((Data.Text.pack "entry") : ((toPathPiece dyn_apvA) : []))

Foundation.hs:35:1:
    No instance for (PathPiece MovieId)
      arising from a use of ‘fromPathPiece’
    In the expression: fromPathPiece
    In the pattern: fromPathPiece -> Just dyn_apwt
    In the pattern: (:) (fromPathPiece -> Just dyn_apwt) []

我被困在这一点上,无法自己继续。我尝试过搜索,但我只能找到一个不使用 yesod 的复合主键的工作示例。我觉得应该可以这样做,因为仅使用 persistent-sqlite 的复合主键有效。

这是Movie 类型在config/models 中的定义方式

Movie
    title Text maxlen=20
    year Int maxlen=11
    genre Text Maybe maxlen=128
    mpaa Text Maybe maxlen=16
    director Text Maybe maxlen=128
    actors Text Maybe maxlen=512
    description Text Maybe maxlen=512
    path Text Maybe maxlen=128
    codec Text Maybe maxlen=32
    length Int Maybe maxlen=11
    poster Text Maybe maxlen=128
    added UTCTime default=CURRENT_TIMESTAMP
    Primary title year
    deriving

【问题讨论】:

【参考方案1】:

我自己并没有真正使用过复合主键功能,但这个错误对我来说是有意义的。对于任意复合键,Text 没有明显的序列化,因此持久性不会为您生成它。如果您想在 URL 中使用 MovieId,则需要手动定义 PathPiece 实例,这只是一对用于与 Text 相互转换的函数。

【讨论】:

以上是关于Yesod中的复合主键的主要内容,如果未能解决你的问题,请参考以下文章

如何识别任何 Mysql 数据库表中的复合主键?

sql工作台中的复合主键与自动递增主键

联结表中的复合主键 - Sequelize

使用 Quarkus 的 JPA 中的复合主键是不是可能?

RedShift - 如何通过复合主键过滤表中的记录?

MySQL 中的复合主键示例