Работа с БД

9 views
Skip to first unread message

Andrew Lelechenko

unread,
Feb 11, 2015, 8:00:16 PM2/11/15
to OdHUG
Привет всем!

Переписываю сейчас высоконагруженный кусок бекенда сайта с PHP на
Haskell. Вот есть у меня набор функций, сохраняющих модели в БД (в моем
случае - MySQL):

{-# LANGUAGE OverloadedStrings #-}
import Database.MySQL.Simple

...

getUserInnerId :: Line -> IO Int
getUserInnerId u@User{} = do
conn <- connect connectInfo
[Only i] <- query conn
"SELECT u.id FROM users AS u WHERE u.id_1c=? LIMIT 1"
[userId u]
return i

createUser :: Line -> IO Int
createUser u@User{} = do
conn <- connect connectInfo
execute conn
"INSERT INTO users (id_1c, email) VALUE (?, ?)"
(userId u, "user" ++ show (userId u) ++ "example.com")
i <- insertID conn
return (fromIntegral i)

updateUser :: Int -> Line -> IO ()
updateUser innerId u@User{} = do
conn <- connect connectInfo
execute conn
"UPDATE users SET name=?, groupid_1c=?, mult_insight=?, active=? WHERE
id=? LIMIT 1"
(userName u, userGroupId u, userMult u, 1 :: Int, innerId)
return ()

И все очень клево работает. Только не могу понять: как обойтись без
переподключения (connect) к БД при каждом вызове функции? Было бы
намного лучше коннектиться один раз при старте приложения.

--
С уважением,
Андрей

Roman Cheplyaka

unread,
Feb 12, 2015, 3:52:36 AM2/12/15
to od...@googlegroups.com
Можно передавать connection в каждую функцию либо с помощью
Control.Monad.Reader, либо как implicit parameter.

Евгений Беркович

unread,
Feb 13, 2015, 6:32:17 AM2/13/15
to od...@googlegroups.com

А я создал специальную монаду, которая обозначает действие с базой данных, с монадой Reader пока еще не разбирался


-- DBA(a) -- это тип обозначающий действие с базой данных, возвращающее что-то типа a

newtype DBA a = DBA { fromDBA :: (Connection -> IO (a) )}
instance Monad (DBA) where
  return a = DBA (\_->return a)
  DBA f >>= g = DBA $ \conn ->
     do
      a <- f conn
      b <- fromDBA (g a) conn
      return b


getDBBracket = \connInfo -> bracket (connectMySQL connInfo) disconnect

dbConnection :: MySQLConnectInfo -> DBA a -> IO a
-- подключается к базе данных и выполняет DBA действие используя это соединение и затем разрывает соединение
dbConnection = \connInfo -> (getDBBracket connInfo . fromDBA)

-- информация о моем соединении
myConnectInfo = defaultMySQLConnectInfo {
              mysqlUnixSocket = "/var/run/mysqld/mysqld.sock",
              mysqlHost     = "localhost",
              mysqlUser     = "root",
              mysqlPassword = "1",
              mysqlDatabase = "db_name"
           }

-- функция db превращает любое DBA действие в IO действие, добавляя вначале создание соединения с mysql, в конце -- разрыв соединения 
db :: DBA a -> IO a
db = dbConnection myConnectInfo

-- IO действия внутри DBA монады

io2DBA :: IO t -> DBA t
io2DBA io = DBA (\_ -> io)

-- Функции, которые работают с базой данных возвращают не IO(..) а DBA(..)
-- Например, эта функция ищет строку в таблице по значению поля id

lookupRowById :: String -> Int -> DBA (Maybe [SqlValue])
lookupRowById tbl id = DBA $ \conn ->
 do
  stmt <- prepare conn $ "SELECT * FROM `"++ tbl ++"` WHERE id = ?"
  execute stmt [nToSql id]
  row <- fetchRow stmt
  return row

main = db . do
 mrow1 <- lookupRowById "mytable" 1
 case mrow1 of
  Just row1 ->
    io2DBA $ putStrLn  ((fromSql (head row1)) :: String)
  Nothing ->
    io2DBA $ putStrLn "Не найдена строка с id 1"

  mrow2 <- lookupRowById "mytable" 2
  ... 


12 февраля 2015 г., 10:52 пользователь Roman Cheplyaka <ro...@ro-che.info> написал:

--
Вы получили это сообщение, поскольку подписаны на группу OdHUG.

Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес odhug+un...@googlegroups.com.
Чтобы добавлять сообщения в эту группу, отправьте письмо по адресу od...@googlegroups.com.
Перейдите в группу по ссылке http://groups.google.com/group/odhug.
Настройки подписки и доставки писем: https://groups.google.com/d/optout.

Roman Cheplyaka

unread,
Feb 13, 2015, 6:40:53 AM2/13/15
to od...@googlegroups.com
Твоя монада изоморфна ReaderT Connection IO.
> <mailto:ro...@ro-che.info>> написал:
>
> Можно передавать connection в каждую функцию либо с помощью
> Control.Monad.Reader, либо как implicit parameter.
>
> On 12/02/15 03:00, Andrew Lelechenko wrote:
> > Привет всем!
> >
> > Переписываю сейчас высоконагруженный кусок бекенда сайта с PHP на
> > Haskell. Вот есть у меня набор функций, сохраняющих модели в БД (в
> моем
> > случае - MySQL):
> >
> > {-# LANGUAGE OverloadedStrings #-}
> > import Database.MySQL.Simple
> >
> > ...
> >
> > getUserInnerId :: Line -> IO Int
> > getUserInnerId u@User{} = do
> > conn <- connect connectInfo
> > [Only i] <- query conn
> > "SELECT u.id <http://u.id> FROM users AS u WHERE u.id_1c=?
> LIMIT 1"
> > [userId u]
> > return i
> >
> > createUser :: Line -> IO Int
> > createUser u@User{} = do
> > conn <- connect connectInfo
> > execute conn
> > "INSERT INTO users (id_1c, email) VALUE (?, ?)"
> > (userId u, "user" ++ show (userId u) ++ "example.com
> <http://example.com>")
> > i <- insertID conn
> > return (fromIntegral i)
> >
> > updateUser :: Int -> Line -> IO ()
> > updateUser innerId u@User{} = do
> > conn <- connect connectInfo
> > execute conn
> > "UPDATE users SET name=?, groupid_1c=?, mult_insight=?,
> active=?
> > WHERE id=? LIMIT 1"
> > (userName u, userGroupId u, userMult u, 1 :: Int, innerId)
> > return ()
> >
> > И все очень клево работает. Только не могу понять: как обойтись без
> > переподключения (connect) к БД при каждом вызове функции? Было бы
> > намного лучше коннектиться один раз при старте приложения.
> >
>
> --
> Вы получили это сообщение, поскольку подписаны на группу OdHUG.
>
> Чтобы отменить подписку на эту группу и больше не получать от нее
> сообщения, отправьте письмо на электронный адрес
> odhug+un...@googlegroups.com
> <mailto:odhug%2Bunsu...@googlegroups.com>.
> Чтобы добавлять сообщения в эту группу, отправьте письмо по адресу
> od...@googlegroups.com <mailto:od...@googlegroups.com>.
> Перейдите в группу по ссылке http://groups.google.com/group/odhug.
> Настройки подписки и доставки писем: https://groups.google.com/d/optout.
>
>
> --
> Вы получили это сообщение, поскольку подписаны на группу "OdHUG".
> Чтобы отменить подписку на эту группу и больше не получать от нее
> сообщения, отправьте письмо на электронный адрес
> odhug+un...@googlegroups.com
> <mailto:odhug+un...@googlegroups.com>.
> Чтобы отправлять сообщения в эту группу, отправьте письмо на электронный
> адрес od...@googlegroups.com <mailto:od...@googlegroups.com>.
> Чтобы зайти в группу, перейдите по ссылке
> http://groups.google.com/group/odhug.
> Чтобы настроить другие параметры, перейдите по ссылке
> https://groups.google.com/d/optout.

Евгений Беркович

unread,
Feb 13, 2015, 6:45:20 AM2/13/15
to od...@googlegroups.com
Понятно, изобрел велосипед :)
А роль моей функции io2DBA играет стандартная функция liftIO?

13 февраля 2015 г., 13:40 пользователь Roman Cheplyaka <ro...@ro-che.info> написал:
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес odhug+un...@googlegroups.com.
Чтобы добавлять сообщения в эту группу, отправьте письмо по адресу od...@googlegroups.com.

Roman Cheplyaka

unread,
Feb 13, 2015, 6:47:08 AM2/13/15
to od...@googlegroups.com
Так точно. Либо lift, в данном случае это одно и то же.

On 13/02/15 13:45, Евгений Беркович wrote:
> Понятно, изобрел велосипед :)
> А роль моей функции io2DBA играет стандартная функция liftIO?
>
> 13 февраля 2015 г., 13:40 пользователь Roman Cheplyaka <ro...@ro-che.info
> <mailto:ro...@ro-che.info>> написал:
> > <mailto:ro...@ro-che.info <mailto:ro...@ro-che.info>>> написал:
> >
> > Можно передавать connection в каждую функцию либо с помощью
> > Control.Monad.Reader, либо как implicit parameter.
> >
> > On 12/02/15 03:00, Andrew Lelechenko wrote:
> > > Привет всем!
> > >
> > > Переписываю сейчас высоконагруженный кусок бекенда сайта с
> PHP на
> > > Haskell. Вот есть у меня набор функций, сохраняющих модели в
> БД (в
> > моем
> > > случае - MySQL):
> > >
> > > {-# LANGUAGE OverloadedStrings #-}
> > > import Database.MySQL.Simple
> > >
> > > ...
> > >
> > > getUserInnerId :: Line -> IO Int
> > > getUserInnerId u@User{} = do
> > > conn <- connect connectInfo
> > > [Only i] <- query conn
> > > "SELECT u.id <http://u.id> <http://u.id> FROM users AS
> > <mailto:odhug%2Bunsu...@googlegroups.com
> <mailto:odhug%252Buns...@googlegroups.com>>.
> > Чтобы добавлять сообщения в эту группу, отправьте письмо по адресу
> > od...@googlegroups.com <mailto:od...@googlegroups.com>
> <mailto:od...@googlegroups.com <mailto:od...@googlegroups.com>>.
> > Перейдите в группу по ссылке http://groups.google.com/group/odhug.
> > Настройки подписки и доставки писем:
> https://groups.google.com/d/optout.
> >
> >
> > --
> > Вы получили это сообщение, поскольку подписаны на группу "OdHUG".
> > Чтобы отменить подписку на эту группу и больше не получать от нее
> > сообщения, отправьте письмо на электронный адрес
> > odhug+un...@googlegroups.com
> <mailto:odhug%2Bunsu...@googlegroups.com>
> > <mailto:odhug+un...@googlegroups.com
> <mailto:odhug%2Bunsu...@googlegroups.com>>.
> > Чтобы отправлять сообщения в эту группу, отправьте письмо на электронный
> > адрес od...@googlegroups.com <mailto:od...@googlegroups.com>
> <mailto:od...@googlegroups.com <mailto:od...@googlegroups.com>>.
Reply all
Reply to author
Forward
0 new messages