データベースとの連携
Struts 2を用いたアプリケーションでデータベース(RDBMS)を利用するのも、基本的には、通常のJavaアプリケーションと変わりはありません。直接JDBCを使うこともできますし、いわゆるO/Rマッピングフレームワーク(Hibernateなど)を利用することもできます。
また、DIコンテナであるSpringとの連携も可能で、Springを通して、データベースとの接続を行うこともできます。ただ、Springを利用すると、どうしてもXMLファイルでの設定項目が多くなってしまいます。また、ちょっとした処理でも前準備が必要で、ソースコードも長くなってしまいますので、今回は、XML設定が不要な、ActiveObjectsというO/Rマッピングフレームワークのみを用いて、データベース接続の基本を説明することにします。
O/Rマッピングとは、オブジェクト指向でのObjectと、RDB(リレーショナルデータベース)をマッピングするということです。一般的なRDBでは、いわゆるオブジェクト指向言語で、直接データベースの値を操作することができません。そのため、オブジェクトをデータベースのレコードに関連付ける仕組みが必要になってきます。
また、そのような機能をコンポーネント化したものを、O/Rマッピングフレームワークと呼び、Hibernate、iBATIS、JPA(Java Persistence API)などが有名です。
一方、DIコンテナとは、「DI(Dependency Injection:依存性の注入)」と呼ばれる設計思想に沿って作られたコンポーネントを、管理するためのライブラリです。DIとは、コンポーネント間の依存関係をソースコードから排除して、実行時に依存するコードを「注入」するデザインパターンです。代表的なDIコンテナとしては、Spring Frameworkや、Seasarがあります。
サンプルとしては、先ほどのBooks2Action
クラスを少し拡張して、データベースに登録する処理と、登録したデータを一覧表示する処理を追加してみます。
前準備
今回のサンプルでは、ActiveObjectsフレームワークが対応しているデータベースの一つである、MySQLを利用します。MySQLのインストールについては、WINGSプロジェクトのサポートページを参照してください。
まず、データベースとテーブルを作成しておきます。ここではデータベースはbooks、テーブルもbooksという名前にしました。テーブルを作成するSQLスクリプトは、次のようになります。ごく単純に、BooksModel
オブジェクトのフィールドを文字列の項目とし、さらに、ActiveObjectsのデフォルトのプライマリーキーである、数値のIDカラムを追加しています。
CREATE TABLE books ( id INTEGER NOT NULL AUTO_INCREMENT, isbn VARCHAR(14), title VARCHAR(100), author VARCHAR(50), PRIMARY KEY (id) ) ENGINE=InnoDB;
また、ActiveObjects(activeobjects-0.8.2.zip)をダウンロードし、解凍して得られるactiveobjects-0.8.2.jarと、MySQLのコネクタであるmysql-connector-java-5.1.7-bin.jarを、参照ライブラリーにコピーしておきます。
Actionクラス(BooksDBAction.java)
まずは、Actionクラスの変更点を見ていきましょう。
public class BooksDBAction extends ActionSupport implements ScopedModelDriven<BooksModel> { private BooksModel model; // 本情報のModelクラス private String scopeKey; // セッションでのModelオブジェクト識別キー List<BooksModel> books; // 登録した本情報の一時格納用 private String message; // データベース例外メッセージ表示用 @Action( value = "/registerDB", results = { @Result(name = "success", location = "booksRegisterDB.jsp" ) } ) public String register() throws Exception { try{ // DAOオブジェクトの生成 BooksDAO dao = new BooksDAO(); // 入力値のデータベース登録 dao.register(model.getIsbn(), model.getTitle(), model.getAuthor()); // データベースから全登録データを取得 books = dao.getAll(); } catch(SQLException e) { setMessage(e.getMessage()); } return "success"; }
フィールドを2つ追加しています。
booksは、データベースに登録した本の情報を取得し、一時的に格納しておくList
オブジェクトです。最後のデータ一覧画面では、このList
オブジェクトの内容を表示しています。
messageは、データベース処理で例外が発生した場合に、その例外メッセージを保持しておく文字列です。
register
メソッドで、データベースの接続、登録/取得処理を行っています。といっても、Actionクラスでは、BooksDAO
クラスのメソッドを呼んでいるだけです。データベースの実際の処理は、BooksDAO
クラスが行っています。
このような実装方法を、「DAO(Data Access Object)パターン」と呼びます。DAOパターンでは、データベースへのアクセスを専用のクラスでカプセル化します。それにより、データベースに依存する処理を、ビジネスロジックから排除することができます。
なお、通常のDAOパターンでは、処理を抽象化したインターフェイスを定義して、DAOクラスで実装するようにします。そうしておけば、データベースへのアクセス手段や実装が異なるDAOクラスを使う場合でも、公開しているインターフェイスは変わりませんので、ビジネスロジックを変更する必要がありません。
DAOクラス(BooksDAO.java)
では、次にBooksDAO
クラスなのですが、その前に、Booksインターフェイスを説明しておきます。このインターフェイスは、booksテーブルに対応するオブジェクトのインターフェイスで、テーブルの項目に対して、Getter/Setterを定義しているだけです(IDカラムは、既にEntityインターフェイスで定義済み)。
実装は、フレームワークのActiveObjectsが自動で生成してくれます。
public interface Books extends Entity { public String getIsbn(); public void setIsbn(String isbn); public String getTitle(); public void setTitle(String title); public String getAuthor(); public void setAuthor(String author); }
BooksDAO
クラス本体は、次のようになります。
public class BooksDAO { private static EntityManager manager; public BooksDAO(){ manager = new EntityManager("jdbc:mysql://localhost/books", "root", ""); // ホスト/データベース名、ユーザー、パスワードを指定 } // 登録 public void register(String isbn, String title, String author ) throws SQLException{ Books book = manager.create(Books.class); book.setIsbn(isbn); book.setTitle(title); book.setAuthor(author); book.save(); } // 全データ取得 public List<BooksModel> getAll() throws SQLException{ List<BooksModel> b = new ArrayList<BooksModel>(); Books[] books = manager.find(Books.class); for (Books element : books) { BooksModel tmp = new BooksModel(); tmp.setIsbn(element.getIsbn()); tmp.setTitleNoencode(element.getTitle()); tmp.setAuthorNoencode(element.getAuthor()); b.add(tmp); } return b; } }
コンストラクタ
BooksDAO
クラスも、とてもシンプルです。コンストラクタで、net.java.ao.EntityManager
クラスをインスタンス化しています。EntityManager
クラスのコンストラクタで、MySQLのホスト名、データベース名、ユーザー名、パスワードを指定します。サンプルでは、それぞれ、"localhost"、"books"、"root"、なし、としています。
registerメソッド
テーブルへの登録は、EntityManager.create
メソッドでBooks
オブジェクトを生成し、登録したい値をセットしたのち、EntityManager.save
メソッドを呼び出します。
getAllメソッド
テーブルからの参照は、EntityManager.find
メソッドを用います。find
メソッドのパラメータで何も指定しなければ、テーブルからすべてのデータを、配列として取得することができます。
サンプルでは、BooksModel
オブジェクトのListを作り、取得した配列から一つずつデータを取りだして、Listコンテナに追加しています。
なお、ActiveObjectsの詳細については、前述のサイトや、JavaDocを参照してください。
booksRegisterDB.jsp
booksRegisterDB.jspファイルでは、<s:iterator>
タグを用いて、BooksModelのList
オブジェクト全内容の一覧を表示しています。
<s:iterator value="books"> <p> 書名 : <s:property value="title" /><br /> 著者 : <s:property value="author" /><br /> ISBN : <s:property value="isbn" /><br /> </p> </s:iterator>
まとめ
今回は、ActionクラスとViewレイヤーでデータをやりとりする仕組みを解説しました。データベースとの連携も、ActiveObjectsを利用すれば、ごくシンプルに記述できることが分かるかと思います。
次回は、これまで紹介してきた機能をまとめて、より本格的なアプリケーションを作成することにしましょう。