Javaのリバースエンジニアリング
ここではSQL Anywhere 12リリース候補版のサーバー上で作動している「デモ」データベースを例に、PowerDesigner 15.2のHibernate機能を解説します。サーバーとPowerDesignerを起動した後、PowerDesignerのリバースエンジニアリング機能を使ってデモデータベースの物理モデルを作成しました。[File]→[Reverse engineer]→[Database link]という手順です。デモデータベース内でGROUPOユーザーが所有しているテーブルは、Employees、Parts、Orders、Departmentsといったおなじみのテーブルで、スキーマの大部分を占めます。リバースエンジニアリングダイアログから、これらを除くすべての項目のチェックを解除しました。
先ほどは物理モデルを作成しましたが、今度はその物理モデルのオブジェクト指向モデルを構築しなければなりません。これにより、必要となるHibernateアーティファクトを作り出すことができます。この作業をアシストするウィザードがあり、[Tools]→[Generate Object-Oriented Model]から開始できます。その結果、以下のようなダイアログが表示されます。
オブジェクト言語として[Java]を選択し、モデルの名前をデフォルト(大文字の物理モデルの名前)から「Java model」に変更します。これでオブジェクト指向のモデルができました。Hibernateオブジェクトの生成を可能にするためには、Hibernateモデルエクステンションでオブジェクト指向モデルを拡張する必要があります。[Model]→[Extended Model Definitions]を選択するとモデルエクステンションのリストが提示されます。ダイアログ上部にあるアイコンのうち右から2番目([Import an extended model definition])をクリックすると以下のダイアログが表示されます。
ここで[Hibernate]を選択します。この時点で、必要なJavaファイルまたはHibernateファイル(もしくはその両方)をPowerDesignerに生成させることが可能になります。注意してほしいのは、Hibernateの方言やその他の設定パラメータをまだ指定していないということです。HibernateのマッピングファイルとJavaのPOJOコードを生成する機能はPowerDesignerにビルトインされています(ちなみに、これらの情報を後で指定して、PowerDesignerにアプリケーション用のテストストリームを自動生成させることもできます。これについては後日の投稿で取り上げるかもしれません)。
JavaコードとHibernateファイルの生成には、メニューから[Language]→[Generate Java Code]を選択します。そのとき、以下の設定ダイアログが表示されます。
上記のスクリーンショットでは、生成される一群のJavaクラスが表示されています。ダイアログ内の他のタブではさまざまなアーティファクト、特にHibernateマッピングファイルを指定することができます。注意が必要なのは、ファイルの生成場所(つまりディレクトリ)の指定です。ダイアログ上部のディレクトリ名のフィールドを空欄にしないよう気をつけてください。ここにはアプリケーションのルートディレクトリを指定する必要があります。このダイアログでは、PowerDesignerによって生成される特定のタイプのオブジェクトについてのディレクトリ指定を含めることができますが、指定した名前は「ルート」ディレクトリの後に''連結''されます。もし間違った絶対パスを指定した場合は(実際、私がやりました)、予測可能なエラーが起きてコード生成のステップが失敗に終わります。
この例では、JavaソースファイルやHibernateマッピングファイルのカスタム化は一切しませんでした。すべてはPowerDesignerのデフォルト機能を使って生成されました。しかしPowerDesignerには、エンティティタイプの階層におけるディスクリミネータ値の使用など、Hibernateマッピングの高度なサポートが含まれています。
次の例は、PowerDesigner 15.2によってEmployeesテーブル用に生成されたHibernateマッピングファイル「Employees.hbm.xml」です。
<?xml version="1.0" encoding="UTF-8"?> <!-- Hibernate XML Mapping File --> <!-- Author: paulley --> <!-- Modified:Monday, June 28, 2010 1:49:22 PM --> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping auto-import="true" default-cascade="none" default-access="property"> <class name="Employees" mutable="true" dynamic-update="false" dynamic-insert="false" select-before-update="false" lazy="true" abstract="false"> <id name="employeeID"> <column name="EmployeeID" sql-type="int" not-null="true"/> </id> <property name="managerID" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="ManagerID" sql-type="int"/> </property> <property name="surname" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="Surname" sql-type="char(20)" not-null="true" length="20"/> </property> <property name="givenName" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="GivenName" sql-type="char(20)" not-null="true" length="20"/> </property> <property name="street" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="Street" sql-type="char(30)" not-null="true" length="30"/> </property> <property name="city" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="City" sql-type="char(20)" not-null="true" length="20"/> </property> <property name="state" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="State" sql-type="char(16)" length="16"/> </property> <property name="country" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="Country" sql-type="char(16)" length="16"/> </property> <property name="postalCode" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="PostalCode" sql-type="char(10)" length="10"/> </property> <property name="phone" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="Phone" sql-type="char(13)" length="13"/> </property> <property name="status" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="Status" sql-type="char(2)" length="2"/> </property> <property name="socialSecurityNumber" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="SocialSecurityNumber" sql-type="char(11)" length="11"/> </property> <property name="salary" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="Salary" sql-type="numeric(20,3)" not-null="true" length="20" precision="3"/> </property> <property name="startDate" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="StartDate" sql-type="date" not-null="true"/> </property> <property name="terminationDate" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="TerminationDate" sql-type="date"/> </property> <property name="birthDate" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="BirthDate" sql-type="date"/> </property> <property name="benefitHealthInsurance" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="BenefitHealthInsurance" sql-type="bit"/> </property> <property name="benefitLifeInsurance" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="BenefitLifeInsurance" sql-type="bit"/> </property> <property name="benefitDayCare" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="BenefitDayCare" sql-type="bit"/> </property> <property name="sex" insert="true" update="true" lazy="false" optimistic-lock="true"> <column name="Sex" sql-type="char(2)" length="2"/> </property> <many-to-one name="departments" class="Departments" outer-join="false" update="true" insert="true"> <column name="DepartmentID" sql-type="int" not-null="true" length="0"/> </many-to-one> <bag name="departments" outer-join="false" lazy="true" optimistic-lock="true"> <key on-delete="noaction" unique="false"> <column name="DepartmentHeadID" sql-type="int" not-null="false" length="0"/> </key> <one-to-many class="Departments"/> </bag> <bag name="salesOrders" outer-join="false" lazy="true" optimistic-lock="true"> <key on-delete="noaction" unique="false"> <column name="SalesRepresentative" sql-type="int" not-null="true" length="0"/> </key> <one-to-many class="SalesOrders"/> </bag> </class> </hibernate-mapping>