基本操作 †HQLでクエリを発行する †SessionFactory sessionfactory = config.buildSessionFactory();
session = sessionfactory.openSession();
List list = session.find("from UserId as userid where userid.userId like 'Masatomi%'");
↑ 結果(UserIdクラス)の格納されたListが返ってくる
Hibernate3.0から上のfindはdeprecatedになりました。3.0からは Query query = session.createQuery
("from UserId as userid where userid.userId like 'Masatomi%'");
List messages = query.list();
としてください。 HQLの文法などはいろんなサイトで説明されているので割愛。上の意味だけ。 from UserId[1] as userid[2] where userid[2].userId[3] like 'Masatomi%' ↑ 意味は、[1]のクラスを検索します。条件は[3]のフィールドが'Masatomi%'であるようなレコード ↑ [2]はエイリアスなんでなんでもよいでしょう Hibernateの一時オブジェクト、永続化オブジェクト、分離オブジェクト †一時オブジェクト †通常のJavaオブジェクト。newしたヤツ。セッションで管理されていない。対応するロウがテーブルにない。 永続化オブジェクト †セッションで管理されている。対応するロウがテーブルにある。 分離オブジェクト †セッションで管理されていない。対応するロウがテーブルにある。セッションを閉じたらこうなる。 各状態への遷移方法 †一時オブジェクト ---save---> 永続化オブジェクト <-------update---------- 分離オブジェクト 一時オブジェクト <--delete-- 永続化オブジェクト --evict,session.close--> 分離オブジェクト 一時オブジェクト --saveOrUpdate--> 永続化オブジェクト 分離オブジェクト --saveOrUpdate--> 永続化オブジェクト デバッグ張って、ためしてみた †当たり前の事をまとめてます。。。 分離オブジェクトをsaveするとinsert文が走った †final SessionFactory sessionFactory = getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setName("kino");
session.save(user);
System.out.println(user);
tx.commit(); <-この処理後、insertが走る
session.flush();
session.close();
System.out.println(user);
session = sessionFactory.openSession();
tx = session.beginTransaction();
session.save(user); さっきの分離オブジェクトをsaveしてる
// session.update(user);
// user.setName("hogehoge");
tx.commit(); <-この処理後、insertが走る(updateではない)
session.flush();
session.close();
分離オブジェクトをsaveすると、insertが走った 分離オブジェクトをupdateするとupdate文が走った(永続化オブジェクトにする、という意味でupdate文が走ってる) †final SessionFactory sessionFactory = getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setName("kino");
session.save(user);
System.out.println(user);
tx.commit(); <-この処理後、insertが走る
session.flush();
session.close();
System.out.println(user);
session = sessionFactory.openSession();
tx = session.beginTransaction();
//session.save(user);
session.update(user); さっきの分離オブジェクトをupdateしてる
user.setName("hogehoge");
tx.commit(); <-この処理後、updateが走る。setNameしてるけど、
上のupdateと併せて一回だけupdateが走る
(setNameしなくても一回はupdateが走った)
session.flush();
session.close();
分離オブジェクトをupdateすると、update文が走った。ただこれは分離オブジェクトをふたたび永続化オブジェクトにしただけ。SQLのupdate文とはちょっと意味が違う。ちなみにupdate文を発行しないで永続化オブジェクトにするには session = sessionFactory.openSession();
tx = session.beginTransaction();
// session.save(user);
// session.update(user);
session.lock(user, LockMode.NONE); <-これ
// user.setName("hogehoge");
tx.commit();
session.flush();
session.close();
session.lockを使用します。上の例だとupdate文は走らないけど永続化されてるみたい。試しにuser.setName("hogehoge");をアンコメントすると、tx.commitでupdate文が発行された。lockメソッド自体で新しいセッションに結びつけられたことによって、setNameとかするだけでupdate文が走っていますね。 分離オブジェクトをupdateしないで変更してもupdate文は走らない †ちなみに session = sessionFactory.openSession();
tx = session.beginTransaction();
// session.save(user);
// session.update(user);
// session.lock(user, LockMode.NONE);
user.setName("hogehoge");
tx.commit();
session.flush();
session.close();
この場合userは分離オブジェクトなので、これではupdate文は走らない その他 †すでに永続化オブジェクトがセッション中にロードされている状態で、更にUpdateしようとすると例外。 †org.hibernate.NonUniqueObjectException?: a different object with the same identifier value was already associated with the session という例外が発生しますね。 Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setName("hogehoge");
session.save(user);
System.out.println(user);
int num = user.getId();
user = new User();
user.setId(num);
user.setName("name");
session.update(user); <-ここで例外
System.out.println(user);
tx.commit();
session.flush();
session.close();
同じ識別子の値で別のオブジェクトがすでに関連付いている、、と。といってもsaveしたuserをevictして分離オブジェクトにすればOKかな?と思ったけど別の例外(org.hibernate.AssertionFailure?: possible nonthreadsafe access to session)がtx.commit時に発生しちゃいます。そもそも一時オブジェクトをupdateすること自体間違ってるのかな? あ、flushしてからevictしなくちゃいけないんですね。 Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setName("hogehoge");
session.save(user);
session.flush();
session.evict(user);
int num = user.getId();
user = new User();
user.setId(num);
user.setName("name");
session.update(user); <-例外は発生しなくなる
System.out.println(user);
tx.commit();
session.flush();
session.close();
これでOKでした。 saveしたオブジェクトをすぐevictするとエラー †上のようにsaveしたあとflushしてからevictしないと、 org.hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session) org.hibernate.AssertionFailure: possible nonthreadsafe access to session が発生する。 selectしてきたオブジェクトにはCGLIBで拡張されている †newしてsaveしたオブジェクトは普通のクラス。session.loadなどしてselectしたクラスはCGLIBで拡張されたクラスになっている。 複数の設定ファイルを使用したい †hibernate.cfg.xmlはどうもひとつのデータベース設定しか書けないみたい(たぶん)。 でも場合によっては一つのデータベースだけではなく、複数のデータベースに接続することがあります。そのときに、複数の設定ファイルを切り替える方法がようやく分かりました。ちなみにTorqueで試行錯誤した経緯はTorque/複数のDBで同時に利用するをご参照。 Configuration config = new Configuration();
// config = config.configure(); // <-通常の設定の読み込み
config = config.configure(new File(
getServlet().getServletContext().getRealPath(
"/WEB-INF/lib/hibernate.cfg.hoge.xml")));
このようにすることで、ランタイムに設定ファイルを指定することが出来るようです。。 結合(関連)を考える †
JavaBeans?(ここではMember)にプロパティを追加してマッピングファイル(Member.hbm.xml)に <many-to-one name ="workgroup" column="GROUPNO" class="nu.mine.kino.binding.ait.hibernate.Workgroup" cascade="all" outer-join="auto" update="false" insert="false" /> を追加すればよいようだ。追加しない場合はMember#getWorkgroup()の返り値がnullになるみたい。 Member(N) -> WorkGroup?(1) なので、many-to-oneとなる
JavaBeans?(ここではWorkGroup?)にプロパティを追加してマッピングファイル(WorkGroup?.hbm.xml)に <set name="memberList" > <key ><column name="groupno" /></key> <one-to-many class="nu.mine.kino.binding.ait.hibernate.Member" /> </set> を追加すればよいようだ。追加しない場合はWorkgroup#getMemberList?()の返り値がnullになるみたい。 WorkGroup?(1) -> Member(N) なので、one-to-manyとなる クラス設計で考えるとWorkGroup? -> Member はあってもその逆はあまりないかなぁ。ようするに aWorkGroup.getMembers() はあっても aMember.getWorkGroup() はないことが多いように思うけど。。。 http://www.atmarkit.co.jp/fjava/rensai3/ormap05/ormap05_1.html WEBアプリの時、設定ファイルなどはどこに置くべきか。 †
middlegenで作ったhbmファイルについて †たとえば <id name="id" type="java.lang.Integer" column="id" >
<meta attribute="field-description">
@hibernate.id
generator-class="Increment"
type="java.lang.Integer"
column="id"
</meta>
<generator class="Increment" />
</id>
の <meta attribute="field-description">
@hibernate.id
generator-class="Increment"
type="java.lang.Integer"
column="id"
</meta>
の部分は、コメントに使われるだけ、だと思う。 MySQLのauto_incrementについて †`name_id` int(10) unsigned NOT NULL auto_increment, と定義されるカラムがあるテーブルについて、middlegenでJavaBeans?とhbm.xmlファイルを作成したら以下のようになりました。 <id
name="nameId"
type="java.lang.Object"
column="name_id"
>
<meta attribute="field-description">
@hibernate.id
generator-class="assigned"
type="java.lang.Object"
column="name_id"
</meta>
<generator class="assigned" />
</id>
実際はIntegerとマッピングして欲しいのですが、うまくいかないですね。とりあえず自分でjava.lang.Integerに変更しました。あと <generator class="assigned" /> を <generator class="increment" /> に変更しました。もちろんJavaソースも作り直しました。 複合キーを使ったテーブルのマッピング。 †主キーが複数、たとえば create table Customer3 (customer_id varchar(20) not null, item_id varchar(20) not null, value varchar(255), upd_date timestamp, primary key (customer_id, item_id)); というようにcustomer_id,item_idでユニークになるテーブルのマッピングについてです。 XDocletなどを使ってhbm.xmlを作る場合がほとんどだと思いますので、まずはPOJOから。結論を言うと、上の複合キーを一つのオブジェクトとするために一つPOJOを作ります。 public class Customer3Id implements Serializable {
private String customerId;
private String itemId;
public Customer3Id() {
}
/**
* @hibernate.property column = "customer_id" not-null = "true" length ="20"
*/
public String getCustomerId() {
return customerId;
}
/**
* @hibernate.property column = "item_id" not-null = "true" length = "20"
*/
public String getItemId() {
return itemId;
}
public String toString(){...}
public boolean equals(Object obj) {...}
public int hashCode() {...}
}
どうも、
で、実際のレコードの方(?)のPOJOはさっきの複合オブジェクトを保持しておきます。 /**
* @hibernate.class table = customer3
*/
public class Customer3 {
private Customer3Id compositeId;
....
/**
* @return compositeId を戻します。
* @hibernate.id generator-class="assigned"
*/
public Customer3Id getCompositeId() {
return compositeId;
}
}
XDocletでhbmを生成したところ、 <?xml version="1.0" encoding="UTF-8"?>
<hibernate-mapping>
<class
name="nu.mine.kino.plugin.hsqldb.hibernate.Customer3" >
<composite-id name="compositeId"
class="nu.mine.kino.plugin.hsqldb.hibernate.Customer3Id">
<key-property
name="customerId"
type="java.lang.String"
column="customer_id"
length="20"
/>
<key-property
name="itemId"
type="java.lang.String"
column="item_id"
length="20"
/>
</composite-id>
<property
name="value"
....................
</class>
</hibernate-mapping>
なんてのができあがりました。ポイントは複合キーはそのキーのオブジェクトを作る必要がある、ってことですかね。これ、結構めんどくさいね。 middlegenおぼえがき †http://boss.bekk.no/boss/middlegen/ samples/build.xmlを修正し流用します。まずは <!ENTITY database SYSTEM "file:./config/database/hsqldb.xml" > を実際に使用するデータベース用のファイルに変更します。ほかには name: javaクラスのパッケージ名に対応 database.name :これも変えるのかな? などを変更します。また上で指定した file:./config/database/XXXXX.xml の内容を、個々人の環境に応じて書き換えます。たとえばこんな感じ: <property name="database.url" value="jdbc:sybase:Tds:[IP]:[PORT]/[DB]"/> <property name="database.userid" value="[ID]"/> <property name="database.password" value="[PASS]"/> <property name="database.schema" value="dbo"/> <-おもにこれ? <property name="database.catalog" value="XXXXXX"/> use XXXXXXとかするやつ で、ant hbm2javaでOK!*1。 ちなみに、自動生成されたxmlファイルを手で修正してJavaを生成したい場合は、hbm2javaタスクのdependをなくしてhbm2java単体で稼動するようにすること。そうしないとmiddlegenタスクとかが動いてxmlファイルを上書きしてしまう。 この記事は
現在のアクセス:2456 |