ActiveObjectsのPDFドキュメントの日本語訳

https://activeobjects.dev.java.net/ActiveObjects.pdfでPDF形式で公開されているActiveObjectsのドキュメントを勝手に日本語訳してみました。

誤訳や誤植等あるかもしれませんが、ソースファイルを読んだ内容と比べても違和感はないので、大間違いはないと思います。

以下、本文となります。

基本的な考え方

Basic Concepts

ActiveObjectsの基本原則は、「設定より規約」という有名なキャッチフレーズです。開発者は基本的なJavaデータオブジェクト規約(get、set、is等…)を使用して抽象Javaクラスインタフェースを作成します。そして、データベースへの接続はActiveObjectsが処理します。さらに言うと、ActiveObjectsJavaクラスモデルからデータベース固有のスキーマを生成します。

The underlying principle behind ActiveObjects is that ever-popular catch phrase: convention rather than configuration. With ActiveObjects, you create the abstract Java class interfaces using standard Java data object conventions (get, set, is, etc...) and ActiveObjects handles the wiring to the database. In fact, ActiveObjects will even generate the database-specific schema to correspond with the specified Java class model.

以下のように、開発者はActiveObjectsを簡単に使用できます。

From a developer standpoint, using ActiveObjects is as simple as the following:

	public interface Person extends Entity {
		public String getFirstName();
		public void setFirstName(String name);
		public String getLastName();
		public void setLastName(String name);
	}
	
	// ...
	EntityManager manager = new EntityManager("jdbc:mysql://localhost/ao_test", "user", "password");

	Person frank = manager.create(Person.class);
	frank.setName("Frank");
	frank.setName("Smith");
	frank.save();

上記のコードだけで動作します。設定ファイルは不要です。実を言うと、データベーススキーマを生成する手順が抜けています。ActiveObjectsマイグレーションという機能でこの手順を行うことができます。

The code specified above is fully functional; there are no unspecified configuration files. In fact, the
only missing step would be to create the corresponding database schema. ActiveObjects can handle
this step too, using a feature called migrations:

// ...
	manager.migrate(Person.class);

以下のようなDDLが実行されます(上記のURL同様にMySQLを想定しています)。

This will execute DDL something like the following (assuming MySQL as in the URI above):

	CREATE TABLE person (
		id INTEGER AUTO_INCREMENT NOT NULL,
		firstName VARCHAR(255),
		lastName VARCHAR(255),
		PRIMARY KEY (id)
	);

マイグレーションにより、スキーマの変更を意識する必要がなくなり、コードのエンティティモデルとスキーマの同期を維持できます。上記の例のPersonエンティティにsetAge():intメソッド(と対応するセッター)を追加するとしたらどうでしょう。Personテーブルが既に作成されている場合、テーブルにデータが含まれていることもあります。新しいバージョンのテーブルを生成し、古いデータの移行と古いテーブルの削除を行うスクリプトを書くのは、不快で苦痛をともなうかもしれません。こんな大変なことをしなくても、(上記の)migrateメソッドを呼び出せば必要なことはすべて行えます。このメソッドを呼び出したとき、ActiveObjectsはゼロからすべてのテーブルを生成せず、スキーマを調べてageフィールドの追加に必要な動作を決めます。結局、下記のDDL文だけを実行します。

With migrations, you don't need to worry about making changes to your schema, or keeping your schema in sync with your entity model in code. Supposing you add a getAge():int method (and its corresponding setter) to the Person entity in the above example. If the person table has already been created, it probably contains data. It would be ugly and painful to write scripts to generate a new version of the table, migrate the old data over and delete the old table. Instead of this mess, all that is necessary is another call to the migrate method (as shown above). This time, instead of generating the full table from scratch, ActiveObjects will inspect the existing schema and determine that the only necessary action is the addition of the age field. To this end, it will execute only the following DDL statement:

	ALTER TABLE person ADD COLUMN age INTEGER;

