hibernate 3.3で復習 一対多関係
hibernate3.3
hibernate3.3インストール
大型のJARが1つあるのではなく、現在では粒度の細かいJARが多数存在する。これにより、ユーザーの必要な構成に合わせて最小化できる。
ダウンロードはここ。
hibernate3.3での変更
heibernate3.3からは、Sessionのconnectionメソッドを使ってJDBCのコネクションを取得することが非推奨になっていて
sess.connection().commit();
のようにしてトランザクションをコミットできない。
慣例的な書き方として、
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// 何らかの作業を行なう
...
tx.commit();
}
catch (Exception e) {
if (tx!=null) tx.rollback();
throw e;
}
finally {
sess.close();
}
のような書き方がある。
テスト用データベースの用意
データベース
親のデータベースとしてstaffを、子のデータベースとしてstaffroleを用意した。
CREATE TABLE staff ( id serial NOT NULL, staff_name character varying(30), staff_password character varying(20), email character varying(30), CONSTRAINT staff_pkey PRIMARY KEY (id) ) WITHOUT OIDS; ALTER TABLE staff OWNER TO chikkun; CREATE TABLE staffrole ( id serial NOT NULL, staff_name character varying(30), staff_role character varying(20), staff_id integer, CONSTRAINT staffrole_pkey PRIMARY KEY (id) ) WITHOUT OIDS; ALTER TABLE staffrole OWNER TO chikkun;
データマッピング
この2つのデータベースのhibernateのマッピングは次のようになる。
Staff
Staff.hbm.xml
<?xml version="1.0" encoding="Windows-31J"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" \
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class lazy="true" table="staff" name="com.chikkun.tany.database.Staff">
<id unsaved-value="null" name="id" type="java.lang.Integer" column="id">
<generator class="sequence">
<param name="sequence">staff_id_seq</param>
</generator>
</id>
<property name="staffName" length="64" column="staff_name" unique="false" not-null="true"/>
<property name="staffPassword" length="64" column="staff_password" unique="false" not-null="true"/>
<property name="email" length="256" column="email" unique="false" not-null="false"/>
<set inverse="false" cascade="all-delete-orphan" order-by="staff_id asc" lazy="false" name="staffrole">
<key column="staff_id"/>
<one-to-many class="com.chikkun.tany.database.Staffrole"/>
</set>
</class>
</hibernate-mapping>
Staffrole
Staffrole.hbm.xml
<?xml version="1.0" encoding="Windows-31J"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" \
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class table="staffrole" name="com.chikkun.tany.database.Staffrole">
<id unsaved-value="null" name="id" type="java.lang.Integer" column="id">
<generator class="sequence">
<param name="sequence">staffrole_id_seq</param>
</generator>
</id>
<property name="staffName" length="64" column="staff_name" unique="false" not-null="true"/>
<property name="staffRole" length="64" column="staff_role" unique="false" not-null="true"/>
<many-to-one insert="true" column="staff_id" cascade="none" update="true" outer-join="auto" class="com.chikkun.tany.database.Staff" \
lazy="false" name="staff"/>
</class>
</hibernate-mapping>
2つのクラスの、cascade、inverse、outer-joinなどは変更ながらテストを行う。
one-to-many
重複行の削除
複数のロールを持つスタッフなど、子を複数持つ親の検索の場合、親が複数Listに入って返ってきてしまう場合がある。
たとえば、
Query query = session.createQuery( "select staff from Staff as staff, Staffrole as role " +
" where role.staffName = staff.staffName AND role.staffName = 'tanaka'");
List list = query.list();
for(int i = 0; i < list.size(); i++){
Staff st = (Staff)list.get(i);
System.out.println("id:" + st.getId());
System.out.println("name:" + st.getStaffName());
System.out.println("\n");
}
で、Staffroleを2つ持っている場合は、
[java] id:11
[java] name:tanaka
[java]
[java]
[java] id:11
[java] name:tanaka
[java]
[java]
のように、同じStaffが返ってきてしまう。
これを防ぐには、重複行の削除を行うDISTINCTをつけて検索する。
session.createQuery( "select distinct staff from Staff as staff, Staffrole as role " +
" where role.staffName = staff.staffName AND role.staffName = 'tanaka'");
すると、
[java] id:11
[java] name:tanaka
[java]
[java]
のように、ひとつのStaffが返ってくる。
cascade、inverseのテスト
cascade
cascadeは、親側のset,bag,map等や、子のmany-to-oneにつける。
親側のinverseはfalse、cascadeは親がall-delete-orphan、子がnoneでは親側からの登録、削除、子の削除とも通常の動作だった。子のcascadeをallに変更して子の削除(子をSetなどからはずしてのアップデート)を行うと、親とその子のすべてが削除された。
親のcascadeをnoneにして、子の削除(子をデリート)の場合は、その子と親だけが削除されて、その他の子は残っていたが、staff_idはnullになっていた。(※1)
子のcascadeがallの場合、一つのStaffの子のStaffroleを全て変更して、Staffroleからのupdateをかけた場合でもすべてのStaffroleが更新された。
StaffDAO sdao = new StaffDAO();
Staff st = sdao.findById(1);
StaffRoleDAO dao = new StaffRoleDAO();
Staffrole sf = null;
for(Iterator ir = st.getStaffrole().iterator(); ir.hasNext();){
Staffrole sf = (Staffrole)ir.next();
sf.setStaffrole("update");
}
dao.saveOrUpdate(sf);
inverse
inverseは、setなど親側につける。
親側のinverseをtrueにしてテスト、cascadeは親がall-delete-orphan、子がnoneでは親側からの登録、削除、子の削除とも同じく通常の動作だった。
子のcascadeがallでもinverse=false時と同じく、登録削除は同じ動作で、子の削除を行うと、親、その他の子のすべてが削除された。
Staff st = new Staff();
Set set = new HashSet();
StaffRoleDAO sf = new StaffRoleDAO();
for(int i = 0; i < roles.length ; i++){
id = new Integer(roles[i]);
Staffrole role = new Staffrole(id);
role.setStaffName(st.getStaffName());
set.add(role);
}
st.setStaffrole(set);
dao.saveOrUpdate(st);
上記を実行したときに、inverse=falseのときは、StaffroleのstaffIdがデータベースに登録されたが、inverse=trueのときは、staffIdがnullであった。※2
Staff st = new Staff();
StaffRoleDAO sf = new StaffRoleDAO();
for(int i = 0; i < roles.length ; i++){
id = new Integer(roles[i]);
Staffrole role = new Staffrole(id);
role.setStaff(st);
}
dao.saveOrUpdate(st);
上記の場合には、inverseに関係なくstaffだけしかDBに保存されなかった。
※1のときinverse=trueの場合は、staff_idが残っていた。
SQLの違い
Set deleteSet = new HashSet();
for(Iterator ir = st.getStaffrole().iterator(); ir.hasNext();){
Staffrole sf = (Staffrole)ir.next();
System.out.println("st:" + sf.getStaffRole() + " target:" + role.getStaffRole() + ":");
if(sf.getStaffRole().equals(role.getStaffRole())){
deleteSet.add(sf);
System.out.println("delete:" + role.getStaffRole());
}
}
st.getStaffrole().removeAll(deleteSet);
dao.update(st);
上記のようにして、親テーブルstaffから子テーブルstaffroleを一つ削除したときに発行されるSQLは、staffがStaffroleを3つ持っていて、inverse=falseのとき
[java] Hibernate: update staff set staff_name=?, staff_password=?, email=?
where id=?
[java] Hibernate: update staffrole set staff_name=?, staff_role=?, staff_id
=? where id=?
[java] Hibernate: update staffrole set staff_name=?, staff_role=?, staff_id
=? where id=?
[java] Hibernate: update staffrole set staff_id=null where staff_id=? and i
d=?
[java] Hibernate: delete from staffrole where id=?
staffがinverse=trueのとき
[java] Hibernate: update staff set staff_name=?, staff_password=?, email=?
where id=?
[java] Hibernate: update staffrole set staff_name=?, staff_role=?, staff_id
=? where id=?
[java] Hibernate: update staffrole set staff_name=?, staff_role=?, staff_id
=? where id=?
[java] Hibernate: delete from staffrole where id=?
となった。結果としてstaffroleが一つ削除されたというところは同じだったが、発行されたSQLに違いがあり、inverse=trueのほうが動作としては軽くなりそうだった。
staff、staffroleの追加のときも、
StaffDAO dao = new StaffDAO();
if(roles != null){
Set set = new HashSet();
StaffRoleDAO sf = new StaffRoleDAO();
for(int i = 0; i < roles.length ; i++){
id = new Integer(roles[i]);
Staffrole role = new Staffrole(id);
role.setStaff(st);
role.setStaffName(st.getStaffName());
set.add(role);
}
st.setStaffrole(set);
}
dao.saveOrUpdate(st);
staffがinverse=falseのとき、
[java] Hibernate: select nextval ('staff_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: insert into staff (staff_name, staff_password, email, id)
values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
[java] Hibernate: update staffrole set staff_id=? where id=?
[java] Hibernate: update staffrole set staff_id=? where id=?
[java] Hibernate: update staffrole set staff_id=? where id=?
とinsertのあとにupdateが行われているが、inverse=trueのときは
[java] Hibernate: select nextval ('staff_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: insert into staff (staff_name, staff_password, email, id)
values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
insertだけであった。
これは、inverse=falseの場合は、主導権が親テーブルであるStaffにあるが、Staffがinsertされた時点では、Staffは自分の子供のstaffroleを知らないからである。
一般的には、無駄なSQL文を発行せず、パフォーマンスを向上するために、inverse="true"を設定することが薦められる。
ただし、※2の場合のときのようにinverse=trueの場合はstaffroleのアップデートが行われないために、staff_idが入らないというようなことも起きる。
子側であるStaffroleから登録を行った場合、(staffroleのcascadeをallにする)
Staffrole role = null;
Set set = new HashSet();
StaffRoleDAO dao = new StaffRoleDAO();
for(int i = 0; i < roles.length ; i++){
id = new Integer(roles[i]);
role = new Staffrole(id);
role.setStaff(st);
role.setStaffName(st.getStaffName());
set.add(role);
}
st.setStaffrole(set);
dao.saveOrUpdate(role);
inverse=falseでStaffroleが2つのとき
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: select nextval ('staff_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: insert into staff (staff_name, staff_password, email, id)
values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
[java] Hibernate: update staffrole set staff_id=? where id=?
[java] Hibernate: update staffrole set staff_id=? where id=?
inverse=trueのとき
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: select nextval ('staff_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: insert into staff (staff_name, staff_password, email, id)
values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, staff_role, staff_id,
id) values (?, ?, ?, ?)
シーケンスの番号の検索の順番以外には、SQLに変わりは見られなかった。
outer-join
outer-joinプロパティーは、many-to-oneと、set、map、bagなどにつける。
設定できる値は、true、false、autoでautoを使う場合は、外部結合を使うかを自動的に判定するが、hibernate.cfg.xmlに、
<property name="use_outer_join">true</property>
のように指定が必要となる。
Integer id = 1;
StaffRoleDAO rdao = new StaffRoleDAO();
Staffrole sf = rdao.findById(id);
System.out.println("staff name:" + sf.getStaff().getStaffName());
上記のプログラムのとき、outer_join=falseのときに発行されるSQLは、
[java] Hibernate: select staffrole0_.id as id0_, staffrole0_.staff_name as
staff2_0_, staffrole0_.staff_role as staff3_0_, staffrole0_.staff_id as staff4_0
_ from staffrole staffrole0_ where staffrole0_.id=1
[java] Hibernate: select staff0_.id as id1_0_, staff0_.staff_name as staff2
_1_0_, staff0_.staff_password as staff3_1_0_, staff0_.email as email1_0_ from st
aff staff0_ where staff0_.id=?
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=? order by staffrole0_.staff_id asc
となり、3つのSQL文が発行されている。
しかし、outer_join=trueのときは、
[java] Hibernate: select staffrole0_.id as id0_, staffrole0_.staff_name as
staff2_0_, staffrole0_.staff_role as staff3_0_, staffrole0_.staff_id as staff4_0
_ from staffrole staffrole0_ where staffrole0_.id=1
[java] Hibernate: select staff0_.id as id1_1_, staff0_.staff_name as staff2
_1_1_, staff0_.staff_password as staff3_1_1_, staff0_.email as email1_1_, staffr
ole1_.staff_id as staff4_3_, staffrole1_.id as id3_, staffrole1_.id as id0_0_, s
taffrole1_.staff_name as staff2_0_0_, staffrole1_.staff_role as staff3_0_0_, sta
ffrole1_.staff_id as staff4_0_0_ from staff staff0_ left outer join staffrole st
affrole1_ on staff0_.id=staffrole1_.staff_id where staff0_.id=? order by staffro
le1_.staff_id asc
外部結合による問い合わせが実行され、SQL文を2つ発行する。
これによってパフォーマンスの改善がされることもあるかもしれない。
insert,update
insert,updateは、各プロパティーや、many-to-oneにつける。
これをfalseにすることで、そのカラムのinsert、updateを行われなくなる。
staffrole.hbm.xml
<?xml version="1.0" encoding="Windows-31J"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" \
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class table="staffrole" name="com.chikkun.tany.database.Staffrole">
<id unsaved-value="null" name="id" type="java.lang.Integer" column="id">
<generator class="sequence">
<param name="sequence">staffrole_id_seq</param>
</generator>
</id>
<property name="staffName" length="64" column="staff_name" unique="false" not-null="true"/>
<property name="staffRole" length="64" column="staff_role" update="false" unique="false" not-null="true" insert="false"/>
<many-to-one insert="false" column="staff_id" cascade="all" update="false" outer-join="auto" class="com.chikkun.tany.database.Staff" \
lazy="false" name="staff"/>
</class>
</hibernate-mapping>
Staffroleを上記のようにして、テストを行った。
ロールが2つでStaffの登録を行うとinverse=falseのとき、
[java] Hibernate: select nextval ('staff_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: insert into staff (staff_name, staff_password, email, id)
values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, id) values (?, ?)
[java] Hibernate: insert into staffrole (staff_name, id) values (?, ?)
[java] Hibernate: update staffrole set staff_id=? where id=?
[java] Hibernate: update staffrole set staff_id=? where id=?
staffroleテーブルには、

のように入った。
inverse=trueのときは、
[java] Hibernate: select nextval ('staff_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: select nextval ('staffrole_id_seq')
[java] Hibernate: insert into staff (staff_name, staff_password, email, id)
values (?, ?, ?, ?)
[java] Hibernate: insert into staffrole (staff_name, id) values (?, ?)
[java] Hibernate: insert into staffrole (staff_name, id) values (?, ?)
staffroleテーブルには、

のように入った。
inverse=trueの場合でないと、many-to-oneのinsert、updateは働いていないように思える。
order-byとsort
order-byは、親側のset、mapなどにつけるもので、この取得の順序を制御する。ここに書くのは、HQLではなくてSQLであることに注意が必要。
staff.hbm.xml
<hibernate-mapping>
<class lazy="true" table="staff" name="com.chikkun.tany.database.Staff">
<id unsaved-value="null" name="id" type="java.lang.Integer" column="id">
<generator class="sequence">
<param name="sequence">staff_id_seq</param>
</generator>
</id>
<property name="staffName" length="64" column="staff_name" unique="false" not-null="true"/>
<property name="staffPassword" length="64" column="staff_password" unique="false" not-null="true"/>
<property name="email" length="256" column="email" unique="false" not-null="false"/>
<set inverse="false" cascade="all-delete-orphan" order-by="id asc" outer-join="auto" lazy="false" name="staffrole">
<key column="staff_id"/>
<one-to-many class="com.chikkun.tany.database.Staffrole"/>
</set>
</class>
</hibernate-mapping>
上記のようにStaffのsetのorder-byをidの昇順にして、
List staffs = dao.findAll();
if(staffs != null){
for(int i = 0; i < staffs.size(); i++){
Staff st = (Staff)staffs.get(i);
Set set = st.getStaffrole();
if(set == null){
continue;
}
System.out.println("set class:" + set.getClass().toString());
for(Iterator is = set.iterator(); is.hasNext();){
Staffrole role = (Staffrole)is.next();
System.out.println("name:" + role.getStaffName());
System.out.println("roleId:" + role.getId());
System.out.println("role:" + role.getStaffRole());
}
}
}
System.out.println("\n");
上記のようなものを実行したとき、
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem2
[java] roleId:76
[java] role:admin
[java] name:sytem2
[java] roleId:77
[java] role:president
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem7
[java] roleId:89
[java] role:user
[java] name:sytem7
[java] roleId:90
[java] role:worker
[java] name:sytem7
[java] roleId:91
[java] role:public
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem11
[java] roleId:95
[java] role:user
[java] name:sytem11
[java] roleId:97
[java] role:public
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem10
[java] roleId:105
[java] role:president
[java] name:sytem10
[java] roleId:106
[java] role:admin
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem14
[java] roleId:107
[java] role:president
[java] name:sytem14
[java] roleId:108
[java] role:admin
このように、出力された。
staffroleは、一応ID順に取得されているようで、その際にorg.hibernate.collection.PersistentSetというクラスが使われているようだった。
order-byを外した場合は、
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem2
[java] roleId:76
[java] role:admin
[java] name:sytem2
[java] roleId:77
[java] role:president
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem7
[java] roleId:91
[java] role:public
[java] name:sytem7
[java] roleId:89
[java] role:user
[java] name:sytem7
[java] roleId:90
[java] role:worker
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem11
[java] roleId:97
[java] role:public
[java] name:sytem11
[java] roleId:95
[java] role:user
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem10
[java] roleId:106
[java] role:admin
[java] name:sytem10
[java] roleId:105
[java] role:president
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem14
[java] roleId:107
[java] role:president
[java] name:sytem14
[java] roleId:108
[java] role:admin
だった。
次にsortについて、sortはorder-byと同じくsetなどに記述するもので、"unsorted"、"natural"、"comparatorClass"を指定できる。
"unsorted"を指定した場合は、ソートが行われず上記の結果と同じだった。
"natural"は、子のクラスがComparableインターフェースを継承しているときに、そのソート順に行う。
hbmファイルが、
<?xml version="1.0" encoding="Windows-31J"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" \
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class lazy="true" table="staff" name="com.chikkun.tany.database.Staff">
<id unsaved-value="null" name="id" type="java.lang.Integer" column="id">
<generator class="sequence">
<param name="sequence">staff_id_seq</param>
</generator>
</id>
<property name="staffName" length="64" column="staff_name" unique="false" not-null="true"/>
<property name="staffPassword" length="64" column="staff_password" unique="false" not-null="true"/>
<property name="email" length="256" column="email" unique="false" not-null="false"/>
<set inverse="false" cascade="all-delete-orphan" outer-join="auto" lazy="false" sort="natural" name="staffrole">
<key column="staff_id"/>
<one-to-many class="com.chikkun.tany.database.Staffrole"/>
</set>
</class>
</hibernate-mapping>
のとき、StaffroleがComparableを継承していなければ、
[java] java.lang.ClassCastException: com.chikkun.tany.database.Staffrole ca
nnot be cast to java.lang.Comparable
[java] at java.util.TreeMap.put(TreeMap.java:542)
[java] at java.util.TreeSet.add(TreeSet.java:238)
......
のようにExceptionが発生する。
Staffroleを
public class Staffrole implements Serializable, Comparable {
/**
* serialVersionUID
*/
private static final long serialVersionUID = 1583398470057024174L;
/**
* id
*/
private Integer id;
/**
* staff_name
*/
private String staffName;
/**
* staff_role
*/
private String staffRole;
/**
* staff(親)
*/
private Staff staff;
/**
* デフォルトコンストラクタ
*/
public Staffrole() {
}
@Override
public int compareTo(Object o) {
//staffNameで比較
Staffrole sf = (Staffrole)o;
if(sf.getStaffName() == null){
return -1;
} else if(staffName == null){
return 1;
}
return staffName.compareTo(sf.getStaffName());
}
のようにComparableを継承して、テストしてみると、
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem2
[java] roleId:76
[java] role:admin
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem7
[java] roleId:89
[java] role:user
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem11
[java] roleId:95
[java] role:user
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem10
[java] roleId:106
[java] role:admin
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem14
[java] roleId:108
[java] role:admin
なぜか、Staffroleを一つしかもっていないStaffが返ってきた。理由は不明。
"comparatorClass"は、ここの部分にComparatorインターフェースを継承したクラス名を記述する。(パッケージ名から書かないとエラーになるようだった)
package com.chikkun.tany.database;
import java.util.Comparator;
public class RoleComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
// TODO Auto-generated method stub
Staffrole sf1 = (Staffrole)o1;
Staffrole sf2 = (Staffrole)o2;
return sf2.getId().compareTo(sf1.getId());
}
}
上記のようなIDの降順にソートするクラスを作成して、
<set inverse="false" cascade="all-delete-orphan" outer-join="auto" lazy="false" sort="com.chikkun.tany.database.RoleComparator" \
name="staffrole">
<key column="staff_id"/>
<one-to-many class="com.chikkun.tany.database.Staffrole"/>
</set>
hbmファイルを書き換えてテストを行った。
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem2
[java] roleId:77
[java] role:president
[java] name:sytem2
[java] roleId:76
[java] role:admin
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem7
[java] roleId:91
[java] role:public
[java] name:sytem7
[java] roleId:90
[java] role:worker
[java] name:sytem7
[java] roleId:89
[java] role:user
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem11
[java] roleId:97
[java] role:public
[java] name:sytem11
[java] roleId:95
[java] role:user
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem10
[java] roleId:106
[java] role:admin
[java] name:sytem10
[java] roleId:105
[java] role:president
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem14
[java] roleId:108
[java] role:admin
[java] name:sytem14
[java] roleId:107
[java] role:president
するとこちらは、ちゃんとソートして取得できた。
<set inverse="false" cascade="all-delete-orphan" order-by="id asc" outer-join="auto" lazy="false" \
sort="com.chikkun.tany.database.RoleComparator" name="staffrole">
<key column="staff_id"/>
<one-to-many class="com.chikkun.tany.database.Staffrole"/>
</set>
上記のように、order-byとsortが両方あった場合は、
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem2
[java] roleId:77
[java] role:president
[java] name:sytem2
[java] roleId:76
[java] role:admin
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem7
[java] roleId:91
[java] role:public
[java] name:sytem7
[java] roleId:90
[java] role:worker
[java] name:sytem7
[java] roleId:89
[java] role:user
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem11
[java] roleId:97
[java] role:public
[java] name:sytem11
[java] roleId:95
[java] role:user
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem10
[java] roleId:106
[java] role:admin
[java] name:sytem10
[java] roleId:105
[java] role:president
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSortedSet
[java] name:sytem14
[java] roleId:108
[java] role:admin
[java] name:sytem14
[java] roleId:107
[java] role:president
上記のように、sortが優先されるよう。ただし、SQLでは
[java] Hibernate: select staff0_.id as id1_, staff0_.staff_name as staff2_1
_, staff0_.staff_password as staff3_1_, staff0_.email as email1_ from staff staf
f0_
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=? order by staffrole0_.id asc
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=? order by staffrole0_.id asc
.......
のように、order byがついているがその分は無駄になる。片方だけの指定をするようにしたほうが良い。
FROM句の違い
Staffの検索を親側からFROMで行った場合、
Query query = session.createQuery("select staff from Staff as staff" +
" where staff.staffName = 'tanaka'");
このとき発行されたSQLは、
[java] Hibernate: select staff0_.id as id1_, staff0_.staff_name as staff2_1
_, staff0_.staff_password as staff3_1_, staff0_.email as email1_ from staff staf
f0_ where staff0_.staff_name='tanaka'
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=? order by staffrole0_.staff_id asc
となった。
子からの検索の場合、
Query query = session.createQuery("select role.staff from Staffrole as role" +
" where role.staffName = 'tanaka'");
このとき発行されたSQLは、
[java] Hibernate: select staff1_.id as id1_, staff1_.staff_name as staff2_1
_, staff1_.staff_password as staff3_1_, staff1_.email as email1_ from staffrole
staffrole0_ inner join staff staff1_ on staffrole0_.staff_id=staff1_.id where st
affrole0_.staff_name='tanaka'
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=? order by staffrole0_.staff_id asc
となり、親側からの検索の方が、joinがない分だけ早そうだった。
少し変えてみて、複数の結果が返るようにしてみる。
Query query = session.createQuery("select role.staff from Staffrole as role" +
" where role.staffRole = 'public'");
これで、publicのロールを持った3つのStaffが返ってくる。このとき、発行されたSQLは、
[java] Hibernate: select staff1_.id as id1_, staff1_.staff_name as staff2_1
_, staff1_.staff_password as staff3_1_, staff1_.email as email1_ from staffrole
staffrole0_ inner join staff staff1_ on staffrole0_.staff_id=staff1_.id where st
affrole0_.staff_role='public'
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=?
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=?
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=?
となった。次に、Staffからの検索。
Query query = session.createQuery("select staff from Staff as staff join staff.staffrole as role" +
" where role.staffRole = 'public'");
このとき、発行されたSQLは、
[java] Hibernate: select staff0_.id as id1_, staff0_.staff_name as staff2_1
_, staff0_.staff_password as staff3_1_, staff0_.email as email1_ from staff staf
f0_ inner join staffrole staffrole1_ on staff0_.id=staffrole1_.staff_id where st
affrole1_.staff_role='public'
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=?
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=?
[java] Hibernate: select staffrole0_.staff_id as staff4_1_, staffrole0_.id
as id1_, staffrole0_.id as id0_0_, staffrole0_.staff_name as staff2_0_0_, staffr
ole0_.staff_role as staff3_0_0_, staffrole0_.staff_id as staff4_0_0_ from staffr
ole staffrole0_ where staffrole0_.staff_id=?
となり、あまり変わらない。
上記2つで返ってきた結果は、
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem3
[java] roleId:69
[java] role:public
[java] name:sytem3
[java] roleId:70
[java] role:worker
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem7
[java] roleId:89
[java] role:user
[java] name:sytem7
[java] roleId:91
[java] role:public
[java] name:sytem7
[java] roleId:90
[java] role:worker
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem11
[java] roleId:97
[java] role:public
[java] name:sytem11
[java] roleId:95
[java] role:user
のように、ロールをもったStaffが返ってきた。ここで、
Query query = session.createQuery("select staff from Staff as staff join fetch staff.staffrole as role" +
" where role.staffRole = 'public'");
のようにjoin fetchを使用すると、
[java] Hibernate: select staff0_.id as id1_0_, staffrole1_.id as id0_1_, st aff0_.staff_name as staff2_1_0_, staff0_.staff_password as staff3_1_0_, staff0_. email as email1_0_, staffrole1_.staff_name as staff2_0_1_, staffrole1_.staff_rol e as staff3_0_1_, staffrole1_.staff_id as staff4_0_1_, staffrole1_.staff_id as s taff4_0__, staffrole1_.id as id0__ from staff staff0_ inner join staffrole staff role1_ on staff0_.id=staffrole1_.staff_id where staffrole1_.staff_role='public'
SQLが一つになり、
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem3
[java] roleId:69
[java] role:public
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem7
[java] roleId:91
[java] role:public
[java]
[java]
[java] set class:class org.hibernate.collection.PersistentSet
[java] name:sytem11
[java] roleId:97
[java] role:public
ロールは、publicしか持っていなかった。Staffのみをとるときは、早そうだが、その他の情報も欲しい場合は注意が必要のよう。
by tanifuji


