行く記憶の流れは絶えずして

戻ってこないのでメモするところ

Liquibase、H2databaseでdropAll()できない

 

表題ままです。1週間悩んでました。

 

流れとして、

  • テストコード書きたい

  • DBにテストデータ入れたい

  • DropwizardのテストからLiquibaseを知る

  • テストを独立させるため@Beforeでテーブル作成、@Afterで全て消したい

  • 消えない(エラー終了)

最初はH2databaseとLiquibaseの相性とかいろいろ考えてました。

foreign key,view,tableを消す処理を自前で作ることも辞さない勢いでした。

 

が、落ち着いてログを見るとスキーマ名とテーブル名がダブルクオートで囲われている。

 

まさかと思ってソースを見ると、わざとそういった処理をしていました。

そのためそこだけオーバーライドしてよけたらうまくいっちゃった。

自己満足のためにgistで公開してみました。

Solve my way of exception on liquibase.dropAll(),using H2database(MODE=MYSQL)

Liquibase-3.1.1、 h2database-1.4で確認しています。

 

プロジェクトのソースに横槍を入れるなどおごがましいと考えているので、

絶対に他にやり方があると思います。

 

1週間の戦いに勝ったので、今日はマッカランで1杯やって寝ます。

DropwizardのテストからLiquibaseを知る

 

DropwizardのDAOテスト用DBをどうしようかと悩んでいたらこれを知りました。

最初はmockitoを使ってやりきろうと思ったけど、SQLのクエリも見たいのと

ログイン周りでSessionを使っているのでこれを選択。

 

DBをバージョン管理できるすごいやつ。

 

今回はMySQLの既存のスキーマxmlにダンプして、h2 dabatase(in memory)に

再構築してテスト用DBを作ろうと頑張ってみました。

Dropwizardに食わせるDB設定は本番環境とテスト環境で設定ymlを分ければいいので楽です。

 

できたこと:

  1. xmlのテンプレートを作っておく(Step 1参照)

    Liquibase | Database Refactoring | Liquibase Quickstart<

    必要ないかもしれません

  1. 設定項目をliquibase.propertiesに記載

    Liquibase使い方(基本)メモ - Qiita
  2. ダンプ

    • java -jar [liquibase.jarのpath] --defaultsFile=[上記で作ったliquibase.properties] generateChangeLog
    • liquibase.jarとDBドライバはDropwizardのプロジェクトで使用(依存)しているものにリンクを張るとバージョンで悩まない
    • 上記だとログ作成時にymlパーサーが見つからないとINFOが出たが今回は無視
    • クラスパスをきっちり指定している人は大丈夫だと思う
  3. h2 databaseへの再構築(一部)、Dropwizardのテストでの使用

    上記で作成したxmlをdropwizard-migrationで使用

    Java - dropwizard-testingが便利でした - Qiita

できなかったこと:

  • ダンプしたxmlからの完全な再構築

    • VIEWのクエリでDATE_FORMAT()関数を使用していた部分、スキーマ指定していた部分
    • DATE_FORMAT()使用箇所はテストでクリティカルではないので削除、
    • スキーマ指定もここでは単一のスキーマなので削除
  • スキーマの指定(上記でも触れていますが、やりたいことができたので後回し)

 

目的は達成できたので満足です。

いずれバージョン管理にも手を出したいところ。

Dropwizardをデプロイ後、8時間後にDB接続エラー

デプロイしていよいよ起動、明くる日に様子を見るとページからログインできない。
ログインボタンを押してもレスポンスがない。
応答を見ると[500 Internal Server Error]。
バグ?ログを見てみると以下。

f:id:taketsuru:20150503013213p:plain


とか出ている。autoReconnect=trueにすると解決すると誘われている。

デフォルトでは最後の接続から8時間経つと切断されるらしい。

調べるとmysqlのpathの最後に?autoreconnect=trueで解決するとか。

しかし非推奨で、コネクションプーリングしなさいとのこと。
dropwizard特有のやり方とかあるんじゃないのとさらに調べる。

見つかる。例によって外人。

java - Broken Pipe exception in Dropwizard application - Stack Overflow

 

  1. 設定+αで解決した報告

    • checkConnectionWhileIdle: true
    • checkConnectionOnReturn: true
    • checkConnectionOnBorrow: true


    とソースのトランザクション境界で直ったらしい。(関係ない気がするけど)
    自分のやつはユーザー認証と掲示板読み書き程度、それぞれでAbstractDAOの継承クラスによるアクセスと@UnitOfWorkをつけているのでいいはず。

  2. コネクションプーリングしなさい

    1の投稿のすぐ下にある。Manageを使おうとのこと。
    ManagedPooledDataSource.javaとかいうド直球なソースが紹介されているけど、
    これとdropwizard-hibernateを絡ませなきゃいけない。すぐには手が出せそうにないので後回し。

今回は1の方法で、最後の接続から8時間後に接続して解決したのでひとまず良しとします。

puttyとsshの違い

VPSにはずーっとputtyでアクセスしてきましたが、

勉強も兼ねてssh(linuxクライアント)でもアクセスしてみました。

 

主な違いは鍵。

それぞれ特有の鍵をそれぞれで生成します。

サーバーに公開鍵、クライアントが秘密鍵を使用するのは基本。

linuxクライアントでは秘密鍵パーミッションを400にしないと怒られる。

 

  1. 鍵の生成、それぞれパスワードを指定

  2. サーバーに登録

    • どちらも拡張子pubのファイルを使用
    • cat ***.pub >> .ssh/authorized_keys
  3. アクセス

