みなさんは、新しいPHPプロジェクトのエンジニアリング・チームのリーダーです。要求事項は満足され、初期段階でのデータ・モデルは約150テーブルです。今度は、スケジュールを考える番です・・・。それぞれのデータベースのアクセス・クラスを書いて単体テストをするのに約1日と見積もると、1日1テーブルとして150テーブルでは150日になります。1ヶ月の稼働日を20日として、データベースのアクセス層を書くだけのために、約8ヶ月も必要なのでしょうか?
恐らく、そんなことはないはずです。ですからみなさんには、必要な時間を減らす方法が必要です。みなさんはパーシスタンス・フレームワークを使うことを考えるかも知れませんが、それを使って時間を半分にしたとしても、まだ4ヶ月が必要です。一般的なライブラリを書く方法もありますが、そうしたライブラリは使用するのが複雑であり、エラーを起こしやすく、またデバッグも困難です。
あるいは、1つか2つのサンプル・コードを書き、残りをコード・ジェネレータで構築する方法もあります。最悪の場合には、ジェネレータ自体を書くのに1ヶ月はかかるでしょう。しかしその後は、残りのコードの生成が数分でできるようになります。さらに、ジェネレータがSQLスキーマとデータベースのアクセス・コードの両方を生成するので、SQLスキーマがアクセス・コードと一致しない場合に発生するバグが、ほとんど無くなります。
そんな方法が本当に可能なのでしょうか?・・・それが、可能なのです。この記事では、どのようにそれを行うのか、そして、そうすることによってアプリケーションがいかに管理しやすく、堅牢に、そして楽しく書けるものになるかを説明します。2回シリーズの第1回の今回は、コード生成の基礎を解説し、また、対象となるモデルに対するSQLを生成するコード・ジェネレータ部分を構築する方法について順を追って説明します。第2回では、XSLをさらに深く掘り下げ、PHPを生成するコードを示しながら、ジェネレータをどのように完成させるかを解説して行きます。
本題に入る前に、データベースの抽象モデルを検討し、そしてSQLスキーマとPHPでデータベースにアクセスするクラスの両方を構築するジェネレータが必要でしょう。図1は、このモデルと、Java™コードなど他の技術向けにデータベースのアクセス・コードを生成する可能性を示しています。
図1. ジェネレータに関する基本的な情報の流れ
図1で、破線で示した矩形(SQLモデルとデータベース・アクセス・モデル)は、抽象モデルから作成された一時的なモデルを示します。点線で示された部分(Java)は、生成できる可能性のあるものを示します。
次の問題は、どの技術を使うべきかです。ジェネレータは、Javaコードを使って書くことも、PerlやPython、あるいはRubyを使って書くこともできます。課題が単純なことを考えると、VelocityやXSLTのようなテンプレート・エンジンで用が足りるかもしれません。XSLT 2.0のようなテンプレート・エンジンは、それ自体が多くの機能をもっており、Javaコードの中に埋め込んでその機能を拡張することもできることから、出発点としては好適です。
XSLT 2.0は、コード生成のためにテンプレート・エンジンを探し求めている開発者に対して、拡張言語機能を提供してくれます。この記事では、そうした機能に関して、抽象テーブルの定義を元に機能し、SQLとPHPの両方に対応する堅牢なジェネレータを構築するという観点から解説します。XSLT 1.xでは、単一レベルでの変換しか可能ではありませんでしたが、XSLT 2.0では、生成すべきコードの中間モデルを構築することができるのです。この機能のおかげで、ジェネレータを理解したり維持管理したりすることが容易になり、また他の言語をターゲットとしたジェネレータに転用することもできるようなります。
XMLの変換技術としてXSLTは、XMLをXMLあるいはテキストに変換することができます(このコード・ジェネレータ・プロジェクトでは、この両方の機能を使います)。XSLT 2.0は、XSLT 1.xよりも大幅に改善されていますが、この記事では再訂されたXSLT 2.0の持つ、3つの新機能を使います。
- 関数: 拡張関数を定義できるようになりました。これは、このために面倒なテンプレート構文を使っていた最初のバージョンのXSLTと比較すると、大きな改善です。
- 一時的ツリー: 最初のバージョンのXSLTでは、入力されたXMLツリーに対して操作をする必要がありました。XSLT 2.0では、他のテンプレートを操作するために使用可能な一時的ツリーを、メモリ上に構築できるようになりました。
- 結果文書: 一つのテンプレートから複数の出力ファイルを生成できるようになりました。SQLファイルや個々のPHPファイルの生成に、この機能を使っています。
では、ジェネレータをどのように構築するかを見てみましょう。
まず、ジェネレータへの入力から始めます。リスト1は、単純な書籍のデータベースに関するテーブル定義の例です。
リスト1. 入力用の抽象的なテーブル定義
<?xml version="1.0" encoding="UTF-8"?>
<tables>
<table name="Author">
<field name="first" type="text"/>
<field name="last" type="text"/>
</table>
<table name="Publisher">
<field name="name" type="text"/>
<field name="last" type="text"/>
</table>
<table name="Book">
<field name="name" type="text"/>
<field name="author" type="id"/>
<field name="publisher" type="id"/>
</table>
</tables>
|
この単純なXMLスクリプトは、Author(著者)、Publisher(出版社)、Book(書籍)という、3つのデータベース・テーブルと、そのフィールドを定義しています。本格的なPHPアプリケーションへの入力は、これよりもずっと大きく複雑でしょうが、このコードで、最初としては充分です。
このXMLを、単一パスでSQLまたはPHPに変換するジェネレータを構築することもできますが、私としてはお勧めしません。テンプレートが複雑になり、維持管理も困難となるからです。コード生成に関する今日のベスト・プラクティスは、複数レベルのモデルを持ち、レベルに応じて次第に厳密に対象を絞るようにすることです。そうすることによって、一つ一つの変換が複雑になりすぎるのを防ぐことができます。これはWebサーバで、複数の階層を使うことによって全体的な複雑さを減少させ、再利用を促進するのと似ています。
再度図1を見ると、抽象モデルがSQL向けに一つのモデルを生成し、さらに、データベース・アクセス・モデルを通してPHP向けに、もう一つのモデルを生成していることが分かります。こうしたモデルは、一時的ツリーに保存され、そしてコードを生成するために使われます。図2では、ジェネレータにおける変換を通じて、Authorテーブルを追跡しています。
図2. 一つのSQLテーブルの進展
図2の一番上にある矩形は、元の抽象的なテーブル・モデルを示しています。中央の矩形は、その抽象的なモデルから構築されたSQLモデルです。一番下の矩形は、結果として作成されるSQL出力を示しています。見て分かる通り、SQLモデルには、最後のSQLファイルの中に何があるべきか、全ての要点を明示しています。コード・テンプレートの役割は、単純にXMLをSQLへと変換することです。それによって、一つのモデルから様々なデータベース用のコードが生成できるようになるのです。では、XSLを見てみましょう。
リスト2は、このジェネレータ・プロジェクト用の主要なXMLスタイルシートの、先頭部分を示しています。
リスト2. ジェネレータ用の主要XMLスタイルシートの先頭部分
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gen="http://www.codegeneration.net/" version="2.0">
<!-- Output specifications -->
<xsl:output method="text"/>
<xsl:output method="xml" name="debug-xml" indent="yes"/>
<xsl:output method="text" name="sql" indent="yes"/>
<!-- SQL Templates -->
<xsl:include href="gen2-sql.xsl" />
<xsl:include href="gen2-sql-model.xsl" />
<xsl:include href="gen2-queries.xsl" />
<!-- Database Access Templates -->
<xsl:include href="gen2-dba.xsl" />
<xsl:include href="gen2-php.xsl" />
<!-- The generator main entry point -->
<xsl:template match="/">
<!-- Create the SQL model -->
<xsl:text>Building SQL model</xsl:text>
<xsl:variable name="sql-model">
<xsl:call-template name="gen-sql-model">
<xsl:with-param name="model" select="."/>
</xsl:call-template>
</xsl:variable>
<!-- Dump it out for debugging -->
<xsl:text>Dumping SQL model</xsl:text>
<xsl:result-document href="db/gen-tables.xml" format="debug-xml">
<xsl:copy-of select="$sql-model"/>
</xsl:result-document>
<!-- Generate the SQL from the SQL model -->
<xsl:text>Generating SQL</xsl:text>
<xsl:result-document href="db/gen-tables.sql" format="sql" >
<xsl:apply-templates mode="sql" select="$sql-model/sql" />
</xsl:result-document>
|
このスクリプトで重要な部分は、SQLモデルを保持するsql-model変数を作成する部分と、sql-model変数の内容を使ってSQLファイルを生成するresult-documentタグを作成するところです。こうしたタグは、XSLT 2.0に貴重な2つの機能が追加されたことを示しています。つまり、xsl:variableタグを使って一時的ツリーを作成する機能と、xsl:result-documentタグを使って複数の出力ファイルを作成する機能です。
では、抽象テーブル・モデルからSQLモデルを生成するテンプレートをお見せしましょう。リスト3は、gen-sql-modelテンプレートを示しています。
リスト3. SQLモデル・ジェネレータ
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gen="http://www.codegeneration.net/" version="2.0">
<!-- Builds the SQL model from the original data model -->
<xsl:template name="gen-sql-model">
<xsl:param name="model"/>
<sql>
<xsl:for-each select="$model/tables/table">
<create name="{lower-case(@name)}"
primary-key="{concat(lower-case(@name),'_id')}">
<field name="{concat(lower-case(@name),'_id')}"
type="{gen:model-type-to-sql('integer')}"/>
<xsl:for-each select="field">
<field name="{lower-case(@name)}"
type="{gen:model-type-to-sql(@type)}"/>
</xsl:for-each>
</create>
</xsl:for-each>
</sql>
</xsl:template>
</xsl:stylesheet>
|
このテンプレートは、ごく単純です。元のモデルから、SQLモデル用の新しいタグを幾つか作ります。gen:model-type-to-sql関数への呼出しに注意してください。この呼出しは標準的なXSLT関数の一部ではなく、私がリスト4で定義している、拡張関数です。
リスト4. モデルからSQL型への変換関数
<xsl:function name="gen:model-type-to-sql">
<xsl:param name="type"/>
<xsl:choose>
<xsl:when test="$type eq 'text'">TEXT NOT NULL</xsl:when>
<xsl:when test="$type eq 'id'">INTEGER NOT NULL</xsl:when>
<xsl:when test="$type eq 'integer'">INTEGER NOT NULL</xsl:when>
</xsl:choose>
</xsl:function>
|
この、新しいXPath関数を作ることができる、という機能は、XSLT 2.0で新規に追加されたものです。以前は、xsl:call-template構文を使う必要がありましたが、これはパラメータが増えると扱いが面倒になりがちです。また、テンプレートが関数として機能するため、テンプレートの名前空間を混乱させることにもなります。
SQLを生成するための最後のステップは、SQLモデルをSQLとして書式化することです。これは、リスト5に示す2つのテンプレートを使って行います。
リスト5. SQL生成テンプレート
<!-- Template for SQL create tags -->
<xsl:template match="create" mode="sql">
DROP TABLE IF EXISTS <xsl:value-of select="@name" />;
CREATE <xsl:value-of select="@name" /> (
<xsl:apply-templates mode="sql" select="field" />
PRIMARY KEY ( <xsl:value-of select="@primary-key" /> )
);
</xsl:template>
<!-- Template for SQL field tags -->
<xsl:template match="field" mode="sql">
<xsl:value-of select="concat(@name,' ',@type,',')" /><xsl:text>
</xsl:text>
</xsl:template>
|
本当にこれがすべてです。SQLモデルのcreateタグに応答する一つのテンプレートがあり、このテンプレートが今度はapply-templatesタグを使って、その子fieldタグを処理します。fieldタグは、2番目のテンプレートによって書式化されます。
もう一度図2を見てください。XMLの構造とSQLの構造が、いかに似ているかに注意してください。createタグは、幾つかのfieldタグを含んでいます。SQLの場合と同様、createコマンドは幾つかのフィールド・パラメータを含んでいます。コードの構成と同じようにモデルを構成することによって、コード・テンプレートを非常に単純化することができます。
この記事では、XSLT 2.0で導入された新しい機能(特に拡張された言語機能と、生成するコードの中間モデルを構築できる機能)を紹介しました。そして、これらを使うことによって、抽象的なテーブル定義からSQLを生成する堅牢なジェネレータを構築できることを説明しました。
第2回では、Webサーバがデータベースにアクセスできるようにするための、PHP部分のコードの生成方法について説明します。また、さらに幾つかのXSLT 2.0の新機能も紹介します。
- このシリーズ2回目の記事、「 Code generation in XSLT 2.0, Part 2: Generate PHP with XSLT 2.0」(developerWorks, 2005年2月)も読んでください。
- コード生成に関する情報源であるCode Generation Network をよく見てください。様々な記事やインタビュー、書評、コード生成に関する議論のための電子メールのリストなどがあります。
- Jack D. Herrington著による、Code Generation in Action を読んでください。データベースのアクセスに限らず、多種多様な目的のコード生成について網羅されています。
-
Saxon をダウンロードしてください。SaxonはXSLT 2.0をサポートする素晴らしいXSLTプロセッサであり、この記事でも使われています。
- この話題に関して将来有望なGenerative Programming について調べて見てください。この分野には、他の自動プログラミング技法に加えて、コード生成も含まれています。
- XSLT 2.0やXPath 2.0についてさらに知りたい方は、Michael Kay著によるXSLT 2.0 Programmer's Reference がこの新しい標準(XSLT 2.0)のバイブルです。またXPath 2.0に関しては、Michael Kay著による姉妹編として、XPath 2.0 Programmer's Reference があります。
- developerWorksのXMLゾーン には、XMLに関する資料が豊富に取り揃えられています。
- XMLおよび関連技術においてIBM認証開発者になる方法についてはこちら を参照してください。