不快なスクリプトの記述をしなくても、データはすべて保持され、コードのエンティティモデルとデータベースのスキーマは以前と同様に同期が取られます。この強力な機能により、エンティティモデルのリファクタリングや、制約やインデックスの追加などを、データベースに悪影響を与えずに行うことができます。

All of the data has been retained, no ugly scripts were written and the entity model in code is once again in sync with the database schema.
This powerful functionality allows for things such as
refactoring your entity model, adding constraints and indexes and so on all without adversely efecting your database.

EntityManagerのコンストラクタに渡すJDBC URIを変更することで、データベースを切り替えることができます。データベース固有の入出力を考慮したり、手作業でSQLを書いたりする必要は一切ありません。実際、データベースクライアントを一度も開かなくても、あるいは、SQLRDBMSの基本を一切理解していなくとも、ActiveObjectを使用することでデータベースの機能を十分に活用することができます。

Switching databases is as easy as changing the JDBC URI value in the EntityManager constructor.
There is never any need to be concerned about the ins and outs of database speci cs or to write any SQL by hand.
In fact, you can use ActiveObjects and take full advantage of database functionality without ever once opening a database client or even understanding the basics of SQL or RDBMS.
In fact, any Java developer can use ActiveObjects, even if they have no experience whatsoever in databases or their underlying concepts.
The framework is designed so that it \feels" natural to any Java developer, not requiring any knowledge of relational databases.

本質

Internals

ActiveObjects APIにより、だいぶ変わった使用パターン(たとえば、ピュアJavaインタフェースによるエンティティ定義の構築)が可能になりますが、このAPIを実現するコードははそれほど複雑でありません。ActiveObjectライブラリの中核部分は、ほとんどリフレクションのプロキシインスタンスの考え方で解決されています。

While the ActiveObjects API does allow for some rather unusual usage patterns (such as the construction of entity definitions as pure Java interfaces), the code to support this API isn't all that complex.
Much of the heart and soul of the ActiveObjects library revolves around the concept of reflective instance proxying.

プロキシインスタンスは、JavaのリフレクションAPIのうちでもあまり知られていません。InvocationHandlerクラスのインスタンスは、指定したクラスの動的に生成されたインスタンスのすべてのメソッド呼び出しを横取りします。EntityManagerはこれを利用して、Entityの任意のサブインタフェースのインスタンスを動的に生成しています。実際、EntityManagerが作成したインスタンスは非常に特殊で、すべてのメソッド呼び出しを動的に解析し、代わりに適切なデータベースを呼び出そうとします。エンティティインスタンスのアクセッサの呼び出しが、すぐにデータベース呼び出しが行われるのはこのためです。

Instance proxying is a lesser known part of the Java reflection API which allows a specified instance of the InvocationHandler class to intercept all method calls to a contrived instance of a specified class type.
This is how the EntityManager can dynamically create an instance of an arbitrary Entity sub-interface.
In fact, the instance created by EntityManager is extremely special in that it will dynamically parse all method calls, attempting to proxy the relevant calls down to the database.
This is how a call to an accessor within an entity instance will immediately correspond to a call to the database.

ActiveObjectsのInvocatonHanderプロキシインスタンスは、EntityProxyクラスです。ExtityProxyには、Entityインスタンスへのすべてのメソッド呼び出しを受け取る、invokeという単一のメソッドがあります。EntityProxyはメソッドの名前やアノテーション、パラメータに一連の規則を適用し、データアクセスの必要なメソッドかどうか判断します。もし、データアクセスが必要な場合、EntityProxyはデータベース接続を取得し、適切なSQLを実行します。

The instance proxy InvocationHandler for ActiveObjects is the EntityProxy class.
EntityProxy has a single method - invoke - which receives all method calls to the corresponding Entity instance.
EntityProxy then applies a set of rules to the method name, method annotations and method parameters to determine if it is a data accessor method. If the method should correspond to a database function, EntityProxy handles the retrieval of a database connection and the execution of the appropriate SQL.
For more details on ActiveObjects's implementation, refer to the javadocs.

