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]はエイリアスなんでなんでもよいでしょう
通常のJavaオブジェクト。newしたヤツ。セッションで管理されていない。対応するロウがテーブルにない。
セッションで管理されている。対応するロウがテーブルにある。
セッションで管理されていない。対応するロウがテーブルにある。セッションを閉じたらこうなる。
一時オブジェクト ---save---> 永続化オブジェクト <-------update---------- 分離オブジェクト 一時オブジェクト <--delete-- 永続化オブジェクト --evict,session.close--> 分離オブジェクト 一時オブジェクト --saveOrUpdate--> 永続化オブジェクト 分離オブジェクト --saveOrUpdate--> 永続化オブジェクト
当たり前の事をまとめてます。。。
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が走った
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文が走っていますね。
ちなみに
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文は走らない
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したあと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
が発生する。
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
<mapping resource="nu/mine/kino/MtAuthor.hbm.xml" />などと指定する
たとえば
<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>
の部分は、コメントに使われるだけ、だと思う。
`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>
なんてのができあがりました。ポイントは複合キーはそのキーのオブジェクトを作る必要がある、ってことですかね。これ、結構めんどくさいね。
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ファイルを上書きしてしまう。
この記事は
現在のアクセス:1085