Kenji Tachibana's Weblog

« 翻訳: JavaFX Async 操作 | メイン | 両方そろいました。 »

http://blogs.sun.com/kenji/date/20081220 2008年 12月 20日 土曜日

JavaFX でデータベースにアクセス - Async を使って非同期で

JavaFX は単一の EDT (イベントディスパッチスレッド) 上で、実行されているので、DB へのアクセスを JavaFX 上で実行すると、たとえば、同じアプリケーション上で、アニメーションを実行していたりすると、そのアニメーションが止まってしまうらしいです。なので、前に作った DB へのアクセス方法を、 Clarkeman's Weblog を参考にして (翻訳版もこの blog に載せてあります) com.sun.javafx.runtime.async パッケージを利用し、非同期で実行することにしました。ただ、ここまでくると、「簡単に」とは言いがたいですね... Clarkeman も言っていましたが、早く JavaFX が非同期実行を簡単に実装できる方法を用意してくれるといいですね。



CustomerLoadImpl の実装 (Java)


データベースの準備等は、「JavaFX でデータベースに簡単にアクセスできるという事実 - その1」とまったく同じです。そして、Java 側にもうひとつ、Customer クラスへの JavaFX 側から、EDT とは別のスレッドからアクセスするために AbstractAsyncOperation を継承した CustomerLoadImpl クラスを実装します。

package javaapplication1;
import com.sun.javafx.runtime.async.AbstractAsyncOperation;
import com.sun.javafx.runtime.async.AsyncOperationListener;
public class CustomerLoadImpl extends AbstractAsyncOperation {
private Customer customer;
private int Id;
public CustomerLoadImpl(Customer customer, int Id, AsyncOperationListener listener) {
super(listener);
this.customer = customer;
this.Id = Id;
}
@Override
public Object call() throws Exception {
CustomerJpaController test2 = new CustomerJpaController();
this.customer = test2.findCustomer(Id);
return(this.customer);
}
public Customer getCustomer(){
return this.customer;
}
}

このクラスは JavaFX 側から呼び出させ、JPA コントローラを使って Customer クラスのオブジェクトを作成します。実際に呼び出しているのは、JavaFX 側に作成される、 CustomerLoad クラスです (後述)。CustomerLoadImpl クラスのオブジェクトが作成され、継承されている start() メソッドが実行されると、オーバーライドで実装されている call() メソッドが呼び出され、JPA コントローラを使って、入力された Id に対応するCustomer オブジェクトを見つけ出し、CustomerLoadImpl の属性に入れておきます。また、この値は、getCustomer() メソッドで取り出せるようにしておきます。

次に JavaFX 側を作成しましょう。「JavaFX でデータベースに簡単にアクセスできるという事実 - その2」を実行した後に、CustomerLoad クラスを作成します。


public class CustomerLoad extends AbstractAsyncOperation {
var peer: CustomerLoadImpl;
public-init var customer:Customer;
public var onComplete: function(customer: Customer): Void;
public override function cancel() : Void {
if (peer != null) then peer.cancel();
}
protected override function start() : Void {
peer = new CustomerLoadImpl(customer, CustomerId, listener);
peer.start();
}
protected override function onCompletion(value : Object) : Void {
this.customer = peer.getCustomer();
onComplete(customer);
}
}

この CustomerLoad クラスのオブジェクトを作成すると、CustomerLoad オブジェクトのstart() メソッドを非同期で実行してくれます。スレッドの処理が終了したら、onCompletion() メソッドを実行して、スレッドを終了します。ここでは、まず、CustomerLoad クラスのオブジェクトが作成されると、Java 側の CustomerLoadImpl クラスのオブジェクトを作成し、CustomerLoadImple オブジェクトの start() メソッドを実行します。これが、先述のように call() メソッドを呼びます。onComplete() は、CustomerLoad を作成する際に、引数として渡されるメソッドで、取得した Customer クラスを引数としてとり、定義された処理を実行します。

最後に CustomerLoad のオブジェクトをボタンがクリックされたときに、以下のように作成します。


var tmpcustomer = new Customer();
var test = CustomerLoad {
customer: tmpcustomer
onComplete: function(cus: Customer) : Void {
if (cus != null) {
CustomerName = cus.getName();
}
}
}

onComplete には、取得した Customer オブジェクトの getName() メソッドの戻り値 (Customer の名前) を CustomerName に代入するメソッドを渡します。CustomerName は、Text にバインドされていて、値が変わるたびに、その値が表示されるようになっています。

これで、実装は完成で、実際に、DB からも値がとってこれたのですが、果たしてこれで、本当に、非同期で実行されているのでしょうか?これを確認するために、 GUI チュートリアルの、雲のアニメーション を無理やり、このアプリケーションに追加しました。以前の「その1 - その3」を使って作ったアプリケーションでは、ボタンを押して、TopLink にアクセスしたときに、確かに、アニメーションが止まってしまいました。ですが、Async を利用した今回のアプリケーションは、アニメーションは止まらず、動いたままでした。どうやらうまく言っているようですね。

JavaFX のソースは ここ においておきました。

投稿されたコメント:

コメント
コメントは無効になっています。

Valid HTML! Valid CSS!

This is a personal weblog, I do not speak for my employer.