使用例

Example Usage

AciteObjectsを使ってアプリケーションを構築するための最初の手順は、エンティティの定義です。これはピュアJavaで行えます(XMLの設定は不要です)。エンティティ定義はデータベース固有ではありません。

The first step in building an application which uses ActiveObjects is to define the entities. This is done
in pure Java (no XML configuration). The entity deifnitions are database non-specific:

	// Person.java
	public interface Person extends Entity {
		public String getName();
		public void setName(String name);

		public int getAge();
		public void setAge(int age);

		@SQLType(Types.CLOB)
		public String getComment();

		@SQLType(Types.CLOB)
		public void setComment(String comment);

		public Family getFamily();
		public void setFamily();

		@ManyToMany(PersonToPerson.class)
		public Person getPeople();
	}
	// Family.java
	public interface Family extends Entity {
		public String getName();
		public void setName(String name);

		@OneToMany
		public Person getPeople();
	}
	// PersonToPerson.java
	public interface PersonToPerson extends Entity {
		public Person getPersonA();
		public void setPersonA(Person person);

		public Person getPersonB();
		public void setPersonB(Person person);
	}

エンティティ定義から、使用するデータベース用のDDL文を自動的に生成できます。生成のためにはマイグレーションを使用できます。ここで、MySQLデータベース用に生成されたDDLを示します。

Once we have the entity definitions, the DDL statements can be auto-generated for whatever database you may be using. This is done using migrations. Here's what the DDL would look like as generated for the MySQL database:

	CREATE TABLE family (
		id INTEGER NOT NULL AUTO_INCREMENT,
		name VARCHAR(255),
		PRIMARY KEY (id)
	) ENGINE=InnoDB;

	CREATE TABLE person (
		id INTEGER NOT NULL AUTO_INCREMENT,
		name VARCHAR(255),
		age INTEGER,
		comment TEXT,
		familyID INTEGER,
		PRIMARY KEY (id),
		CONSTRAINT fk_person_familyID FOREIGN KEY (familyID) REFERENCES family(id)
	) ENGINE=InnoDB;

	CREATE TABLE personToPerson (
		id INTEGER NOT NULL AUTO_INCREMENT,
		personAID INTEGER,
		personBID INTEGER,
		PRIMARY KEY (id),
		CONSTRAINT fk_personToPerson_personAID FOREIGN KEY (personAID) REFERENCES person (id),
		CONSTRAINT fk_personToPerson_personBID FOREIGN KEY (personBID) REFERENCES person (id)
	) ENGINE=InnoDB;

フィールド名のや主キー、外部キー等を決める規約の使い方に気が付くでしょう。もちろん、ORM層は実際に使わないとあまり意味がありません。ここで、この新しいモデルでの基本的なCRUD操作を行う、サンプルコードを示します。

