ActiveObjectsでCompositeパターンを実装できない理由
ActiveObjectsでのCompositeパターンの実装はたぶんできません。
で、調子に乗っていろいろやっていると、Compositeパターンで嵌る。
・
・
・
いろいろ解決法を探していると。Polymorphicの実験(3) ==失敗== @Polymorphicと@OneToManyを組み合わせた再帰的構造 - 何かしらの言語による記述を解析する日記
やはり、無理なのか?
今回は、Compositeパターンを実装できない理由について調べてみました。
関連を持つエンティティには、インデックスが作られる
まず、他のエンティティへの関連を持つエンティティについて考えます。このようなエンティティをマイグレーションすると、ActiveObjectsはエンティティテーブル間に外部制約とインデックスを作成します。
外部制約は関連元のエンティティテーブルから、関連先のエンティティテーブルに対して作成されます。また、外部キーの値から関連元のエンティティテーブルを検索するインデックスが作成されます。
例えば、以下のエンティティComponentは他のエンティティCompositへの関連を持ちます。
interface Component extends Entity { Composite getParent(); void setParent(Composite parent); } interface Composite extends Entity { }
このエンティティをマイグレーションすると、以下のようなDDLが発行されます。
// compositeテーブルを作成する。 CREATE TABLE composite ( id INTEGER AUTO_INCREMENT NOT NULL, PRIMARY KEY(id) ) ENGINE=InnoDB // componentテーブルを作成する。 CREATE TABLE component ( id INTEGER AUTO_INCREMENT NOT NULL, name VARCHAR(255), parentID INTEGER, // compositeテーブルへの外部制約 CONSTRAINT fk_component_parentid FOREIGN KEY (parentID) REFERENCES composite(id), PRIMARY KEY(id) ) ENGINE=InnoDB // 外部キーでcomponentテーブルを検索するためのインデックス CREATE INDEX index_component_parentid ON component(parentID)
これにより、componentテーブルからcompositeテーブルへの外部制約が作成されます。また、componentテーブルのエンティティを外部キーparentIDで検索するインデックスが作成されます。
Polymophicを使った場合、関連エンティティへのインデックス作成がおかしい!!
さて、本題です。ComponentエンティティをPolymophicにし、CompositeエンティティにComponentを継承させます。
@Polymorphic interface Component extends Entity { String getName(); void setName(String name); Composite getParent(); void setParent(Composite parent); } interface Composite extends Component { }
このエンティティをマイグレーションすると、以下のようなDDLが発行されます。
// compositeテーブルを作成する。 CREATE TABLE composite ( id INTEGER AUTO_INCREMENT NOT NULL, name VARCHAR(255), parentID INTEGER, CONSTRAINT fk_composite_parentid FOREIGN KEY (parentID) REFERENCES composite(id), PRIMARY KEY(id) ) ENGINE=InnoDB // componentテーブルは作成されない。Polymorphicであるため! // 外部キーでcompositeテーブルを検索するためのインデックスを作成する。 CREATE INDEX index_composite_parentid ON composite(parentID) // 外部キーでcomponentテーブルを検索するためのインデックスを作成する???? CREATE INDEX index_component_parentid ON component(parentID) // インデックス作成で、エラー発生 com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'aotest.component' doesn't exist at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:532) at com.mysql.jdbc.Util.handleNewInstance(Util.java:353) at com.mysql.jdbc.Util.getInstance(Util.java:336) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1031) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:957) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2938) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1601) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1710) at com.mysql.jdbc.Connection.execSQL(Connection.java:2430) at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1374) at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1290) at net.java.ao.schema.SchemaGenerator.migrate(SchemaGenerator.java:77) at net.java.ao.EntityManager.migrate(EntityManager.java:192) at TestC.main(TestC.java:26)
CompositeエンティティはComponentエンティティの以下のメソッドを継承します。
Composite getParent(); void setParent(Composite parent);
そのため、Compositeエンティティは自分自身に対する関連を持ちます。したがって、compositeテーブルに外部キーによるインデックスが作成されます。ここまでは問題ありません。
しかし、ActiveObjectsはcomponentテーブルに対してもインデックスを作成しようとするのです。というのも、エンティティが他のエンティティを継承している場合、ActiveObjectsは親エンティティを調べます。そして、親エンティティの持つ関連に対しても、インデックスを作成しようとします。
今回の場合、Compositeエンティティはの親エンティティであるComponentエンティティを調べます。ComponentエンティティはCompositeエンティティに対して関連をもちます。したがって、componentテーブルにインデックスを作成しようします。
とはいえ、componentテーブルはPolymorphicであるため、componentテーブルは存在しません。存在しないテーブルに対するインデックス作成は、当然エラーになります。
なんでActiveObjectsはこんなことに??
わかりません。ともかく現状はこういったことはできないようです。
ちなみに、ActiveObjectsのソースを書き換えればこの問題には対処できます。Polymorphicの実験(3) ==失敗== @Polymorphicと@OneToManyを組み合わせた再帰的構造を参照ください。
参考:ActiveObjectsの発行するSQLの確認
EntityManagerのインスタンスを作成後に以下のコードを実行すると、ActiveObjectsの発行したSQLが標準出力に出力されるようになります。
java.util.logging.Logger.getLogger("net.java.ao").setLevel(java.util.logging.Level.FINE);