PythonやPHPの連想配列的なアクセス方法が使いやすいんじゃないかとおもい、おもむろに
書いてしまったので、良く言う「グズグズ」のコードですが勘弁してください
テーブル構成と初期データ
データ構成は、userが買い物で消費した金額を保持しているのがcostテーブルですまぁあまりcostはどうでもいいのですが...
SAMPLEDBを作成
cuomo@karky7 ~ $ mysqladmin -u root create SAMPLEDB cuomo@karky7 ~ $ mysql -u root SAMPLEDB
テーブル構成、userテーブルと、costテーブルを作成
CREATE TABLE `user` ( `uid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(256) DEFAULT NULL, `age` int(11) DEFAULT NULL, `addr` text, UNIQUE KEY `uid` (`uid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cost` ( `cost_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `reg_date` datetime NOT NULL, `cost` int(11) DEFAULT '0', `ref_uid` bigint(20) unsigned NOT NULL, PRIMARY KEY (`cost_id`), UNIQUE KEY `cost_id` (`cost_id`), KEY `fk_uid_cost_id` (`ref_uid`), CONSTRAINT `fk_user_cost` FOREIGN KEY (`ref_uid`) REFERENCES `user` (`uid`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
初期データuserとcostの初期値は以下のとおり、INSERTして、すいません
mysql> select * from user; +-----+------------+------+---------------------+ | uid | name | age | addr | +-----+--------+------+-------------------------+ | 1 | ユーザー1 | 23 | 住所11 | | 2 | ユーザー2 | 42 | 住所22 | | 3 | ユーザー3 | 35 | 住所33 | | 4 | ユーザー4 | 40 | 住所44 | +-----+--------+------+-------------------------+ 4 rows in set (0.00 sec) mysql> select * from cost; +---------+---------------------+-------+---------+ | cost_id | reg_date | cost | ref_uid | +---------+---------------------+-------+---------+ | 1 | 2012-09-16 10:18:23 | 8900 | 1 | | 2 | 2012-08-09 08:30:32 | 12000 | 1 | | 3 | 2012-03-12 13:45:32 | 8700 | 1 | | 4 | 2012-04-08 18:12:12 | 3400 | 2 | | 5 | 2012-01-05 14:59:13 | 23000 | 2 | | 6 | 2011-12-31 23:59:13 | 7800 | 3 | | 7 | 2012-10-29 13:59:13 | 2300 | 3 | | 8 | 2012-07-15 09:07:08 | 5600 | 3 | | 9 | 2012-12-15 10:10:10 | 1000 | 3 | +---------+---------------------+-------+---------+ 9 rows in set (0.00 sec)
ファイル配置
ファイル配置は以下のとおり. ├── DB │ └── MySQL_Lib.hs ├── Lib │ └── TableData.hs └── Main.hs
ソースコード一式
MySQLコネクタ
ファイル: MySQL_Lib.hsmodule DB.MySQL_Lib (
SqlHash,
getDBConn,
getInsertLastId
) where
import Control.Monad
import Database.HDBC
import Database.HDBC.MySQL
import qualified Data.Map as Map
type SqlHash = Map.Map String SqlValue
getDBConn :: IO Connection
getDBConn = connectMySQL defaultMySQLConnectInfo {
mysqlHost = "127.0.0.1",
mysqlUser = "karky7",
mysqlPassword = "",
mysqlDatabase = "SAMPLEDB",
mysqlPort = 3306,
mysqlUnixSocket = ""
}
getInsertLastId :: Connection -> IO Integer
getInsertLastId con = do
[lastId:_] <- quickQuery con "SELECT LAST_INSERT_ID()" []
return $ fromSql lastId
テーブル情報へアクセスする為のライブラリ
ファイル: TableData.hsmodule Lib.TableData (
TableData,
createUser,
getUserCost,
getVal,
setVal,
getUser,
saveUser,
getCostArray
) where
import System.IO
import Control.Monad
import Database.HDBC
import Database.HDBC.MySQL
import DB.MySQL_Lib
import qualified Data.Map as Map
data TableData = TableData { val :: SqlHash } deriving(Show, Eq)
createUser xs = parse_tableData $ map (\(key, val) -> (key, toSql val)) xs
parse_tableData :: [(String, SqlValue)] -> TableData
parse_tableData tb = TableData { val = Map.fromList tb }
getVal :: String -> TableData -> String
getVal key tb = let list = val tb
target = Map.lookup key list
in case target of
Just val -> fromSql val
Nothing -> ""
setVal :: String -> String -> TableData -> IO TableData
setVal key value tb = return (TableData { val = Map.insert key (toSql value) (val tb) })
saveUser :: TableData -> IO Integer
saveUser user = do
let uid = getVal "uid" user
case isId uid of
True -> updateUser user
False -> insertUser user
updateUser :: TableData -> IO Integer
updateUser user = do
let uid = getVal "uid" user
con <- getDBConn
stm <- prepare con "UPDATE user SET name=?, age=?, addr=? WHERE uid=?"
ret <- execute stm [toSql $ getVal "name" user, toSql $ getVal "age" user, toSql $ getVal "addr" user, toSql uid ]
commit con
return (read uid::Integer)
insertUser :: TableData -> IO Integer
insertUser user = do
con <- getDBConn
stm <- prepare con "INSERT user(name, age, addr) VALUES(?, ?, ?)"
ret <- execute stm [toSql $ getVal "name" user, toSql $ getVal "age" user, toSql $ getVal "addr" user ]
commit con
getInsertLastId con
isId :: String -> Bool
isId x = let num = read x::Integer
in if num == 0
then False
else num == toInteger num
getUser :: Integer -> IO(Maybe TableData)
getUser user_id =
do
con <- getDBConn
stm <- prepare con "SELECT * FROM user WHERE uid=?"
execute stm [toSql user_id]
row <- fetchRowAL stm
case row of
Just val -> return $ Just $ parse_tableData val
Nothing -> return $ Nothing
getUserCost :: Integer -> IO [TableData]
getUserCost user_id =
do
con <- getDBConn
stm <- prepare con "SELECT * FROM cost WHERE ref_uid=?"
execute stm [toSql user_id]
rows <- fetchAllRowsAL stm
return $ map parse_tableData rows
getCost :: Integer -> IO(Maybe TableData)
getCost user_id =
do
con <- getDBConn
stm <- prepare con "SELECT * FROM cost WHERE uid=?"
execute stm [toSql user_id]
row <- fetchRowAL stm
case row of
Just val -> return $ Just $ parse_tableData val
Nothing -> return $ Nothing
getCostArray :: [TableData] -> [Integer]
getCostArray costs = map (\x -> read $ getVal "cost" x :: Integer) costs
メイン処理
ファイル: Main.hsimport System.IO
import System.Environment (getArgs)
import qualified Lib.TableData as T
import qualified DB.MySQL_Lib as MySQL_Lib
import qualified Data.Map as Map
import Database.HDBC
import Database.HDBC.MySQL
main :: IO()
main = do
args <- getArgs
let user_id = read (head args)::Integer
putStrLn "============ ユーザー検索"
user <- T.getUser user_id
case user of
Just p -> putUserInfo p
Nothing -> putStrLn "そのような人は知りません"
putStrLn ""
putStrLn "============ ユーザー登録"
let newUser = T.createUser [("uid" , "0"), ("name" , "ユーザー6"), ("age", "40"), ("addr", "住所55")]
putUserInfo newUser
nuid <- T.saveUser newUser
putStrLn $ "登録uid = " ++ show nuid
putStrLn ""
putStrLn "============ ユーザー変更"
Just user1 <- T.getUser 1
user1 <- T.setVal "age" "23" user1
modid <- T.saveUser user1
putStrLn $ "更新ユーザーID = " ++ show modid
calcPachiTotal :: String -> IO Integer
calcPachiTotal uid =
do
costs <- T.getUserCost (read uid::Integer)
if length costs > 0
then return $ foldr (+) 0 [cost | cost <- T.getCostArray costs]
else return 0
putUserInfo :: T.TableData -> IO()
putUserInfo user =
do
let uid = T.getVal "uid" user
username = T.getVal "name" user
age = T.getVal "age" user
addr = T.getVal "addr" user
putStrLn $ "ユーザーID: " ++ uid
putStrLn $ "ユーザ名: " ++ username
putStrLn $ "住所: " ++ addr
putStrLn $ "年齢: " ++ age ++ "歳"
-- 消費金額を計算
total_price <- calcPachiTotal (T.getVal "uid" user)
if total_price > 0
then
putStrLn $ "使ってしまったお金は: " ++ show total_price ++ "円です\n"
else
putStrLn "お金、使ってない、偉いね\n"
処理内容の説明
主処理はMain.hsなのですが、Mainでは3つのことを実行しています- ユーザーIDを利用したユーザー情報の取得(ユーザーIDをプログラムの引数から取得)
- ユーザー情報の新規登録
- 既に存在するユーザー情報の更新処理(uid=1のageを40〜23へ更新)
実行方法
cuomo@karky7 ~/Code/haskell/mysqlSample4 $ runhaskell Main.hs 3 ============ ユーザー検索 ユーザーID: 3 ユーザ名: ユーザー3 住所: 住所33 年齢: 35歳 使ってしまったお金は: 16700円です ============ ユーザー登録 ユーザーID: 0 ユーザ名: ユーザー6 住所: 住所55 年齢: 40歳 お金、使ってない、偉いね 登録uid = 9 ============ ユーザー変更 更新ユーザーID = 1
今回はTableDataの中にMapデータとして持たせたところが味噌なのですがどうでしょう?
イメージとしては、それぞれのテーブルデータをTableDataへ突っ込んで管理するみたいな感じなのですが。
insert、update、などの関数をテーブル毎に書かなければならないところが気に入らないデスが
アクセスはしやすいかと思いますが、Map k vのからfromSqlで取り出すときがちょっと気になります(すべてString型にしてしまっている)
SqlValueをそのままfromSqlした型で返せないかと頑張ったのですが、どうもうまく出来ませんでした 笑...まぁHTMLとかで使うならこれでもいいと思うのですが
もし、こんなアクセス方法あるよってネタがあったら、何方か、後教授を...適当ですいません...
0 件のコメント:
コメントを投稿