Play Scala周りで出てくるよくわからん単語を整理していく
Scalaを触ってると聞いたことない単語がバンバン出てくる。
参考となるドキュメントを漁ってみても、Java上がりの人向けに書いてる記事が多く、
業務でJavaを使ったことがない人間からしたら結構ツラい。
それでもPlay ScalaでDBアクセスをしようとがんばって調べつつ設定をしていたが、
だんだんわけがわからない状態になってきたので、一旦言葉の意味と関係性を整理したい。
DBアクセス用ライブラリ
調べてみたらこちらの記事が欲しい情報をほぼ全て網羅していた。
一応メモしたことを書いておく。
JDBC
JavaとRDBとの接続用API。
conn = DriverManager.getConnection()
して
conn.createStatement()
したStatementオブジェクトにSQL文を渡して
executeQuery()
ってしたら漸くDBにCRUDができる。
Webアプリケーション開発で初めて触ったのがRailsのActiveRecordだったので
かなり面倒に感じる。
Hibernate
JavaのORMライブラリ。
JDBCを使ってRDBMSと接続しているっぽい。
実装用のインタフェースが直接JDBCを扱うより充実しているんでしょう。
使ったことはないのでわかりませんが。
ScalikeJDBC
Scala用のORMライブラリ。
これもJDBC上で動くっぽい。
RDB
H2
組み込み用RDB。DBサーバとしても使える。
Android上に載っているSQliteと同じカテゴリ。
JDBC, Hibernateなどを使ってアクセスできる。
MySQL
略
PostgreSQL
ry
MongoDB
r
カテゴライズできないもの
Akka
Scalaのライブラリ。並行処理やアクターモデルに対応している。
ここが詳しい。
www.slideshare.net
Maven
ソフトウェアプロジェクト管理ツール。
ここが詳しい。
1. Maven とは | TECHSCORE(テックスコア)
Play Scalaにどう関連するかというと、
Maven RepogitoryにあるlibraryDependencies
を
build.sbt
に追記すると、ビルド時に記入したソフトウェアをインストールしてきてくれる。
例えばDB周りでslick
とpostgresql
を使いたい場合は以下のように書く。
build.sbt
1 name := """play_test""" 2 3 version := "1.0-SNAPSHOT" 4 5 lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 7 scalaVersion := "2.11.7" 8 9 libraryDependencies ++= Seq( 10 cache, 11 ws, 12 "org.postgresql" % "postgresql" % "42.1.4", 13 "com.typesafe.play" %% "play-slick" % "2.0.0", 14 )
ここまででわかったこと
言葉の関係性を整理した結果、 DB周りに関してはこんな感じの組み合わせができる、ように見える。
- Play Scala * slick * PostgreSQL
- Play Scala * scalikeJDBC * MySQL
- Play Scala * anorm * H2
など。
今回のWebアプリケーションでは1の組み合わせを採用したい。
PostgreSQLを含めたNoSQLをまともに触ったことがないので、この機会に触ってみたい。
テーブルカテゴライズ
業務でテーブル設計をレビューしていただいて、色々考えることがあったので、例え話で書きます。
繰り返しますが全て例え話です。
前提
- Appleの製品をRDBMS上でテーブルとして扱いたい。
- 扱いたい製品はiPhoneとiPadの2種類で、それぞれシリーズによって分類したい。
- 製品はロジック上は違いを意識したくない。
- 製品情報はシステム上では末端の情報で、他のロジックに対して及ぼす影響は少ない。
このようなシステム要件に対して、以下のような設計をして、案1として提案した。
apple_products
テーブル
column_name | type | description |
---|---|---|
id | int | なし |
purchase_date | date | 購入日 |
product_type | int | 1: iPhone, 2: iPad |
iphones
テーブル
column_name | type | description |
---|---|---|
id | int | なし |
apple_produt_id | int | apple_productのid |
product_type | int | 1固定 |
series | int | 1: iPhone6, 2: iPhone6S, 3: iPhone7 |
serial_number | text | シリアルナンバー |
phone_number | text | 電話番号 |
ipads
テーブル
column_name | type | description |
---|---|---|
id | int | なし |
product_type | int | 2固定 |
apple_produt_id | int | apple_productのid |
series | int | 1: iPad, 2: iPad mini, 3: iPad Pro |
serial_number | text | シリアルナンバー |
apple_products
の1つ1つのレコードにはiphones
のレコードかipads
のレコード、
どちらか1つがくっついている、という構成。
Rails上の実装はこんな感じをイメージしていた。
class AppleProduct < ActiveRecord::Base has_one :iphones has_one :ipads def product return iphone if product_type == 1 ipads if product_type == 2 end end
この設計がどうか、という相談をしたら、
以下のようにしたらいいのでは、というお話をいただいた。
これを案2とする。
apple_products
テーブル
column_name | type | description |
---|---|---|
id | int | なし |
purchase_date | date | 購入日 |
specific_products
テーブル
column_name | type | description |
---|---|---|
id | int | なし |
apple_produt_id | int | apple_productのid |
product_type | int | 1: iPhone, 2: iPad |
series | int | 1: iPhone6, 2: iPhone6S, 3: iPhone7, 4: iPad, 5: iPad mini, 6: iPad Pro |
serial_number | text | シリアルナンバー |
phone_number | text | 電話番号 ない場合は空文字 |
つまり、「iPhoneもiPadもいっしょくたにして1つのテーブルにしてしまえば?」というお話。
この例ではiphones
テーブルとipads
テーブルのカラムの違いはphone_number
だけだが、
実際に業務で使っているテーブルはカラムの違いが他にもあって、
今回の例に照らすと、specific_products
にipad
の情報を入れたら、
そこそこの数のnull
なり空文字なりが入って歯抜け情報になってしまうという状況。
それでもこっちの構成のほうが楽じゃない?と言われて、理由が全然わからなかった。
歯抜けの情報があるって気持ち悪いし、別のものなら別のものとしてテーブルを分けるのが自然な発想なんじゃないの?って。
で、理由を訊いてみたら以下のような内容だった。
apple_products
のレコードが、自分の持っているproductの情報を知らなくてすむ。
案1だと、自分の持っているproductがiphone
なのかipad
なのかをapple_product
が知っていることになる。それはapple_product
にとってはどうでもいいこと。- RDBMSの観点から明らかに案2の方がシンプル。Rails上でも
AppleProduct.includes(:specific_product)
と書ける。
案1では、DBに問い合わせして結果が返ってくるまで各apple_products
レコードがiphone
,ipad
どちらを持っているのかわからないので、無意識にjoin
したりinclude
したりできない。 - 確かに
null
や空文字など歯抜けの情報はあるが、それはRails上でうまいことやればよい。
これらの理由は納得できるものだったので、最終的に案2の方針でいくことに決めた。
持ち帰って改めて考えてみたけど、今まで自分の中に「設計はできるだけ実体に即したものにする、もしくはがんばってそれに近づける」という発想しかなくて、
「データとして楽に扱えるようにする」という発想がなかったと思った。
「システムとしてはこういうものができていればいいね」という段階までビジネス要件が落とし込んであれば、
設計はエンジニアが構成を見てどういうものなのかがわかれば良くて、あとはデータとして楽に扱える、ということを主眼において考えればよいのかなと思う。
案2のRails上の実装も考えてみたけど、こんなかんじになるだろうか。
apple_product.rb
class AppleProduct < ActiveRecord::Base has_one :specific_product def product specific_product.product end end
specific_product.rb
class SpecificProduct < ActiveRecord::Base belongs_to :apple_product def product return IPhone.new if product_type == 1 IPad.new if product_type == 2 end def iphone IPhone.new end def ipad IPad.new end class IPhone attr_reader :product_type, :type, :searial_number, :phone_number def initialize @product_type = product_type @series = series @searial_number = searial_number @phone_number = phone_number end end class IPad attr_reader :product_type, :type, :searial_number, :phone_number def initialize @product_type = product_type @series = series @searial_number = searial_number @phone_number = "-" end end private_constant :IPhone, :IPad end
別の話になるけど、色々考えてるうちに、ORMとしてのActiveRecord
って何を隠蔽してるんだ?と思った。
Rails上でレコードをオブジェクトとして扱えるっていうのは便利だと思うけど
includes
とかするなら結局テーブル構成見なきゃいけないし、SQL文がちょっと楽に書けるようになっているだけでは…?
Play Scala 触っててわかったこと
標題の通り。
activator run
でサーバ立ち上げた後、下記は次のアクセス時にオートロードされる。
- ファイルの中身変更
- ファイル名変更
- ファイル新規作成
オートロードされるというか、次のリクエストが飛んできたら
project内にファイルないしコードの差分があるかどうかを検知して
コンパイルし直す、みたいなことをしているっぽい。
編集したコードのインスタンスを呼び出す画面とは違う画面にアクセスしても、
編集部分のエラーがコンソールログに吐かれる & 画面に表示される。
詳しくはまだわからんけど、
「システムのどっかに1こでもエラーがあったら何も表示したらんぞ」
ていうスタイル、に見える。
つまりエラーが出ない状態になった時点でシステムに型エラーがない(少ない?)ことが
ある程度担保されているわけで。
これは好ましい仕組みかなと思う。
なんかエラーとかバグとか、言葉の意味を自分の中で混同している気がする。
RailsもSyntax Errorくらいなら出してくれるし、
Playである、というかScalaであることの恩恵に関してもっと理解が必要だなと思った。
レアジョブに申し込んでみようとした
「エンジニアに最も必要な言語は英語」ってどっかで読みました。
WritingやReadingはある程度自分ひとりでできますが、会話は相手がいないとできないですね。
英語が普通に喋れるようになりたい。仕事で使えるようになりたい。
でも時間はないし金はかけたくない。
てかんじで、オンライン英会話に行き着きました。
調べたら日本のオンライン英会話サービスはDMM英会話とレアジョブの二大巨頭って感じ。
ここらへんの記事を読んでみる。
みたとこ、専門性が高くてビジネスに使う分にはレアジョブがいいんじゃない?と読めた。
ので、レアジョブを使うことにする。
で、安く申し込みしたいなーと思ってたらこんな記事も見つけた。
毎月10%オフってめっちゃ安いやん。
てことでベネフィット・ワンに登録して申し込んでみる。
クーポン紹介記事には10%オフクーポンが載っていたが
2017/10/31までに申し込むと更に6レッスン分の料金が無料らしい。
すご。
ただ1つ引っかかったことがあって。
これ、月割で支払うのか一括で支払うのかどっちなんやろ。
レアジョブに電話して訊いてみましたが、
ベネフィット・ワンからの申込みだと一括支払いとのこと。
日常英会話コース 毎日25分・6か月プラン 37,584円 ⇒ 33,800円
ビジネス英会話コース 毎日25分・5か月プラン 52,920円 ⇒ 47,600円
…一括か。
ってことで一旦保留にしました。
多分申し込むと思うけど。
ちなみにレアジョブ上でクレカで申し込んだら月ごとの支払いとのことです。
参考までに。
Play Scala Controller, Viewの追加
やること
- Controllerを定義する。
- Viewを定義してブラウザ上で表示させる。
設計はこちらを参考に。
やったこと
/app/controllers
下にArticleController.scala
を作成する。
書き方は既存のサンプルのControllerに倣う。
package controllers import javax.inject._ import play.api._ import play.api.mvc._ @Singleton class ArticleController @Inject() extends Controller { def index = Action { Ok(views.html.article.index("Articleだぞ")) } }
次に、/app/views/
下にarticle
ディレクトリを作成して、
その中にindex.scala.html
を定義する。
@(message: String)
<h1>Articleです</h1>
<p>ぜったいってなに</p>
詳しくはわかってないけど、現状viewに渡したmessage
は呼び出さないと
「controllerで引数渡してるのに参照してねえぞ」みたいなエラーが出るので、
しょうがなく@(message: String)
を書いておく。
次。
/conf/routes
でroutesを設定する。
書き方は既存に倣う。
GET /article controllers.ArticleController.index
画面を作るのに必要そうなことはやった。
projectのhomeディレクトリでactivator run
して
localhost:9000/article
にアクセスする。
ださ。
90年代のホームページってかんじ。
でもまあとりあえずできた。
- TODO
- DB接続 (postgresql)
- model定義
- seedsでレコードサンプルを定義して適当に一覧表示する
転職ドラフト
登録しました。
明日ドラフトだって聞いたのでレジュメ頑張って書いたんですけど
審査通らなくて、修正して再申請したけど駄目だったんで今回は諦めます。
これで自分のおおよその市場価値をはかろうと思ったんですがそもそも土俵にすら立ててないですね。
「技術に関するアピールをもっと書いてください」ていうフィードバックをいただいたんですが、
技術に関して「俺こんなんやったぜ!」みたいに言えること特にないなーと思って。
まずいな。
Play ScalaでWebアプリケーションを作る
標題の通り。
ブログっぽいものを作る予定。
「Play ScalaをRailsみたく触れるようにする」が目的。
ゆくゆくはGraphQLでAPI実装とかやる。
アプリのテーブル設計はこんな感じ。
超シンプル。(sqldesignerで書きました)
user: ログインユーザ
article: 記事
comment: 記事に対するコメント
記事はひとまず画像とか無しで、文字のみ。
画面設計は以下をイメージしている。
画面 | Controller | action |
---|---|---|
Home | Home | index |
ログイン画面 | User | login |
記事一覧 | Article | index |
記事詳細 | Article | show |
記事新規作成 | Article | new |
記事編集 | Article | edit |
記事一覧に「記事を新規作成する」ボタンがあって
記事詳細に「記事を削除する」ボタンがある。
記事詳細ではcommentも投稿できて、
投稿したらComment#create
してArticle#show
に戻る、かな。
Rails書いてていつも思うけど、User#login
てリソースルーティングに乗ってないけど
無理矢理にでも、たとえばUser#new
にlogin
機能を割り当てたりしたほうがいいのだろうか。
「名前は実体に即するように」 VS 「Railに乗る」
で、気になって調べたけど、Railsではこんなかんじに書いてある。
んー基本的には
「resourceに対する操作でリソースルーティングに含まれるactionに
適切なものがあるならばそれを選んで、ないならば別でactionを定義する」が正しいか。
じゃあ今回はUser
をindex
しないしshow
しないしnew
しないしcreate
しないし、、、で
User#login
が正しいことになるか。
何かご意見ありましたらください。
まま、とりあえず設計はおいといて手を動かすということで、
IntelliJ IDEA上のSBT Shell
上でrun
して
localhost:9000
で動かしてみました。
Initしたぞ。
以上。