1周遅れのscala入学 #nds41
TRANSCRIPT
![Page 1: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/1.jpg)
第41回長岡IT技術者勉強会
1週周遅れのScala入学
2015/4/11 @nemuzuka
![Page 2: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/2.jpg)
自己紹介• 片桐 一宗(かたぎり かずむね)
• id:nemuzuka
• @nemuzuka
• サーバサイドにJavaを使用したWebアプリケーションの開発を主にしております
• フリーランス(vss.jp.net)
![Page 3: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/3.jpg)
Scala再入学のきっかけ
![Page 4: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/4.jpg)
システムを新しく構築するチャンス• AWSのサービス使用
• S3 / CloudSearch / CloudFront…
• 全ての操作で画面切り替えとかちょっと…
• AjaxでDOM書き換え
• HTML5
• IE?最新しかサポートしない!
• スケジュールがタイト
• 要望がコロコロ変わりそう…
![Page 5: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/5.jpg)
そこで選んだのが
• サーバサイドはJava
• Tomcat
• Seasar2
• SAStruts
• S2Dao
+今まで培ってきた ・diconファイル ・薄いラッパー
![Page 6: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/6.jpg)
ちょっと 開発のパワーが足りないから 外部の人の協力を得よう
![Page 7: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/7.jpg)
『新規なのにJavaなんですか』
『新規なのにSeasar2 なんですか( ´_ゝ`)』
えもいわれぬ老害感
![Page 8: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/8.jpg)
結局Javaの構成で 無事カットオーバー できましたが...
何か新しいこと やらないといけないかな
![Page 9: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/9.jpg)
選択肢は2つ• 別のフレームワークを学ぶ
• JavaEE
• Spring
• Play Framework
• 新しい言語を学ぶ
• 静的型付けが良い
• IDEが使えた方が良い
![Page 10: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/10.jpg)
あっ!
![Page 11: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/11.jpg)
入学してた。
![Page 12: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/12.jpg)
当時は「できない子」
• bot写経で躓き、「ガッ」できなかった
• みんなの「XXXのコード見せてください」の画面切り替えが早すぎて追いつけなかった
• 単純に手が遅い
![Page 13: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/13.jpg)
再入学の後押し• Javaの資産が使える
• Apache Commonsとか
• AWSのSDKだって使える
• 関数型プログラミングもオブジェクト指向プログラミングもサポート
• とりあえず「使う」だけなら関数脳にならなくても良い
• 型推論でスクリプト言語のようにも見える
• でも、IDEで追いやすい
![Page 14: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/14.jpg)
「Javaやってます」 よりも
「Scalaやってます」 の方がなんとなく仕事が来そう
![Page 15: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/15.jpg)
というわけで Scala再入学することに
![Page 16: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/16.jpg)
どうやって再入学したか?
• 今更、NullPoGaBot作ってもなー
• どうせやるなら何か案件が始まった時に使えるものにしたい
![Page 17: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/17.jpg)
S2でやってたことを Scalaでやるならどうするか に置き換えてみよう
![Page 18: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/18.jpg)
フレームワークの置き換え
機能 JAVA SCALA
WEBフレームワーク SAStrutsScalatra
+scalata-forms
テンプレートエンジン JSP Scalate(SSP)
ORM S2Dao Slick
![Page 19: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/19.jpg)
こんなときどうする(1)
~トランザクション管理~
![Page 20: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/20.jpg)
トランザクション• DBの変更を
• 適用する(commit)
• 取り消す(rollback)
• これをそれぞれのプログラムで行うと
• DBに対する操作とビジネスロジックが混在する
• ソースコードの見通しも良くない
• コネクションの解放漏れ等にも繋がる
![Page 21: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/21.jpg)
S2だと DI• S2の仕組みに乗れば自動的にトランザクションが効く
• @Bindingを付与して実装クラスをInjection
• 正常終了(何も例外が発生しない)時commit
• 例外が発生すればrollback
• DI使うのに外部XMLは不要
• コネクションプールもdiconに書くだけで使える
![Page 22: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/22.jpg)
Scala(Scalatra)だと filter• Scalatraのinitでコネクションプール設定
• filterでトランザクション制御
• トランザクション開始
• 処理呼び出し
• 正常終了(何も例外が発生しない)時commit
• 例外が発生すればrollback
![Page 23: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/23.jpg)
ソースはこんな感じ
//Filter class class TransactionFilter(db: Database) extends Filter {
def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
db withDynTransaction { // 1. トランザクションを開始し、ThreadLocalにSessionを格納 chain.doFilter(req, res) // 2. Servletの処理を行う } // 3. 正常時commit、例外発生時rollback } }
DBDatabase.dynamicSession
をimportThreadLocal取得して処理を行うことが可能
class ScalatraBootstrap extends LifeCycle {
val cpds = new ComboPooledDataSource() // 1.設定ファイルを元にコネクションプールを生成
override def init(context: ServletContext) { // 2.Scalatra起動時に一回だけ呼ばれるメソッド
val db = Database.forDataSource(cpds) // 3. Slick用データソースの取得 context.addFilter("transactionFilter", new TransactionFilter(db)) // 4. TransactionFilterをfilterとして登録 context.getFilterRegistration(“transactionFilter").addMappingForUrlPatterns(
util.EnumSet.allOf(classOf[DispatcherType]), true, “/*")
// 5. Servletの定義が続く… } }
![Page 24: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/24.jpg)
これでこんなLayer設計ができます
・Servlet ・Service →DBアクセスしないので、Sessionをコード上で意識しない
・Dao →ThreadLocalからSessionを取得してSQL発行
![Page 25: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/25.jpg)
こんなときどうする(2)
~認証・認可チェック~
![Page 26: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/26.jpg)
認証・認可• 認証
• リクエストを送ってきた人が正規のユーザであることを確認すること
• ID / パスワードでログインしている
• 本人しかしらない筈なので、正規のユーザとみなす
• 認可
• その機能を利用する権限の有無を確認すること
• 管理者権限を所有していないのに、管理者機能を使用できるのはNG
• Webアプリの場合、URLを直接叩かれる可能性があるので、特に注意する必要あり
![Page 27: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/27.jpg)
S2だと S2AOP• ログイン成功時、HttpSessionにログイン情報を設定
• 受けたリクエストが認証済みでなければならない場合
• Interceptorを使用して、Actionの呼び出し前にログイン情報がHttpSessionに存在するかチェック
• 存在しなければログイン画面へリダイレクト
• 存在するが、そのActionを使用する権限をユーザが持っていない場合、不正アクセスが来たとみなし、しかるべき画面にリダイレクト
• AOPの定義はdiconファイルに定義
• パッケージ、クラス名等の正規表現で定義できる
![Page 28: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/28.jpg)
Scala(Scalatra)だと filter• ログイン成功時、HttpSessionにログイン情報を設定
• リクエストURIを元に受けたリクエストが認証済みで無ければならない場合
• filterでログイン情報がHttpSessionに存在するかチェック
• 存在しなければログイン画面へリダイレクト
• 存在するが、そのServletを使用する権限をユーザが持っていない場合、不正アクセスが来たとみなし、しかるべき画面にリダイレクト(これは、Servlet側機能)
![Page 29: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/29.jpg)
こんなときどうする(3)
~コード自動生成~
![Page 30: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/30.jpg)
ER図を常に信じられる状態にしたい
3.ソースコード (絶対に手動で修正しない)
2.RDBMS
1.ER図
![Page 31: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/31.jpg)
• S2Dao-CodeGen
• Slick code generator
S2だと
Scala(Slick)だと
![Page 32: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/32.jpg)
こんなときどうする(4)
~動的SQL発行~
![Page 33: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/33.jpg)
画面に入力された項目だけwhere句に追加
未入力の場合、その項目はwhere句に含めない
![Page 34: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/34.jpg)
S2だと IFコメント• 条件に応じてSQLを変更することが可能
• /* IF 条件 */…/*END*/
• 条件がtrueの場合、/*IF*/と/*END*/に囲まれた部分が評価される
/*IF hoge != null*/hoge = /*hoge*/‘abc’/*END*/
引数hogeがnullでない場合にのみ、 hoge = hogeの値 がSQL文に追加される
![Page 35: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/35.jpg)
Scala(Slick)だと 生Preparedstatement
• ゴニョゴニョ頑張れば他のやり方でできる気もするけど、こっちの方が確実だと思う
• where句のカラムが動的に変わらないのであれば、StaticQueryを使用した方が良い
• 複雑なテーブル結合する場合はStaticQueryやPreparedstatement使ったほうが余計なことにハマらなくて良いかも
• S2Dao使ってる時でも生SQLを発行してた
• ORMは楽になるところだけ使う
def coffeeByName(name: String) = sql"select * from coffees where name = $name".as[Coffee] println("Coffee Colombian: " + coffeeByName("Colombian").firstOption)
![Page 36: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/36.jpg)
まだいろいろあるけど、 ドキュメント読めば何とかなります
![Page 37: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/37.jpg)
Java使いがScalaと戯れた感想
![Page 38: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/38.jpg)
型推論
• 変数名定義の時にクラス名の有無でこんなに違うかーってくらいスッキリします
[Scala] val credentials = new BasicAWSCredentials(accessKey, secretKey) val s3client = new AmazonS3Client(credentials) val localFile = new File("ローカルファイルパス") val bucketName = "バケット名" val filePath = "S3のアップロード先のパス" val upReq = new PutObjectRequest(bucketName, filePath, localFile) s3client.putObject(upReq)
[Java] AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); AmazonS3 s3client = new AmazonS3Client(credentials); File localFile = new File("ローカルファイルパス"); String bucketName = "バケット名"; String filePath = "S3のアップロード先のパス"; PutObjectRequest upReq = new PutObjectRequest(bucketName, filePath, localFile); s3client.putObject(upReq);
![Page 39: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/39.jpg)
名前付き引数• 大好きです。Javaにもあれば良いのに
• 使う側が意識して設定するようになるのがイイと思います
• 引数の順番が変わった時にも追従してくれます
• 特にSlickのデータモデル(case class)のインスタンスを生成するときは嬉しい
• カラムの追加や順番を変更することが多いので
[Scala]
case class User(age:Int, name:String) ・ ・ ・
val hanako = User(7, “はなこ”) // ① val jiro = User(age = 38, name = “二郎”) // ② val taro = User(name = “太郎”, age = 17) // ③
→全てUserクラスのインスタンスが生成可能だが、 case class User(name:String, age:Int)
と変更した場合、①はコンパイルエラーになる
![Page 40: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/40.jpg)
index付きループ• Javaにもあれば良いのに
• Javaだと拡張forを諦めてfor(int i = 0; i < list.size(); i++)
[Scala]
val list = List("A", "B", "C") for((e, index) <- list.zipWithIndex) { // eには該当要素、indexには該当indexが格納される
・ ・ ・
}
![Page 41: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/41.jpg)
まだまだあるよ• if とか for は式なので値を返せる
• 「このifで何をしたいんだっけ?」が見えるようになるのがイイ!
• 複数の戻り値を返せる(タプル)
• わざわざ戻り値用のclass作らなくてもいいんです
• traitでmix-inがすごい
• チェック例外がない
• 個人的にはあってもいいと思うけど…(設計思想変える必要あり)
• Either(さっきのヤツ!)
• 比較は「==」でOK
• equalsを使わなくて怒られる新人減ります
• breakが変
• もなど / かりー → よくわかりません
![Page 42: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/42.jpg)
再入門に準備したもの
![Page 44: 1周遅れのScala入学 #nds41](https://reader031.vdocuments.pub/reader031/viewer/2022020101/55b157d3bb61eb72028b46a0/html5/thumbnails/44.jpg)
まとめ
• 結構面白いです、Scala
• 潤沢なメモリ+SSD必須
• 金食い虫なので仕事に使うなら上の理解が必要かも
• 次はテスト周りをしっかりと