リレーショナルデータベース(Relational Database = RDB)のデータを、オブジェクト指向プログラミング言語で表すことはなかなか困難なことです。RDBとオブジェクト指向プログラミング言語では、そもそもデータの表現方法がまったく違っています。例えば、RDBの主キーをオブジェクト指向言語でどう表せばよいのでしょう?また、オブジェクト指向言語での継承の概念は、RDBではどうなるのでしょう?こうした違いは「インピーダンス・ミスマッチ」と呼ばれています。
インピーダンス・ミスマッチのひとつの解決法として、データもオブジェクト指向データベースで管理してみる、というのがあります。オブジェクト指向プログラミング言語を使えば、インピーダンス・ミスマッチが解消されるのではないか、と考えられるでしょう。
しかし、オブジェクト指向データベースは、なかなか普及しません。オブジェクト指向データベースと比べると、RDBには次のような特徴があります。
このような理由から、データベース管理システムにはRDBが使われることが多いのです。
では、インピーダンス・ミスマッチはどうすればよいのでしょう?
ここでは、JavaからRDBを使う手法について概観してみましょう。
まず、いちばん基本的な手法はJDBCです。
Connection con = ......; String select = "select title from books"; Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(select); while (rs.next()) { String title = rs.getString("title"); }
JDBCは、SQLをJavaプログラムでラッピングすることで、JavaプログラムからRDBにアクセスするための技術です。もともとSQLですから、手続き型のプログラミングスタイルであり、オブジェクト指向的ではありません。
J2EEパターンのひとつに「Data Access Object (DAO)パターン」というものがあります。これは、永続ストレージにあるデータにアクセスするためのプログラムをひとまとめにしておくというパターンです。永続ストレージとは、データをメモリ上ではなく、ディスク上に管理しているもののことを指します。リレーショナルデータベース、LDAP、XML、テキストファイルなどが永続ストレージに挙げられます。
BookDAO.javaのソースコードです。
import java.util.Collection; import java.util.List; import java.util.ArrayList; import java.sql.DriverManager; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class BookDAO { private String url = "jdbc:hsqldb:hsql://localhost"; public BookDAO() { try { Class.forName("org.hsqldb.jdbcDriver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public Collection<Book> searchBooksByTitle(String title) { List<Book> list = new ArrayList<Book>(); try { // Connectionの取得 Connection con = DriverManager.getConnection(url, "sa", ""); // SQLの実行 String selectStatement = "select * from books where title like ?"; PreparedStatement prepStmt = con.prepareStatement(selectStatement); prepStmt.setString(1, "%" + title + "%"); ResultSet rs = prepStmt.executeQuery(); // SQLの実行結果の処理 while (rs.next()) { Book book = new Book(); book.setNdc(rs.getString("ndc")); book.setTyosya_hyouji(rs.getString("tyosya_hyouji")); book.setId(rs.getInt("id")); book.setTitle(rs.getString("title")); book.setAuthor(rs.getString("author")); book.setPublisher(rs.getString("publisher")); list.add(book); } // 後始末 rs.close(); prepStmt.close(); con.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } }
次に、クライアントプログラムです。
import java.util.Collection; public class JDBCTest { public static void main(String args[]) { BookDAO bookDao = new BookDAO(); Collection<Book> result1 = bookDao.searchBooksByTitle(args[0]); for (Book book: result1) { System.out.println(book.getTitle()); } System.out.println("-----"); Collection<Book> result2 = bookDao.searchBooksByAuthor(args[1]); for (Book book: result2) { System.out.println(book.getAuthor()); } } }
DAOのメリットとは何なのでしょうか?
DAOでは、リレーショナルデータベースにアクセスするためのコードがひとつのクラスにまとめられています。その結果、JDBCのコードがDAOの中に隠蔽されています。DAOを利用するプログラマは、DAOがどのように実装されているのか知る必要はありません。
DAOでは、データベースにアクセスするコードが局所化されており、オブジェクト指向的であると言えます。
インピーダンス・ミスマッチを解消させつつ、POJOを永続化させることを、「Object Relational Mapping (O/R Mapping)」といいます。"POJO"とは「Plain Old Java Object」の略で、特定のソフトウェアへの依存性がない通常のJavaオブジェクトのことを指します。
O/R Mappingでは、RDBのデータとPOJOを直接対応させます。そして、オブジェクトに対する生成、読み込み、更新、削除といった操作を、RDBに対する操作と関連づけるのです。このとき、通常SQLは使用しません。こうした生成、読み込み、更新、削除といった操作のことを、一般にCRUD (Create / Read / Update / Delete)といいます。
また、O/R Mappingをおこなうソフトウェアでは、SQLよりも便利な統一的なクエリ言語を用意しているのが普通です。
そして、O/R Mappingのソフトウェアでは、JDBCのコードは登場しません。
こうしたO/R Mappingのソフトウェアには次のようなものがあります。
このように、O/R Mappingをおこなうソフトウェアにはさまざまなものがありますが、Java EE 5のリリースに伴い、Java EE公式のO/R MappingのAPIが登場しました。
それが「Java Persistence API」です。
Java Persistence APIは、HibernateやToplinkなどの評価が高いO/R Mappingのソフトウェアをもとに、JSR 220で議論されてきました。Java EE 5では、EJB 3.0 Entity Beanで採用されています。また将来的には、J2SE環境下でも利用可能になる予定です。現在でも、HibernateでJava Persistence APIを利用することができます(Hibernate Annotations / Hibernate EntityManager)。
このように、さまざまなO/R Mappingのソフトウェアで、Java Persistence APIが採用されつつあります。Java Persistence APIを使ってみると、オブジェクト指向らしいプログラムを素直に記述できるようになります。今後はもしかすると、JDBCに代わって、RDBにアクセスするための標準APIの地位を占めるかもしれません。