You'll notice the usage of convention to determine field names, primary and foreign keys, etc... Of course, an ORM layer isn't very useful unless you actually use it. Here's some sample code showing some basic CRUD operations with our new model:

	// ...

	// 指定したURIに関連するEntityMangerを取得する。
	// 可能なら(クラスパスが適切に設定されていれば)、接続はプールされます。
	// retrieves an EntityManager relevant to the specified URI
	// if available (i.e. the classpath set appropriately), the connection will be pooled
	EntityManager manager = new EntityManager("jdbc:mysql://localhost/ao_test", "user", "password");

	Family family = manager.create(Family.class);
	family.setName("Spiewak");
	family.save();

	Person me = manager.create(Person.class);
	me.setName("Daniel Spiewak");
	me.setAge(27);
	me.setComment("I love databasing");
	me.setFamily(family);
	me.save();

	Person you = manager.create(Person.class);
	you.setName("Joe Blow");
	you.setAge(23);
	you.setComment("Guess who?");
	you.setFamily(family);
	you.save();

	PersonToPerson relation = manager.create(PersonToPerson.class);
	relation.setPersonA(me);
	relation.setPersonB(you);
	relation.save();

	family.getPeople(); // ...returns new Person {you, me}
	you.getPeople(); // ...returns new Person {me}

	/*
	 * SQLを使用する最初の例ですが、わざとらしい例です。
	 * notice, this is the first use of SQL in the whole example, and
	 * it's a contrived usage at that
	 */
	Family families = manager.findWithSQL(Family.class, "familyID", "SELECT DISTINCT familyID FROM person");

	// ageが18以上のすべてのpersonを返す。
	// returns any person with age greater than or equal to 18
	Person overAge = manager.find(Person.class, "age >= ?", 18);

	/*
	 * 可変長パラメータと、ID値を意識しない、エンティティインスタンスの直接的な使用
	 * notice the varargs parameters, as well as the direct use of an
	 * entity instance without worrying about the ID value
	 */
	Person[] inFamilyOver21 = manager.find(Person.class, "age >= ? AND familyID = ?", 21, family);

ご覧のように、このフレームワークのほとんどは、スキーマの詳細から開発者を完全に守るように設計されています。唯一このようになっていないのは、findメソッドで、開発者を完全に守ることより、SQLの全力を発揮できるように設計されています。

As you see, much of the framework is designed to completely encapsulate the developer from the complexities of SQL and even from the precise speci cs of the schema. The only place where this rule is broken is in the find methods, which are designed to leverage the full power of SQL, rather than completely shelter the developer.

サポートするデータベース

Supported Databases

ActiveObjectsは現在ほとんどの主要なデータベースをサポートしています。現在のバージョンから0.8安定リリースまでの状況は以下のとおりです。

Most major databases are currently supported by ActiveObjects. This data is only current to the 0.8 stable release.

データベース URI プロトコル サポート状況
Derby jdbc:derby, jdbc:derby:// 安定しいている
HSQLDB jdbc:hsqldb, jdbc:hsqldb:// よくテストされ非常に安定している
MS SQL Server 2005 and 2007 jdbc:sqlserver://, jdbc:jtds:sqlserver:// 安定している
MySQL jdbc:mysql:// よくテストされ非常に安定している
Oracle jdbc:oracle:thin, jdbc:oracle:oci 開発中
PostgreSQL jdbc:postgresql:// よくテストされ安定している
Database URI Protocol Support
Derby jdbc:derby, jdbc:derby:// Stable
HSQLDB jdbc:hsqldb, jdbc:hsqldb:// Well tested and very stable
MS SQL Server 2005 and 2007 jdbc:sqlserver://, jdbc:jtds:sqlserver:// Stable
MySQL jdbc:mysql:// Well tested and very stable
Oracle jdbc:oracle:thin, jdbc:oracle:oci Under development
PostgreSQL jdbc:postgresql:// Well tested and stable

なぜActiveObjectsなのか?

Why ActiveObjects?


ActiveObjectsに関するもっとも重要な質問には、「なぜ他のORMを使うのか?」について説明することで答えられるでしょう。JavaのORMフレームワークの分野では、JBossフレームワークHibernateが完全に優位に立っています。実際、Hibernateは幅広く利用され評価が高いため、.NETへの移行まで行われています(NHibernate)。このように幅広く評価され、幅広く利用され、すべての重要な問題を解決しうる成熟したフレームワークがすでにあるのに、なぜ新たなものを投げ入れて混乱を起こすのでしょうか?

Possibily the most important question to answer regarding ActiveObjects is to explain: why another ORM? The Java ORM framework genre is completely dominated by the JBoss framework, Hibernate. In fact, Hibernate is so widely used and respected that it's even been ported to .NET (NHibernate). So, if there's already a widely respected, widely used and mature framework which seems to satesfy all important use-cases, why throw another into the mix?

