Deno でのデータベースアクセスを試したので、記録を残しておきます。 試すのはこれら。
それでは試していきます。
参考
環境準備 dene の実行環境は、docker で用意する。
docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 version: "3" services: app: build: context: . dockerfile: Dockerfile privileged: true entrypoint: - /sbin/init ports: - "8080:8080" volumes: - .:/usr/src/app:cached tty: true db: image: mysql:5.7 command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci ports: - "${MYSQL_PORT:-3306}:3306" volumes: - ./db/mysql_data:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-}
dockerfile 1 2 3 4 5 6 7 8 9 10 FROM denoland/deno:centosRUN yum update -y && \ yum install -y which systemd-sysv crontabs && \ yum install -y mysql-devel mysql RUN mkdir /usr/src/app WORKDIR /usr/src/app EXPOSE 8080
deno_mysql deno_mysql は、ORM ではなくてデータベースドライバー。 Active Record を触るのが日常になった今、なかなかデータベースドライバーを直接触ることが少なくなりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 import { Client } from "https://deno.land/x/mysql/mod.ts" ;import { configLogger } from "https://deno.land/x/mysql/mod.ts" ;await configLogger ({ enable : false });interface User { id : number ; name : string ; created_at : string ; } interface Item { id : number ; user_id : number ; name : string ; created_at : string ; } const connectionParam = { hostname : "db" , username : "ユーザー名" , password : "パスワード" , db : "testdb" , }; const client = await new Client ().connect (connectionParam);await client.execute (`CREATE DATABASE IF NOT EXISTS ${connectionParam.db} ` );await client.execute (` CREATE TABLE IF NOT EXISTS users ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, created_at timestamp not null default current_timestamp, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ` );const resultInsert = await client.execute (`Insert Into users(name) values(?)` , [ "username" , ]); console .log (resultInsert);const targetId = resultInsert.lastInsertId ;let resultUpdate = await client.execute ( `update users set ?? = ? where ?? = ?` , ["name" , "username_1" , "id" , targetId] ); console .log (resultUpdate);let result = await client.execute (`delete from users where ?? = ?` , [ "id" , targetId, ]); console .log (result);const users : User [] = await client.query (`select * from users` );console .log (users);const resultSelect = await client.execute (`select * from users` );console .log (resultSelect);await client.execute (` CREATE TABLE IF NOT EXISTS items ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) NOT NULL, name varchar(100) NOT NULL, created_at timestamp not null default current_timestamp, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ` );const resultTran = await client.transaction (async (conn) => { const result1 = await conn.execute (`Insert Into users(name) values(?)` , [ "username" , ]); console .log (result1); const result2 = await conn.execute ( `Insert Into items(user_id, name) values(?, ?)` , [result1.lastInsertId , "itemname" ] ); return { result1, result2 }; }); console .log (resultTran);users.map ((user ) => { console .log (user.id ); }); await client.close ();
昔、Promise-mysql とか Node MySQL 2 触っていたころのことを思い出しました。
dso - Deno Simple Orm - 先に試した、deno_mysql ベースの ORM だそう。 試してみたところ、deps.ts 内での std ライブラリのバージョン指定がどうも違うらしく、動作できず。 こういうの修正ってどうやって試したらいいんだろう。?
1 2 3 4 5 ソースコード "https://deno.land/std@v0.51.0/testing/asserts.ts"; おそらく正しくは "https://deno.land/std@0.51.0/testing/asserts.ts";
プルリク出せるかしら。
DenoDB 続けて DenoDB。 こちらは Deno 向け ORM. とりあえず基本操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 import { DataTypes , Database , Model , MySQLConnector , } from "https://deno.land/x/denodb@v1.0.40/mod.ts" ; const connection = new MySQLConnector ({ host : "db" , username : "ユーザー名" , password : "パスワード" , database : "testdb" , }); const db = new Database (connection);class User extends Model { static table = "users" ; static fields = { id : { primaryKey : true , autoIncrement : true }, name : DataTypes .STRING , }; static defaults = { name : "default_name" , }; static timestamps = true ; } db.link ([User ]); await db.sync ({ drop : true });await User .create ({});await User .create ({ name : "user_A" , }); const user1 = new User ();user1.name = "user_B" ; await user1.save ();await User .create ([ { name : "user_C" , }, { name : "user_D" , }, ]); const user2 = await User .where ({ id : 4 });await user2.update ({ name : "user_CCCC" });console .log ( await User .select ("id" ).where ("id" , ">" , "3" ).get () ); console .log (await User .where ("id" , ">" , "3" ).count ());await User .deleteById ("1" );const user3 = await User .where ({ id : 2 });await user3.delete ();const user4 = await User .all ();const deleteQuerys = user4.map (async (u) => !u.id ? "" : await User .deleteById (u.id .toString ()) ); await Promise .all (deleteQuerys);await db.close ();
イイ感じに使えるが、toSql()
的なメソッドが公開されていないところが少々残念。 private では持っているようなので、できれば公開されてほしい。
一旦は、データベース側で実行クエリをロギングして、おけばイイかな?という見込み。MySQL に投げられたすべての SQL クエリをロギングする
リレーション 続けて、リレーションとトランザクションは以下のようになります。 ドキュメントに書いているとおりだとエラーになること確認したので、注意が必要でした。
外部キー 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import { Database , DataTypes , Model , MySQLConnector , Relationships , } from "https://deno.land/x/denodb@v1.0.40/mod.ts" ; const connection = new MySQLConnector ({ host : "db" , username : "ユーザー名" , password : "パスワード" , database : "testdb" , }); const db = new Database (connection);class User extends Model { static table = "users" ; static fields = { id : { primaryKey : true , autoIncrement : true , type : DataTypes .INTEGER }, Name : DataTypes .STRING , }; static defaults = { name : "default_name" , }; static timestamps = true ; } class Item extends Model { static table = "items" ; static fields = { id : { primaryKey : true , autoIncrement : true , type : DataTypes .INTEGER }, name : DataTypes .STRING , }; static defaults = { name : "default_item_name" , }; static user ( ) { return this .hasOne (User ); } static timestamps = true ; } Relationships .belongsTo (Item , User );db.link ([User , Item ]); await db.sync ({ drop : true });const { lastInsertId } = await User .create ({});if (!!lastInsertId) { await Item .create ({ userId : lastInsertId as number }); console .log (await User .all ()); console .log (await Item .all ()); console .log (await Item .where ("id" , 1 ).user ()); }
Item -> User は追えるが、逆をたどることはできない。
一対多 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 import { Database , DataTypes , Model , MySQLConnector , Relationships , } from "https://deno.land/x/denodb@v1.0.40/mod.ts" ; const connection = new MySQLConnector ({ host : "db" , username : "ユーザー名" , password : "パスワード" , database : "testdb" , }); const db = new Database (connection);class User extends Model { static table = "users" ; static fields = { id : { primaryKey : true , autoIncrement : true , type : DataTypes .INTEGER }, Name : DataTypes .STRING , }; static defaults = { name : "default_name" , }; static items ( ) { return this .hasMany (Item ); } static timestamps = true ; } class Item extends Model { static table = "items" ; static fields = { id : { primaryKey : true , autoIncrement : true , type : DataTypes .INTEGER }, name : DataTypes .STRING , }; static defaults = { name : "default_item_name" , }; static user ( ) { return this .hasOne (User ); } static timestamps = true ; } Relationships .belongsTo (Item , User );db.link ([User , Item ]); await db.sync ({ drop : true });const { lastInsertId } = await User .create ({});if (!!lastInsertId) { await Item .create ([ { userId : lastInsertId as number }, { userId : lastInsertId as number }, { userId : lastInsertId as number }, ]); console .log (await User .all ()); console .log (await Item .all ()); console .log (await Item .where ("id" , 1 ).user ()); console .log (await User .where ("id" , 1 ).items ()); } await db.close ();
ActiveRecordでイメージするような、データの取得ができました。 hasManyなリレーションだけ上の通り記載しましたが、もちろん1対1 多対多もあります。
トランザクション 最後にトランザクション。 残念ながら、__このコードは動きません__。
error: Module evaluation is still pending but there are no pending ops or dynamic imports. This situation is often caused by unresolved promise.
となります。
更新:v1.0.40 ではエラーにならなくなったが、Promiseの結果が返ってこないという状況になっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import { Database , DataTypes , Model , MySQLConnector , Relationships , } from "https://deno.land/x/denodb@v1.0.40/mod.ts" ; const connection = new MySQLConnector ({ host : "db" , username : "ユーザー名" , password : "パスワード" , database : "testdb" , }); const db = new Database (connection);class User extends Model { static table = "users" ; static fields = { id : { primaryKey : true , autoIncrement : true , type : DataTypes .INTEGER }, Name : DataTypes .STRING , }; static defaults = { name : "default_name" , }; static items ( ) { return this .hasMany (Item ); } static timestamps = true ; } class Item extends Model { static table = "items" ; static fields = { id : { primaryKey : true , autoIncrement : true , type : DataTypes .INTEGER }, name : DataTypes .STRING , }; static defaults = { name : "default_item_name" , }; static user ( ) { return this .hasOne (User ); } static timestamps = true ; } Relationships .belongsTo (Item , User );db.link ([User , Item ]); await db.sync ({ drop : true });await db.transaction (async () => { const { lastInsertId } = await User .create ({}); if (!lastInsertId) return await Item .create ([ { userId : lastInsertId as number }, { userId : lastInsertId as number }, { userId : lastInsertId as number }, ]); }); await db.close ();
DenoDB かなりイイじゃないか!と感じていたのも束の間、トランザクションが上手く動かなかったり。 「deno_mysql使うのが結局素直なのでは?」というところが、所感でした。 「Deno 使いの方々は、DBのアクセスに何を使っているのか?」というところが興味あるところです。
ではでは。