2006-11-23


近況

Java でアプリケーションを作っている. 足回りが動いたのでアプリっぽくしようとデータベースをつなぎだしたが ... かったるい. RDB の出てこないコードを書いていた昨日までは快調だったのに, 今日 SQL がでてきた途端に意欲がガタ落ち. このかったるさの原因はなんだろう. 思いあたる節はある. まず RDB はなんとなく仕事っぽい. 趣味らしさを損なう. あと, ここしばらく ORM considered harmful 仮説に与して Hibernate なんかを使っていない. (かわりに Spring JDBC Template を利用.) その結果お約束コピペコードが増えて萎える. アンチ ORM 派の主な主張は, 実世界のデータモデルが持つ複雑さを ORM では吸収しきれない (結局普通に RDB を使うのと大差がなくなる) というものだ. でも私が作っているものはそこらのアプリよりだいぶ単純なデータモデルしか持っていない. だから ORM を使ってもよかった気がする. 失敗した...

ただ ORM にも別のかったるさがあるのは確かだ. やっぱリデータベースは面倒なのかとグズグズ Web をぶらついていたら, O'Reilly の "LINQ: The Future of Data Access in C# 3.0" なる PDF 本を発見. そうだ, ぼくらには LINQ があるじゃないか.

ということで現実逃避に読んでみた. 60 ページくらいに 短く LINQ の概要がまとめられている. 入門にいい. XLINQ, DLINQ といった個別のサブシステムについては深くふれず, もっぱら共通部分を説明している. (XLINQ 編と DLINQ 編は近日発売予定とのこと.) DLINQ の扱いが軽いのはやや悲しいけれど. サンプルコードが多いのには助かる.

ひとくちに LINQ といっても 素 LINQ, XLINQ, DLINQ ではそれぞれ実現の仕組みが異る. DLINQ は LINQ の問合せ式を SQL に変換して RDB に投げる. たとえばこんなコードが...:

// PDF より抜粋
Databases.SchedulingDocs db = new Databases.SchedulingDocs(connectionInfo);

var oct2006 = ( // find all doctors scheduled for October 2006:
    from d in db.Doctors
    join c in db.Calls
    on d.Initials equals c.Initials
    where c.DateOfCall >= new DateTime(2006, 10, 1) &&
          c.DateOfCall <= new DateTime(2006, 10, 31)
    orderby d.Initials
    select d.Initials
   )
   .Distinct();

こんな SQL を発行する:

SELECT DISTINCT [t0].[Initials]
  FROM [Doctors] AS [t0], [Calls] AS [t1]
  WHERE ([t1].[DateOfCall] >= @p0) AND
        ([t1].[DateOfCall] <= @p1) AND
        ([t0].[Initials] = [t1].[Initials])
  ORDER BY [t0].[Initials]

私の妄想だと DLINQ は SQLServer と CLR が癒着して魔法のように動くことになっていた. 現実は案外地味なんだね...

そもそも RDB を使うコードは何がかったるいんだろう. SQL を書くのはそんなに面倒でもない. なんというか, 割にあう感じがする. もっと面倒なのは戻り値オブジェクトのクラスを定義するところと, そのオブジェクトに値を詰めるところだ. こっちはルーチンワークの徒労感がある. ORM は値を詰めてくれるけれど, すくなくともクラスを定義する必要はある.

DLINQ ではどうするかというと, SQLMetal というツールを使ってデータベースのスキーマからクラスを生成するらしい.

C:\...\Bin>sqlmetal /server:. /database:SchedulingDocs \
                    /language:csharp /code:SchedulingDocs.cs \
                    /namespace:Databases

私の妄想だと DLINQ は前代未聞の型推論とメタプログラミングが適当な無名クラスを生成し うまい具合に動くことになっていた. 現実は案外地味なんだね... まあそこそこ DRY だし, 妥当なところなのかもしれない.

Java で LINQ 風味 (は厳しそう)

結局 LINQ の新に新しいところは

    from d in db.Doctors
    join c in db.Calls
    on d.Initials equals c.Initials
    where c.DateOfCall >= new DateTime(2006, 10, 1) &&
          c.DateOfCall <= new DateTime(2006, 10, 31)
    orderby d.Initials
    select d.Initials

みたいなコードが C#/VB として書ける点に尽きる気がしてきた. 文法の一部なら IDE の支援もあるだろうしね.

このコードにあらわれる字句のうち, join, where, orderby, select あたりは LINQ 用組込みクラスのメソッド呼び出しにマップされ, ユーザがフックすることもできるらしい. 要するに構文糖なわけだが, こればかりは syntax matters だ. なにしろベタな Java に直すとこんな感じになってしまう.

ds.from().join(Call.class)
  .on(new OnCond() {
        public boolean pred2<Doctor, Call>(Doctor c, Call d) {
          return c.getInitials().equals(c.getInitials()); } })
  .where(new WhereCond() {
           public boolean pred<Call>(Call c) {
             return c.getDateOfCall().after(new DateTime(2006, 10, 1)) &&
                    c.getDateOfCall().before(new DateTime(2006, 10, 31)); } })
  .orderBy(new OrderByCriteria<Doctor, Date> {
             public boolean Date proj(Doctor d) { return d.getInitial(); } })
  .select();

LINQ の短さまでにはだいぶ距離がある. Java もそのうち closure が使えるようになれば, もう少し簡潔にはなるだろう. また EasyMockJBehave は static import や generics, メソッド連鎖を駆使して DSL チックなコードを実現している.

  // easymock
  expect(mock.voteForRemoval("Document"))
      .andReturn((byte) 42).times(3)
      .andThrow(new RuntimeException(), 4)
      .andReturn((byte) -42);
  // jbehave
  exchangeRateServiceMock.expects("retrieveRate").
      with(eq(Currency.USD)).
      will(returnValue(new ExchangeRate(1.85, 0.54)));

この路線を突き詰めれば少しは LINQ に近づけるかもしれない.

Ruby で LINQ 風味 (はなぜか空しい)

そういえば Ruby ならブロック構文がある. 頑張れば LINQ ごっこができそうだ.

  ds.from(db.Doctors)
    .join(db.Calls)
    .on{ |d, c| d.Initials == c.Initials }
    .where { |c| c.DateOfCall >= DateTime.new(2006, 10, 1) &&
                 c.DateOfCall <= DateTime.new(2006, 10, 31) }
    .orderby { |d| d.Initials }
    .select { |d| d.Initials }

お. それっぽい!

ふとぐぐってみたところ, A Ruby DSL for generating SQL という記事があった. この人は実際に Ruby で SQL を吐く DSL を作り, ActiveRecord と併用しているらしい. でもいざ実物をみると, RJS に通じる空しさを感じてしまうなあ. なんというか, やっぱり素の SQL でいいですという気分になってくる. IDE の支援がないなら大差なさそうだし... SQL が嫌いで ruby が好きな人にはいいかもしれない. 私は ActiveRecord くらいでいいや.

こうして道の険しさを知り, 現実逃避はおしまい. だるい気分は曇り空のせいかもしれず, いましばらくは Spring JDBC でがんばる気になりました.

リンク