高度なカスタマイズ > サービスおよびインフラストラクチャのカスタマイズ > システム生成 > ビジネスオブジェクトのモデル化 > テーブルのモデル化
  
テーブルのモデル化
モデル化されたクラスの構造 (GenAsPersistable)
まず、シンプルで機能的な例を示します。この例は、この章のほかのセクションからも参照します。
リスト 1: SimpleExample.java
01 package com.acme.example;
02
03 import wt.fc.InvalidAttributeException;
04 import wt.fc.WTObject;
05 import wt.inf.container.WTContained;
06 import wt.util.WTException;
07 import wt.util.WTPropertyVetoException;
08
09 import com.ptc.windchill.annotations.metadata.*;
10
11 @GenAsPersistable(superClass=WTObject.class,
interfaces={WTContained.class},
12 properties={
13 @GeneratedProperty(name="name", type=String.class,
14 constraints=@PropertyConstraints(required=true))
15 })
16 public class SimpleExample extends _SimpleExample {
17 static final long serialVersionUID = 1;
18
19 public static SimpleExample newSimpleExample() throws WTException {
20 final SimpleExample instance = new SimpleExample();
21 instance.initialize();
22 return instance;
23 }
24
25 @Override
26 public void checkAttributes() throws InvalidAttributeException {
27 super.checkAttributes();
28 try {
29 nameValidate(name);
30 } catch (WTPropertyVetoException wtpve) {
31 throw new InvalidAttributeException(wtpve);
32 }
33 }
34 }
_SimpleExample を拡張するクラス SimpleExample (行番号 16) が定義されています。このクラスには GenAsPersistable アノテーション (行番号 11 から 15) が付けられています。これは、このクラスがデータベース内でテーブルとして永続化されることを示します。このクラスには、(必須の) 文字列である name というプロパティ (行番号 13 から 14) が 1 つあります。さらに、静的なファクトリメソッド (行番号 19 から 23) と checkAttributes() メソッドのオーバーライド (行番号 25 から 33) が 1 つずつ実装されています。
_SimpleExample のソースは次のとおりです。
GenAsPersistable アノテーションのアノテーションプロセッサによって親クラスが生成されます (厳密には、後でコンパイラによってコンパイルされるソースファイルが生成されます)。
この親クラスは、name プロパティの実装 (照会定数、フィールド、ゲッター、セッター、およびセッターの検証) を提供するだけではなく、外部化ロジックやその他の必要なメソッドも提供します。
_ クラスは、リスト 2 に示されているフィールドとメソッドで構成されます。
リスト 2: _SimpleExample javap の結果
01 public abstract class com.acme.example._SimpleExample extends wt.fc.WTObject implements wt.inf.container.WTContained,java.io.Externalizable{
02 static final long serialVersionUID;
03 static final java.lang.String RESOURCE;
04 static final java.lang.String CLASSNAME;
05 public static final java.lang.String NAME;
06 static int NAME_UPPER_LIMIT;
07 java.lang.String name;
08 wt.inf.container.WTContainerRef containerReference;
09 public static final long EXTERNALIZATION_VERSION_UID;
10 public com.acme.example._SimpleExample();
11 public java.lang.String getName();
12 public void setName(java.lang.String) throws wt.util.WTPropertyVetoException;
13 void nameValidate(java.lang.String) throws wt.util.WTPropertyVetoException;
14 public java.lang.String getContainerName();
15 public wt.inf.container.WTContainer getContainer();
16 public wt.inf.container.WTContainerRef getContainerReference();
17 public void setContainer(wt.inf.container.WTContainer) throws wt.util.WTPropertyVetoException, wt.util.WTException;
18 public void setContainerReference(wt.inf.container.WTContainerRef) throws wt.util.WTPropertyVetoException;
19 void containerReferenceValidate(wt.inf.container.WTContainerRef) throws wt.util.WTPropertyVetoException;
20 public java.lang.String getConceptualClassname();
21 public wt.introspection.ClassInfo getClassInfo() throws wt.introspection.WTIntrospectionException;
22 public java.lang.String getType();
23 public void writeExternal(java.io.ObjectOutput) throws java.io.IOException;
24 protected void super_writeExternal_SimpleExample(java.io.ObjectOutput) throws java.io.IOException;
25 public void readExternal(java.io.ObjectInput) throws java.io.IOException, java.lang.ClassNotFoundException;
26 protected void super_readExternal_SimpleExample(java.io.ObjectInput) throws java.io.IOException, java.lang.ClassNotFoundException;
27 public void writeExternal(wt.pds.PersistentStoreIfc) throws java.sql.SQLException, wt.pom.DatastoreException;
28 public void readExternal(wt.pds.PersistentRetrieveIfc) throws java.sql.SQLException, wt.pom.DatastoreException;
29 boolean readVersion6009937787959182077L(java.io.ObjectInput, long, boolean) throws java.io.IOException, java.lang.ClassNotFoundException;
30 protected boolean readVersion(com.acme.example.SimpleExample,
java.io.ObjectInput, long, boolean, boolean) throws java.io.IOException,
java.lang.ClassNotFoundException;
31 protected boolean super_readVersion_
SimpleExample
(com.acme.example._SimpleExample, java.io.ObjectInput, long, boolean, boolean) throws java.io.IOException, java.lang.ClassNotFoundException;
32 boolean readOldVersion(java.io.ObjectInput, long, boolean, boolean) throws java.io.IOException, java.lang.ClassNotFoundException;
33 static {};
34 }
このアノテーションは、後でコンパイラによって実装される宣言で構成されています。name のすべての要素を手動で実装する代わりに、これがアノテーション内でプロパティとして宣言され、_ 親ファイルがすべての必要なコンポーネントを実装します。リスト 3 は、required=truenameValidate() メソッドに対する効果の例を示しています。
リスト 3: nameValidate() の断片
01 if (name == null || name.trim().length() == 0)
02 throw new wt.util.WTPropertyVetoException("wt.fc.fcResource", wt.fc.fcResource.REQUIRED_ATTRIBUTE,
03 new Object[] { new wt.introspection.PropertyDisplayName (CLASSNAME, "name") },
04 new java.beans.PropertyChangeEvent(this, "name", this.name, name));
superClassinterfaces (リスト 1 の行番号 11) の目的は次のとおりです。
superClass アノテーションメンバーはシンプルであり、extends_ クラスによって吸収されています。これにより、モデル化された要素をコードにして、クラスに組み込むことができます。したがって、"実際の" (論理的な) 親 (ここでは WTObject) を指定できるようにする superClass が必要です。これは、後で _ クラス (_SimpleExample) が自動的に拡張します (前述のとおり)。
interfaces アノテーションメンバーも同じような目的で使用されます。これは、実装するインタフェースを識別できるようにします。implements は同じように吸収されていませんが、さまざまな技術的観点から、アノテーションメンバーを使用することは、実装を直接使用することよりも効果的で、論理にかなっています。この例は WTObject を拡張し、WTContained を実装します。コンパイラは public class <X> extends _<X> { ... } 構文を適用するので、この構文が使用されていなければ、コンパイラでコンパイルエラーが発生します。
ファクトリパターン (リスト 1 の行番号 19 にある newSimpleExample()) が使用されていることに注目してください。Windchill では、コンストラクタの代わりにこのパターンが使用されます。外部化では引数を取らないコンストラクタを使用してインスタンスが構築される必要があり、またこれを呼び出すための負担が最小限である必要があるからです。ファクトリパターンは、public static <X> new<X>(<args...>) throws WTException { ... } という形式の静的メソッドで構成され、アノテーションが付けられているファイルのコンストラクタの代わりに使用されます。ファクトリのボディは、例によって示されている形式に従っている必要があります。具体的には、次の処理を行う必要があります。
1. クラスの instance を、その (デフォルトの) 引数を取らないコンストラクタを使用して構築する。
2. (静的ではない) initialize メソッドを呼び出して、静的メソッドに指定されている引数を渡す。静的ファクトリが単独で引数を処理することはできません。サブクラスが静的メソッドの処理を利用できないからです (動作を継承できるようにすることが initialize メソッドの主な目的です)。独自の initialize メソッドを作成する必要がある場合があります (存在しない場合、または動作を補足する必要がある場合)。その場合、これは protected void initialize(args...) throws WTException という形式である必要があり、また super.initialize(...) を呼び出す必要があります。
3. instance を返します。
次のファクトリ/初期化ペアは、必要なプロパティが割り当てられるように name を受け取るファクトリメソッドを追加する適切な方法を示しています。
リスト 4: ファクトリ/初期化ペアの例
01 public static SimpleExample newSimpleExample(final String name) throws WTException {
02 final SimpleExample instance = new SimpleExample();
03 instance.initialize(name);
04 return instance;
05 }
06
07 protected void initialize(final String name) throws WTException {
08 super.initialize();
09 try {
10 setName(name);
11 } catch (WTPropertyVetoException wtpve) {
12 throw new WTException(wtpve);
13 }
14 }
さらに、serialVersionUID (リスト 1 の行番号 17) が必要です。Windchill が外部化ロジック (必要に応じて古いバージョンのシリアル化解除を含む) を管理し、システムによって割り当てられたシリアルバージョン UID がこのロジックを破断するからです。ここでも、これが提供されていなければ、コンパイラでコンパイルエラーが発生します。
最後に、checkAttributes() は、生成されたフィールドとメソッド (リスト 1 の行番号 29) が使用されていることを示すためだけに役立ちます。特に、namenameValidate() は、アノテーションが付けられているクラスにアクセスすることを許可するだけのデフォルトのアクセス設定で生成されます。
コンパイル
コンパイルについては、com.ptc.windchill.annotations.metadata パッケージの Javadoc で詳しく説明されていますが、SimpleExample のカスタマイズは、次のステップを実行することによってコンパイルし、組み込むことができます。
1. ロードポイント (たとえば /opt/ptc/Windchill) に cd で移動します。
2. bin/windchill shell を呼び出して、windchill シェルを起動します。
3. mkdir -p src/com/acme/example を実行して、src/com/acme/example ディレクトリを作成します。
4. このディレクトリに SimpleExample.java を作成し、これに上記の例の内容を含めます。
5. ant -f bin/tools.xml class -Dclass.includes=com/acme/example/* を実行して、この例をコンパイルします (このコマンドはロードポイントディレクトリで実行する必要があります)。
6. ant -f bin/tools.xml sql_script -Dgen.input=com.acme.example.* を実行して、SQL スクリプトを生成します。
7. SQL スクリプト create_SimpleExample_Table.sql を見つけ (db 内にあります)、ロードします。
8. create_SimpleExample_Index.sql についても同じステップを繰り返します。
9. MethodServer を起動または再起動します。
リスト 5 に示されているように、Jython を使用すると、この例を素早く検証できます。
リスト 5: SimpleExample の永続化
01 from wt.fc import PersistenceHelper
02 from com.acme.example import SimpleExample
03
04 se = SimpleExample.newSimpleExample()
05 se.setName('test')
06 se = PersistenceHelper.manager.store(se)
プロンプトが表示されたら、システム管理者としての認証情報を入力します。オブジェクトがサイトコンテナに作成されます。これは、sn.getContainer().getName() を実行することによって確認できます。setName(’test’) が別個に呼び出されていますが、これは、引数として name を使用してファクトリ/初期化メソッドを作成した場合には必要ありません。
クラスのテーブルと列へのマッピング
この例では SimpleExample のインスタンスが永続化されたので、テーブルに行が 1 つ追加されています。テーブルの詳細をリスト 6 に示します。
リスト 6: create_SimpleExample_Table.sql
01 exec WTPK.dropTable('SimpleExample')
02 set echo on
03 REM Creating table SimpleExample for com.acme.example.SimpleExample
04 set echo off
05 CREATE TABLE SimpleExample (
06 classnamekeycontainerReferen VARCHAR2(600),
07 idA3containerReference NUMBER,
08 name VARCHAR2(600) NOT NULL,
09 createStampA2 DATE,
10 markForDeleteA2 NUMBER NOT NULL,
11 modifyStampA2 DATE,
12 classnameA2A2 VARCHAR2(600),
13 idA2A2 NUMBER NOT NULL,
14 updateCountA2 NUMBER,
15 updateStampA2 DATE,
16 CONSTRAINT PK_SimpleExample PRIMARY KEY (idA2A2))
17 STORAGE ( INITIAL 20k NEXT 20k PCTINCREASE 0 )
18 ENABLE PRIMARY KEY USING INDEX
19 TABLESPACE INDX
20 STORAGE ( INITIAL 20k NEXT 20k PCTINCREASE 0 )
21 /
22 COMMENT ON TABLE SimpleExample IS 'Table SimpleExample created for com.acme.example.SimpleExample'
23 /
24 REM @//com/acme/example/SimpleExample_UserAdditions
テーブルの名前は、クラスの名前を反映します。これは変更できます。詳細については、アノテーションメンバー tableProperties および関連付けられているメンバー tableName の Javadoc を参照してください。列のうち、name については内容が誰にでもすぐにわかりますが、その他の列についてはそうとはかぎりません。WTContained であることに基づいて取得された classnamekeycontainerReferen 列と idA3containerReference 列には、コンテナへの参照が格納されています。その他の列は、Persistable であることに基づいて取得されました。これは、Windchill クラスをテーブルとして永続化するためのトップレベルインタフェースです。
モデル化された関連付け (GenAsBinaryLink)
クラスをテーブルにマッピングするためのアノテーションは 2 つあり、1 つは GenAsPersistable で、もう 1 つは GenAsBinaryLink です。これは、2 つの永続オブジェクト (2 つのテーブルの行) を相互にリンクする関連付けを表します。各バイナリリンクは、役割 A と役割 B の 2 つの役割で構成されます。
SimpleExamples のグラフを作成できます。このグラフでは、SimpleExample に複数の親と子を設定できます。
リスト 7: SimpleExampleLink.java
01 package com.acme.example;
02
03 import wt.fc.ObjectToObjectLink;
04 import wt.util.WTException;
05
06 import com.ptc.windchill.annotations.metadata.*;
07
08 @GenAsBinaryLink(superClass=ObjectToObjectLink.class,
09 roleA=@GeneratedRole(name="parent", type=SimpleExample.class),
10 roleB=@GeneratedRole(name="child", type=SimpleExample.class))
11 public class SimpleExampleLink extends _SimpleExampleLink {
12 static final long serialVersionUID = 1;
13
14 public static SimpleExampleLink newSimpleExampleLink
(final SimpleExample parent, final SimpleExample child)
throws WTException {
15 final SimpleExampleLink instance = new SimpleExampleLink();
16 instance.initialize(parent, child);
17 return instance;
18 }
19 }
これは、同様にコンパイルすると、リスト 8 に示すように、Jython で検証できます。
リスト 8: SimpleExampleLink の永続化
01 from wt.fc import PersistenceHelper
02 from com.acme.example import *
03
04 parent = SimpleExample.newSimpleExample()
05 parent.setName('parent')
06 parent = PersistenceHelper.manager.store(parent)
07
08 child = SimpleExample.newSimpleExample()
09 child.setName('child')
10 child = PersistenceHelper.manager.store(child)
11
12 l = PersistenceHelper.manager.store(SimpleExampleLink.
newSimpleExampleLink (parent, child))
SimpleExample の場合と同じように、ファクトリメソッドが使用されます (リスト 7 の行番号 14 から 17)。ただし、リンクは常に 2 つのオブジェクトを関連付けるので、ファクトリメソッド (および関連付けられている initialize メソッド) は役割 A と役割 B を引数として受け取ります。