DATE : 2006/11/18 (Sat)
Hibernate 3.1 までは、データの件数を取得する処理は次のように書けました。(以下は、Cat クラスの全オブジェクトの件数を取得する処理です。)
int catCount = (Integer) session.iterate( "select count(*) from Cat").next();
しかし、Hibernate 3.2 からは、HQL の count 関数は Long 型を返すようになりました。つまり、以下のように書き直さなければなりません。
long catCount = (Long) session.iterate( "select count(*) from Cat").next();
この変更は、Java Persistence API に準拠するために行われたようです。そのため、Hibernate 3.2 以降で結果を Integer で取得しようとすると ClassCastException が発生します。
ただし、org.hibernate.cfg.Configuration を次のように設定すると、3.2 以降でも Integer で取り出すことができます。
configuration.addSqlFunction("count", new ClassicCountFunction());
なお、count 関数以外にも、sum や avg なども戻り値が変更されています。sum は、元々は合計された型そのものが返されていましたが、
- Long 以下の整数型であれば Long
- 浮動小数点数であれば Double
- BigInteger や BigDecimal であればその型
と変わりました。また、avg は フィールドの型に依存していたものが、Double が返されるように変わりました。
sum や avg も含めて 3.1 以前の書き方を適用する場合は、次のように Configuration を設定します。
configuration.addSqlFunction("count", new ClassicCountFunction()); configuration.addSqlFunction("avg", new ClassicAvgFunction()); configuration.addSqlFunction("sum", new ClassicSumFunction());
ちなみに今のところ、Hibernate 3.2.0ga のドキュメントは3.1以前の方法で書かれているので注意が必要です。
(;^ω^)見事にひっかかりました
参考文献
- Hibernate3 Migration Guide - Hibernate 3.2 - Query language changes - Changed aggregation (count, sum, avg) function return types
- [HHH-1538] aggregations functions in EJBQL queries does not return the appropriate types - Hibernate JIRA
DATE : 2006/11/16 (Thu)
処理の中で System.out.println などの標準出力への出力を行うメソッドをテストすると、テスト中に出力がコンソールに次々と流れ、テスト結果が埋もれてしまうことがあります。
そこで、標準出力を他の出力ストリームに割り当て直します。
割り当て直すには、java.lang.System#setOut(java.io.PrintStream) を使います。java.io.PrintStream はコンストラクタの一部に java.io.OutputStream をとるものがあります。そこで、OutputStream として java.io.ByteArrayOutputStream を割り当てます。ByteArrayOutputStream は、ストリームに書き込まれたバイト列を byte 配列に書き込む出力ストリームです。他の出力ストリームと比べて手頃なので、このストリームを選択しました。
ByteArrayOutputStream を標準出力に割り当てるコードは次のようになります。
System.setOut(new PrintStream(new ByteArrayOutputStream()));
このコードを、 JUnit の場合は setUp メソッド(もしくは @Before アノテーションの着いたメソッド)に記述すれば、そのテストクラスでの標準出力への出力を抑制することができます。
また、ByteArrayOutputStream オブジェクトを保持しておけば、ByteArrayOutputStream#toString() で出力された文字列を取り出すこともできます。標準出力に出力する文字列もテストする場合は、ByteArrayOutputStream オブジェクトを保持しておくと良いでしょう。
なお、標準エラー出力の場合も同様です。
System.setErr(new PrintStream(new ByteArrayOutputStream()));
DATE : 2006/11/15 (Wed)
java.lang.String#getBytes(String) メソッドを使うと、文字列を特定のエンコーディングに変換することができます。例えば、文字列 string を UTF-8 に変換するコードは次のようになります。
byte[] utf8 = string.getBytes("UTF8");
それに対して、java.nio.Charset などの New I/O を用いて文字列 string を UTF-8 に変換する場合は、次のようになります(ここでは、java.nio.Charset を使用した場合を示します)。
ByteBuffer utf8 = Charset.forName("UTF-8").encode(string);
byte 配列ではなく java.nio.ByteBuffer として返されるので、New I/O を使う際にはこちらを使った方が見通しが良くなります。
ただし、その結果返された ByteBuffer を操作する際には注意が必要です。
ByteBuffer は固定長のバイト列を表すオブジェクトで、バイト長を表す要素として「容量」と「リミット」があります。前者は ByteBuffer そのものの長さ、後者はバッファの読み書きを禁止し始める位置を表します。また、容量は ByteBuffer#capacity() で、リミットは ByteBuffer#limit() で取り出すことができます。
ここで、Charset#encode(String) で変換されたバイト長を表すのはリミットです。Charset#encode(String) で生成された ByteBuffer なので一見すると容量イコール変換されたバイト長のように思えます。しかし、変換されたバイト列を調べてみると、リミット以降はただ単に0が格納されていました。
DATE : 2006/11/13 (Mon)
PostgreSQL ではセキュリティ上の観点から、PostgreSQLサーバアプリケーションを管理者アカウントから実行できないようになっています。サーバアプリケーションの中にはデータベースクラスタを初期化する initdb が含まれており、初期化するには非管理者アカウントが必要になります。
しかし、Windows 版 PostgreSQL のインストーラはインストール時に PostgreSQL 用のアカウントを作っています。ログオン画面には表示されませんが、コマンドプロンプトから次のコマンドを入力すると、確かに作られていることが分かります。(「>」はプロンプトを表します)
> NET USER
すると、このアカウントで initdb を実行すればデータベースクラスタを初期化できそうです。
インストール時に作成したアカウント(ここでは「postgres」という名前とします)で initdb を実行するには、方法が2通りあります。
- コマンドプロンプトのショートカットを右クリック→「別ユーザとして実行(A)...」で postgres としてのコマンドプロンプトを開く
- コマンドプロンプトで、次のように RUNAS コマンドを実行して postgres としてのコマンドプロンプトを開く(直接 initdb を実行することもできます)
> RUNAS /user:postgres cmd
ただし、上のように開いたコマンドプロンプトでは、PostgreSQL へのパスは通っていません。そのため、まずパスを設定するか、PostgreSQL の bin フォルダをカレントフォルダにしなければなりません。
以上で別アカウントで initdb を使用できるようになりました。
なお、データベースクラスタを構築するフォルダがすでにある場合はあらかじめ消しておき、initdb に作らせるようにします。そうしないと、フォルダに権限がないという理由で initdb の実行が失敗してしまいます。
参考文献
関連記事
(;^ω^)この記事の中でも似たようなことを書いているのですが、PostgreSQL をインストールするたびにこの部分でハマってしまうので、あえて再び取り上げました
DATE : 2006/11/12 (Sun)
Java Persistence API(JPA)を使用すると、Java Beans とデータベースとを比較的容易に対応付けることができます。
例えば、あるクラスの code プロパティを主キーとして対応付ける場合は、次のようになります(この場合、対応付けられるのはデータベースの code 列になります)。(「...」は省略を表します)
@Id @Column public Integer getId() { ... } public void setId(Integer id) { ... }
また、主キーの値がデータベースによって自動生成される場合は、次のように指定します。
@Id @Column @GeneratedValue public Integer getId() { ... } ...
この GeneratedValue アノテーションには strategy という要素があります。この要素によって、主キーの値の生成方式を指定できます。
現在の Java Persistence API では、以下の方式が定義されています。
- GenerationType.IDENTITY
- データベースが識別番号を生成します。データベースやフレームワークが IDENTITY 型を定義している必要があります。
- GenerationType.SEQUENCE
- データベースが連番を生成します。データベースやフレームワークが連番型を定義している必要があります。
- GenerationType.TABLE
- 連番の状態を特定のテーブルに保存することで、識別番号を生成します。
- GenerationType.AUTO
- Java Persistence API を提供するライブラリやフレームワークが、自動的に方式を判断します。
PostgreSQL では、連番型を表す serial 型が定義されています。そのため、serial 型の主キーにプロパティを対応付ける場合、Generation.SEQUENCE を指定しがちです。
@Id @Column @GeneratedValue(stragegy = GenerationValue.SEQUENCE) public Integer getId() { .... }
しかし実際には、Generation.IDENTITY を指定しなければ正しく動作しません。
@Id @Column @GeneratedValue(stragegy = GenerationValue.IDENTITY) public Integer getId() { ... }
なぜ SEQUENCE ではなく IDENTITY なのかは、勉強不足のため正確なところはわかりません。
ただ、PostgreSQL の連番型は、連番を生成するジェネレータから出力される値が連番であるだけで、主キー列が必ずしも連番になるとは限りません(例えば、トランザクションがアボートされた場合、ジェネレータの連番は元に戻りません。つまり、主キーの連番が1つ空くことになります)。
このあたりが、PostgreSQL の連番型には SEQUENCE ではなく IDENTITY を指定する理由なのではないかと思います。
参考文献
- Using Primary Keys with Java Persistence
- PostgreSQL 8.1.5文書 8.1.4. 連番型
- PostgreSQL 8.1.5文書 9.12. シーケンス操作関数
- Sun Java System Application Server Platform Edition 9 Developer's Guide - 7. Using the Java Persistence API - Database Restrictions and Optimizations - Using a SERIAL Field in PostgreSQL
- Hibernate Forums "ID in abstract class, with entity-specific sequence name" GenerationType.AUTO を指定した場合の方法が書かれています