sshはコマンドなので一例を。

オプションをファイルに書いておく方法もあるらしいけど今回はパス。

  • ssh user@url -p 12345 -i ***.ppk

    • 普通のログイン用
    • urlのサーバーにuserでログイン
    • サーバーのsshポートは12345
    • 秘密鍵として***.ppkを使用
  • ssh user@url -p 12345 -N -f -L 23456:localhost:34567

    • トンネル用
    • urlのサーバーにuserでログイン
    • サーバーのsshポートは12345
    • ログイン後バックグラウンドへ(-f)
    • ログイン後何もしない(-N)
    • クライアントのポート23456にアクセスすると
      サーバーから見えるlocalhostのポート34567に転送する

一番下が一番ややこしい。

Dropwizardのassetsに/をmappingしたい

Dropwizardにおいて、静的なファイル(html, js, css…)はデフォルトでは

  • http://{ドメイン}/assets/* → "src/main/resources/assets/*"

となっています。

 

ですが、トップページなどはURL直接指定でアクセスできると格好良かったりします。

先記のデフォルトは一応AssetBundleを使用することで変更できますが

ルートにはできない仕様らしいです。

Asset bundles not able to be served from root path. · Issue #661 · dropwizard/dropwizard · GitHub

 

Google先生に聞くと外人がたくさん困ってましたが、

ログを一生懸命漁っていると解決した声を見つけました。

上記リンクの下のほうにあり、まとめると

  1. Application#initialize()において

    bootstrap.addBundle(new AssetsBundle("/assets/", "/"));
  2. Application#run()において

    environment.jersey().setUrlPattern("/services/*");

1つめでやりたいことやって(従来はだめでしたが)、

2つめでjerseyのマッピングをしています(ここでは"/services/"以下)。

Dropwizardのjerseyはルートがデフォルトなので、

2つめがないと静的ファイルをjerseyで解決しようとして404になります。

( 2つ目の項目ですが、公式ドキュメントにあるように、

設定ファイルにapplicationContextPathを設定してもうまく動作しませんでした。)

 

日本語が少なくて大変…

DropwizardにおけるHibernateでのテーブル名定義箇所

今回一番引っかかったところ。

 

Dropwizardに組み込まれているHibernateのテンプレートクラスは、

  1. 設定ファイルに接続情報を記載
  2. Pojoの定義
  3. AbstractDAOの継承
  4. HQLを@NamedQueryで宣言、呼び出し
  5. サービス登録

って感じでマッピングxmlをいちいち作成しなくて便利。

だが、テーブル名の定義が@Tableと@NamedQuery内の2箇所であり、

どっちを使ってるのかいまいち。

 

そこで実験してみました。

というより今回引っかかったのでついでにいろいろ試しただけです。

 

大文字小文字の区別をこんな感じでしてみました。

Dropwizardのバージョンは0.7.0です。

NoSQLテーブル名POJOクラス名@Table@NamedQuery結果
1 BBSDATA BBSData BBSData BBSData クエリ呼び出し時にNG
MySQLSyntaxErrorException: Table 'Schema.BBSData' doesn't exist
2 BBSDATA BBSData BBSData BBSDATA Dropwizard起動時にNG
QuerySyntaxException: BBSDATA is not mapped
3 BBSDATA BBSData BBSDATA BBSData OK
4 BBSDATA BBSData BBSDATA BBSDATA Dropwizard起動時にNG
QuerySyntaxException: BBSDATA is not mapped
5 BBSDATA BBSDATA BBSData BBSData Dropwizard起動時にNG
QuerySyntaxException: BBSData is not mapped
6 BBSDATA BBSDATA BBSData BBSDATA クエリ呼び出し時にNG
MySQLSyntaxErrorException: Table 'Schema.BBSData' doesn't exist
7 BBSDATA BBSDATA BBSDATA BBSData Dropwizard起動時にNG
QuerySyntaxException: BBSData is not mapped
8 BBSDATA BBSDATA BBSDATA BBSDATA OK

 

意外だったのがPOJOのクラス名をみている?こと。

 

考察してみる。

  • No1とNo3より、SQL問い合わせは@Tableを使用してるっぽい。
  • No2、4、5、7より、"not mapped"と叱られているのは@NamedQuery。
  • また、No2、4、5、7はPOJOクラス名≠@NamedQuery。
  • 成功しているNo3、No8POJOクラス名=@NamedQuery。

 

これらより、

@NamedQueryに出てくるPOJOクラス名を@Tableで置換しているのか?

と推測。

 

 

Dropwizard勉強中のTips②

考えてみれば当たり前なんだけど、Dropwizardはjerseyを使ってるから

調べるときもjersey(JAX-RS)で検索したほうがかかりやすい、と。

 

ただ、今使ってるのDropwizardは0.7。

0.8がすでに存在し、jerseyのバージョンが上がって結構変わるらしい。

HK2とか言う記述をチラッと見たけど、

とりあえず0.7で一通り完成させてから対応させるつもり。

 

  1. @Entityのあるpojoはスコープのどれかに@Idが必要.

    tableではなくviewにマッピングすると忘れがち。

    たまたまUniqueな値がいたからそれにしたけど、違うと何かあるのかな。

  2. @ContextでHttpSessionを扱いたい場合はHttpSession,SessionHandlerの登録が必要。