その答えは本当は複雑になります。Hibernateは信じられないほど複雑なフレームワークです。仮に、複雑である一番の理由が強力であることだとしても、時には(あるいはほとんどの場合)要求される問題はとても単純であり、Hibernateの複雑さを必要としません。ActiveObjectsは、最低限の設定だけで、できる限り簡単に使えることを基本としています。実際、今までのところActiveObjectsにはXML(や他のフォーマット)の設定ファイルを扱うフックは一つもありません。コードから推測させるか、コード中でアノテーションを個別に指定する簡単な方法により、すべての設定が行えます。

The answer really comes back to complexity. Hibernate is an incredibly complex framework. Granted, it is complex mainly because it is powerful, but sometimes - most of the time even - the required use-case is very simple and doesn't require all of Hibernate's compexities. ActiveObjects is designed from the ground up to be as easy to use as possible, with a bare minimum of configuration. In fact, to date ActiveObjects doesn't even have a single hook which takes an XML (or any other format) configuration file. Any and all configuration is either guessed from code, or easily set in code through the discrete use of annotations.

業界は「設定より規約」の向う傾向が大きくなっています。このことはRuby on RailsのようなRADフレームワークを影響が大きいでしょう。多くの業界の専門家は、20行のコードと150行のXML設定を書くより、20行のコードを書く方がずっと簡単だということに同意しています。ActiveObjectは実装においてできる限りこの原則に従っています。実際のところ、このフレームワークは、Railsのもつ素晴らしいActive Record ORMに着想を得ています。例外的に、ActiveObjectsは英語の複数系の規則の適用をデフォルトにはしませんでした(必要な場合にこの機能を使うことは可能です)。

There is an increasing trend in the industry towards "convention over configuration." This is most reflected in RAD frameworks such as Ruby on Rails. Much of the accepted industry pundits agree that writing 20 lines of code is much easier than 20 lines of code and 150 lines of XML configuration (go figure). ActiveObjects follows this practice as much as possible in its implementation. In fact, much of the inspiration for the framework comes from Rails's excelent ActiveRecord ORM (hence, the name). The exception to this inspiration would be that ActiveObjects does not impose English pluralization rules by default (though it is capable of such functionality when it is desirable).

実は、ActiveObjectsは永続化フレームワークを簡単で使いやすくするために非常に努力しており、(分散トランザクションのような)いくつかの機能は単に除外しています。なぜなら、99%の問題はそのような最先端の手法を使用しないからです。もし、プロジェクトがORMにそのような複雑な動作を求める場合は、Hibernateを使用するべきです。それくらい簡単なことなのです。ActiveObjectはHibernateにとって代わることを目的としていません。そうではなく、Hibernateの強力な力を必要としない多くの状況において、より簡単でより軽量な代替となることを目標としています。

In fact, ActiveObjects strives so hard to be a simple and easy-to-use persistence framework that some functionality (such as distributed transactions) has been simply omitted. The reasoning behind this is that 99% of use-cases do not call for such extreme measures. If your project does require such complex behavior within your ORM, you should be using Hibernate. It's as simple as that. ActiveObjects is not intended to supplant Hibernate. Rather, its goal is to be an easier and lighter alternative for the many common scenarios which do not call for all of Hibernate's awsome power.

ようするに、ActiveObjectsはデータベース開発をより簡単で楽しくすることを目指しています。データベーススキーマの複雑さや独特のアクセス方法といった細かなことを、開発者が意識させないようにします。ActiveObjectsを使えば、開発者は高レベルなオブジェクト指向設計やデータカプセル化などの高レベルな概念だけを意識すればよくなります。

In short, ActiveObjects attempts to make database development simple and fun again. It abstracts the developer from the intricacies of the database schema and the particulars of how to access it. Using ActiveObjects, the only thing the developer needs to worry about is the high-level concept of object-oriented design and data encapsulation.